zathura.c 44 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
  /* UI */
96
97
  zathura->ui.session = girara_session_create();
  if (zathura->ui.session == NULL) {
98
    goto error_out;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
99
100
  }

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

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

  return zathura;

error_out:

  zathura_free(zathura);

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

117
118
static void
create_directories(zathura_t* zathura)
119
{
120
  static const unsigned int mode = 0711;
121

122
123
124
  if (g_mkdir_with_parents(zathura->config.config_dir, mode) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.config_dir,
                 strerror(errno));
125
126
  }

127
128
129
  if (g_mkdir_with_parents(zathura->config.data_dir, mode) == -1) {
    girara_error("Could not create '%s': %s", zathura->config.data_dir,
                 strerror(errno));
130
  }
131
}
132

133
134
135
static bool
init_ui(zathura_t* zathura)
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
136
  if (girara_session_init(zathura->ui.session, "zathura") == false) {
137
    return false;
138
139
140
  }

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

144
  /* zathura signals */
145
146
147
  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);
148
149
150
151

  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
    return false;
158
159
  }

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

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

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

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

  /* Connect vadjustment signals */
  g_signal_connect(G_OBJECT(vadjustment), "value-changed",
177
                   G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
178
  g_signal_connect(G_OBJECT(vadjustment), "changed",
179
                   G_CALLBACK(cb_view_vadjustment_changed), zathura);
Moritz Lipp's avatar
Moritz Lipp committed
180

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

185
186
187
188
  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);
189

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

192
  /* statusbar */
193
194
  zathura->ui.statusbar.file =
    girara_statusbar_item_add(zathura->ui.session, TRUE, TRUE, TRUE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
195
  if (zathura->ui.statusbar.file == NULL) {
196
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
197
198
  }

199
200
  zathura->ui.statusbar.buffer =
    girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
201
  if (zathura->ui.statusbar.buffer == NULL) {
202
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
203
204
  }

205
206
  zathura->ui.statusbar.page_number =
    girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
207
  if (zathura->ui.statusbar.page_number == NULL) {
208
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
209
210
  }

211
212
  girara_statusbar_item_set_text(zathura->ui.session,
                                 zathura->ui.statusbar.file, _("[No name]"));
Moritz Lipp's avatar
Moritz Lipp committed
213

Moritz Lipp's avatar
Moritz Lipp committed
214
  /* signals */
215
216
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "destroy",
                   G_CALLBACK(cb_destroy), zathura);
217

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  return true;
}

static void
init_css(zathura_t* zathura)
{
  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);
}

static void
init_database(zathura_t* zathura)
{
259
260
261
262
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
263
    girara_debug("Using plain database backend.");
264
265
266
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
267
    girara_debug("Using sqlite database backend.");
268
269
    char* tmp =
      g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL);
270
271
272
    zathura->database = zathura_sqldatabase_new(tmp);
    g_free(tmp);
#endif
Sebastian Ramacher's avatar
Sebastian Ramacher committed
273
  } else if (g_strcmp0(database, "null") != 0) {
274
275
276
    girara_error("Database backend '%s' is not supported.", database);
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
277
  if (zathura->database == NULL && g_strcmp0(database, "null") != 0) {
278
279
280
281
282
283
    girara_error(
      "Unable to initialize database. Bookmarks won't be available.");
  }
  else {
    g_object_set(G_OBJECT(zathura->ui.session->command_history), "io",
                 zathura->database, NULL);
284
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
285
  g_free(database);
286
}
287

288
289
290
static void
init_jumplist(zathura_t* zathura)
{
291
292
  int jumplist_size = 20;
  girara_setting_get(zathura->ui.session, "jumplist-size", &jumplist_size);
293

294
  zathura->jumplist.max_size = jumplist_size < 0 ? 0 : jumplist_size;
295
296
297
298
  zathura->jumplist.list     = NULL;
  zathura->jumplist.size     = 0;
  zathura->jumplist.cur      = NULL;
}
299

300
301
302
303
304
static void
init_shortcut_helpers(zathura_t* zathura)
{
  zathura->shortcut.mouse.x = 0;
  zathura->shortcut.mouse.y = 0;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
305

306
  zathura->shortcut.toggle_page_mode.pages = 2;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
307

308
309
310
311
  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;
}
Sebastian Ramacher's avatar
Sebastian Ramacher committed
312

313
314
315
316
317
318
bool
zathura_init(zathura_t* zathura)
{
  if (zathura == NULL) {
    return false;
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
319

320
321
322
323
324
325
326
327
328
329
330
331
332
  /* create zathura (config/data) directory */
  create_directories(zathura);

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

  /* configuration */
  config_load_default(zathura);
  config_load_files(zathura);

  /* UI */
  if (!init_ui(zathura)) {
    goto error_free;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
333
334
  }

335
336
  /* database */
  init_database(zathura);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
337

338
339
340
341
  /* bookmarks */
  zathura->bookmarks.bookmarks = girara_sorted_list_new2(
    (girara_compare_function_t)zathura_bookmarks_compare,
    (girara_free_function_t)zathura_bookmark_free);
342

343
344
  /* jumplist */
  init_jumplist(zathura);
345

346
347
348
349
350
  /* CSS for index mode */
  init_css(zathura);

  /* Shortcut helpers */
  init_shortcut_helpers(zathura);
351

352
  /* Start D-Bus service */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
353
354
355
356
  bool dbus = true;
  girara_setting_get(zathura->ui.session, "dbus-service", &dbus);
  if (dbus == true) {
    zathura->dbus = zathura_dbus_new(zathura);
357
358
  }

359
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
360
361
362

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
363
  if (zathura->ui.page_widget != NULL) {
364
    g_object_unref(zathura->ui.page_widget);
365
366
  }

367
  return false;
Moritz Lipp's avatar
Moritz Lipp committed
368
369
370
371
372
373
374
375
376
}

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

Moritz Lipp's avatar
Moritz Lipp committed
377
  document_close(zathura, false);
378

379
380
381
382
383
384
385
#ifdef G_OS_UNIX
  if (zathura->signals.sigterm > 0) {
    g_source_remove(zathura->signals.sigterm);
    zathura->signals.sigterm = 0;
  }
#endif

386
  /* stop D-Bus */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
387
388
389
  if (zathura->dbus != NULL) {
    g_object_unref(zathura->dbus);
    zathura->dbus = NULL;
390
391
  }

Moritz Lipp's avatar
Moritz Lipp committed
392
393
394
395
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

396
  /* shortcut */
397
398
399
  if (zathura->shortcut.toggle_presentation_mode.first_page_column_list != NULL) {
    g_free(zathura->shortcut.toggle_presentation_mode.first_page_column_list);
  }
400

401
402
403
404
405
406
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

407
408
409
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

410
  /* database */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
411
412
413
  if (zathura->database != NULL) {
    g_object_unref(G_OBJECT(zathura->database));
  }
414

415
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
416
  if (zathura->print.settings != NULL) {
417
418
419
420
421
422
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
424
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
425
  zathura_plugin_manager_free(zathura->plugins.manager);
426
427
428
429

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

432
433
434
435
436
437
438
439
440
  /* 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);
  }

441
  g_free(zathura);
442
443
}

444
#ifdef GDK_WINDOWING_X11
445
446
447
448
449
450
451
void
zathura_set_xid(zathura_t* zathura, Window xid)
{
  g_return_if_fail(zathura != NULL);

  zathura->ui.session->gtk.embed = xid;
}
452
#endif
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470

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

473
474
475
476
477
478
479
  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
480
}
481

Sebastian Ramacher's avatar
Sebastian Ramacher committed
482
483
484
void
zathura_set_cache_dir(zathura_t* zathura, const char* dir)
{
485
  g_return_if_fail(zathura != NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
486
487
488
489
490
491
492
493

  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);
  }
494
495
496
497
498
499
500
501
502
503
504
}

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
505
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
506
507
508
509
510
511
    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
512
    zathura_plugin_manager_add_dir(zathura->plugins.manager, path);
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
    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;
}

528
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
529
prepare_document_open_from_stdin(const char* path)
530
{
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  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;
  }

545
546
547
  GError* error = NULL;
  gchar* file = NULL;
  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
Moritz Lipp's avatar
Moritz Lipp committed
548
  if (handle == -1) {
549
550
551
552
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
553
554
555
    return NULL;
  }

556
557
558
  // read and dump to temporary file
  if (infileno == -1) {
    girara_error("Can not read from file descriptor.");
559
560
561
562
563
564
565
566
    close(handle);
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  char buffer[BUFSIZ];
  ssize_t count = 0;
567
  while ((count = read(infileno, buffer, BUFSIZ)) > 0) {
Moritz Lipp's avatar
Moritz Lipp committed
568
    if (write(handle, buffer, count) != count) {
569
570
571
572
573
574
575
      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
576

577
578
  close(handle);

Moritz Lipp's avatar
Moritz Lipp committed
579
  if (count != 0) {
580
    girara_error("Can not read from file descriptor.");
581
582
583
584
585
586
587
588
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

Lukas K.'s avatar
Lukas K. committed
589
static gchar*
Sebastian Ramacher's avatar
Sebastian Ramacher committed
590
prepare_document_open_from_gfile(GFile* source)
Lukas K.'s avatar
Lukas K. committed
591
{
592
  gchar*         file     = NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
593
  GFileIOStream* iostream = NULL;
594
  GError*        error    = NULL;
Lukas K.'s avatar
Lukas K. committed
595
596

  GFile *tmpfile = g_file_new_tmp("zathura.gio.XXXXXX", &iostream, &error);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
597
  if (tmpfile == NULL) {
Lukas K.'s avatar
Lukas K. committed
598
599
600
601
602
603
604
605
    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
606
  if (rc == FALSE) {
Lukas K.'s avatar
Lukas K. committed
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
    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;
}

623
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
624
625
626
627
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
628
  char* uri = NULL;
Moritz Lipp's avatar
Update    
Moritz Lipp committed
629

630
  if (document_info->zathura != NULL && document_info->path != NULL) {
631
    char* file = NULL;
632
633
    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
634
      file = prepare_document_open_from_stdin(document_info->path);
635
636
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
637
                      _("Could not read file from stdin and write it to a temporary file."));
638
639
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
640
641
      }
    } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
642
643
644
645
      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
646
647
      }
      else {
648
        /* copy file with GIO */
Lukas K.'s avatar
Lukas K. committed
649
        uri = g_file_get_uri(gf);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
650
        file = prepare_document_open_from_gfile(gf);
651
652
653
        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
654
        } else {
655
          document_info->zathura->stdin_support.file = g_strdup(file);
Lukas K.'s avatar
Lukas K. committed
656
657
658
        }
      }
      g_object_unref(gf);
659
660
661
    }

    if (file != NULL) {
662
      if (document_info->synctex != NULL) {
Lukas K.'s avatar
Lukas K. committed
663
        document_open_synctex(document_info->zathura, file, uri, 
664
665
                              document_info->password, document_info->synctex);
      } else {
Lukas K.'s avatar
Lukas K. committed
666
        document_open(document_info->zathura, file, uri, document_info->password,
667
668
                      document_info->page_number);
      }
669
      g_free(file);
Lukas K.'s avatar
Lukas K. committed
670
      g_free(uri);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
671
672
673
674
675
676
677
678
679
680
681
682

      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);
        }
      }
683
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
684
685
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
686
  free_document_info(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
687
  return FALSE;
688
689
}

Lukas K.'s avatar
Lukas K. committed
690
691
char*
get_formatted_filename(zathura_t* zathura, bool statusbar)
692
693
{
  bool basename_only = false;
Lukas K.'s avatar
Lukas K. committed
694
  const char* file_path = zathura_document_get_uri(zathura->document);
695
  if (file_path == NULL) {
Lukas K.'s avatar
Lukas K. committed
696
697
    file_path = zathura_document_get_path(zathura->document);
  }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
698
  if (statusbar == true) {
699
    girara_setting_get(zathura->ui.session, "statusbar-basename", &basename_only);
Lukas K.'s avatar
Lukas K. committed
700
701
  } else {
    girara_setting_get(zathura->ui.session, "window-title-basename", &basename_only);
702
703
  }

704
705
  if (basename_only == false) {
    bool home_tilde = false;
706
707
708
709
710
711
    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
712
    const size_t file_path_len = file_path ? strlen(file_path) : 0;
713

Sebastian Ramacher's avatar
Sebastian Ramacher committed
714
715
716
    if (home_tilde == true) {
      char* home = girara_get_home_directory(NULL);
      const size_t home_len = home ? strlen(home) : 0;
717

Sidharth Kapur's avatar
Sidharth Kapur committed
718
719
720
721
      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
722
        g_free(home);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
723
        return g_strdup_printf("~%s", &file_path[home_len]);
724
      } else {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
725
        g_free(home);
726
        return g_strdup(file_path);
727
728
      }
    } else {
729
      return g_strdup(file_path);
730
731
    }
  } else {
732
    const char* basename = zathura_document_get_basename(zathura->document);
733
    return g_strdup(basename);
734
735
736
  }
}

737
738
739
740
741
742
743
744
745
746
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
747
bool
Lukas K.'s avatar
Lukas K. committed
748
document_open(zathura_t* zathura, const char* path, const char* uri, const char* password,
749
              int page_number)
Moritz Lipp's avatar
Moritz Lipp committed
750
{
751
  if (zathura == NULL || zathura->plugins.manager == NULL || path == NULL) {
752
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
753
754
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
759
  if (document == NULL) {
760
    if (error == ZATHURA_ERROR_INVALID_PASSWORD) {
761
      girara_debug("Invalid or no password.");
762
763
764
      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
765
        password_dialog_info->path = g_strdup(path);
Lukas K.'s avatar
Lukas K. committed
766
        password_dialog_info->uri = g_strdup(uri);
767

Sebastian Ramacher's avatar
Sebastian Ramacher committed
768
        if (password_dialog_info->path != NULL) {
769
          gdk_threads_add_idle(document_open_password_dialog, password_dialog_info);
770
771
772
773
774
775
776
          goto error_out;
        } else {
          free(password_dialog_info);
        }
      }
      goto error_out;
    }
Moritz Lipp's avatar
Moritz Lipp committed
777
778
779
    if (error == ZATHURA_ERROR_OK ) {
      girara_notify(zathura->ui.session, GIRARA_ERROR, _("Unsupported file type. Please install the necessary plugin."));
    }
780
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
781
  }
Lukas K.'s avatar
Lukas K. committed
782
783
  
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
784

785
786
  const char* file_path        = zathura_document_get_path(document);
  unsigned int number_of_pages = zathura_document_get_number_of_pages(document);
787

788
789
790
791
792
793
  if (number_of_pages == 0) {
    girara_notify(zathura->ui.session, GIRARA_WARNING,
        _("Document does not contain any pages"));
    goto error_free;
  }

794
  /* read history file */
795
796
797
798
799
800
  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
801
    .first_page_column_list = NULL,
802
803
804
    .position_x = 0,
    .position_y = 0
  };
805
806
807
808
  bool known_file = false;
  if (zathura->database != NULL) {
    known_file = zathura_db_get_fileinfo(zathura->database, file_path, &file_info);
  }
809

Moritz Lipp's avatar
Moritz Lipp committed
810
  /* set page offset */
811
  zathura_document_set_page_offset(document, file_info.page_offset);
812

813
  /* check for valid scale value */
814
815
816
  if (file_info.scale <= DBL_EPSILON) {
    file_info.scale = 1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
817
818
  zathura_document_set_scale(document,
      zathura_correct_scale_value(zathura->ui.session, file_info.scale));
819
820

  /* check current page number */
821
822
823
824
825
826
  /* 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) {
827
828
    girara_warning("document info: '%s' has an invalid page number", file_path);
    zathura_document_set_current_page_number(document, 0);
829
  } else {
830
    zathura_document_set_current_page_number(document, page_number);
831
832
833
  }

  /* check for valid rotation */
834
  if (file_info.rotation % 90 != 0) {
835
836
837
    girara_warning("document info: '%s' has an invalid rotation", file_path);
    zathura_document_set_rotation(document, 0);
  } else {
838
    zathura_document_set_rotation(document, file_info.rotation % 360);
839
840
841
842
843
844
845
846
847
848
849
  }

  /* 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";
850
  if (known_file == false && girara_setting_get(zathura->ui.session, "adjust-open", &(adjust_open)) == true) {
851
852
853
854
855
856
857
858
    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);
859
860
  } else {
    zathura_document_set_adjust_mode(document, ZATHURA_ADJUST_NONE);
861
862
  }

863
864
865
866
867
  /* 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;

868
  /* update statusbar */
Lukas K.'s avatar
Lukas K. committed
869
  char* filename = get_formatted_filename(zathura, true);
870
871
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, filename);
  g_free(filename);
872

Moritz Lipp's avatar
Moritz Lipp committed
873
  /* install file monitor */
874
  file_uri = g_filename_to_uri(file_path, NULL, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
875
876
877
878
879
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
880
881
882
883
    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
884
885
886
  }

  if (zathura->file_monitor.monitor == NULL) {
887
888
889
890
891
    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
892
893
894
  }

  if (zathura->file_monitor.file_path == NULL) {
895
    zathura->file_monitor.file_path = g_strdup(file_path);
896
897
898
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
899
900
  }

901
  if (password != NULL) {
902
    g_free(zathura->file_monitor.password);
903
    zathura->file_monitor.password = g_strdup(password);
Moritz Lipp's avatar
Moritz Lipp committed
904
905
906
907
908
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
909
910
911
912
913
914
  /* create marks list */
  zathura->global.marks = girara_list_new2((girara_free_function_t) mark_free);
  if (zathura->global.marks == NULL) {
    goto error_free;
  }

915
916
917
918
919
920
921
922
923
  /* 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;
  }

924
  /* threads */
925
  zathura->sync.render_thread = zathura_renderer_new(cache_size);
926
927
928
929
930

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

Sebastian Ramacher's avatar
Sebastian Ramacher committed
931
  /* set up recolor info in ZathuraRenderer */
932
  char* recolor_dark  = NULL;
933
934
935
936
937
938
939
940
  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);

941
942
943
944
945
  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);
946
947
  girara_setting_get(zathura->ui.session, "recolor-reverse-video", &recolor);
  zathura_renderer_enable_recolor_reverse_video(zathura->sync.render_thread, recolor);
948

949
950
951
952
953
954
  /* 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));

955
  const unsigned int view_width = (unsigned int)floor(gtk_adjustment_get_page_size(hadjustment));
956
  zathura_document_set_viewport_width(zathura->document, view_width);
957
  const unsigned int view_height = (unsigned int)floor(gtk_adjustment_get_page_size(vadjustment));
958
959
  zathura_document_set_viewport_height(zathura->document, view_height);

960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
  /* 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
977
    g_object_ref(page_widget);
978
979
    zathura->pages[page_id] = page_widget;

980
981
982
    gtk_widget_set_halign(page_widget, GTK_ALIGN_CENTER);
    gtk_widget_set_valign(page_widget, GTK_ALIGN_CENTER);

983
984
985
    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",
986
        G_CALLBACK(cb_page_widget_image_selected), zathura);
987
988
989
990
    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);
991
992
    g_signal_connect(G_OBJECT(page_widget), "scaled-button-release",
        G_CALLBACK(cb_page_widget_scaled_button_release), zathura);
993
994
  }

995
  /* view mode */
996
  unsigned int pages_per_row = 1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
997
  char* first_page_column_list = NULL;
998
999
1000
1001
  unsigned int page_padding = 1;

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

1002
1003
1004
1005
1006
  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
1007

1008
  /* read first_page_column list */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1009
  if (file_info.first_page_column_list != NULL && strcmp(file_info.first_page_column_list, "")) {
1010
    first_page_column_list = file_info.first_page_column_list;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1011
    file_info.first_page_column_list = NULL;
1012
  } else {
1013
    girara_setting_get(zathura->ui.session, "first-page-column", &first_page_column_list);
1014
1015
  }

1016
1017
1018
  /* 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
1019
  girara_setting_set(zathura->ui.session<