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

3
#define _BSD_SOURCE
Sebastian Ramacher's avatar
Sebastian Ramacher committed
4
#define _XOPEN_SOURCE 700
5

Moritz Lipp's avatar
Moritz Lipp committed
6
#include <stdlib.h>
Pavel Borzenkov's avatar
Pavel Borzenkov committed
7
#include <unistd.h>
8
#include <math.h>
Moritz Lipp's avatar
Moritz Lipp committed
9

10
11
12
13
14
#include <girara/datastructures.h>
#include <girara/utils.h>
#include <girara/session.h>
#include <girara/statusbar.h>
#include <girara/settings.h>
15
#include <glib/gstdio.h>
16
#include <glib/gi18n.h>
17

18
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
19
20
#include "callbacks.h"
#include "config.h"
21
22
23
24
#ifdef WITH_SQLITE
#include "database-sqlite.h"
#endif
#include "database-plain.h"
25
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
26
#include "shortcuts.h"
Moritz Lipp's avatar
Moritz Lipp committed
27
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
28
#include "utils.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
29
#include "render.h"
Moritz Lipp's avatar
Moritz Lipp committed
30

Moritz Lipp's avatar
Update    
Moritz Lipp committed
31
32
33
34
35
36
37
typedef struct zathura_document_info_s
{
  zathura_t* zathura;
  const char* path;
  const char* password;
} zathura_document_info_t;

38
static gboolean document_info_open(gpointer data);
Moritz Lipp's avatar
Moritz Lipp committed
39

Moritz Lipp's avatar
Moritz Lipp committed
40
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
41
42
zathura_t*
zathura_init(int argc, char* argv[])
43
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
44
  /* parse command line options */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
45
#if (GTK_MAJOR_VERSION == 2)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
46
  GdkNativeWindow embed = 0;
47
48
#else
  Window embed = 0;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
49
50
#endif

51
  gchar* config_dir = NULL, *data_dir = NULL, *plugin_path = NULL, *loglevel = NULL;
52
  bool forkback = false;
53
  GOptionEntry entries[] =
Sebastian Ramacher's avatar
Sebastian Ramacher committed
54
  {
55
56
57
58
    { "reparent",    'e', 0, G_OPTION_ARG_INT,      &embed,       _("Reparents to window specified by xid"),       "xid"  },
    { "config-dir",  'c', 0, G_OPTION_ARG_FILENAME, &config_dir,  _("Path to the config directory"),               "path" },
    { "data-dir",    'd', 0, G_OPTION_ARG_FILENAME, &data_dir,    _("Path to the data directory"),                 "path" },
    { "plugins-dir", 'p', 0, G_OPTION_ARG_STRING,   &plugin_path, _("Path to the directories containing plugins"), "path" },
59
60
    { "fork",        '\0', 0, G_OPTION_ARG_NONE,    &forkback,    _("Fork into the background"),                   NULL },
    { "debug",       'l', 0, G_OPTION_ARG_STRING,   &loglevel,    _("Log level (debug, info, warning, error)")     "level" },
61
    { NULL, '\0', 0, 0, NULL, NULL, NULL }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
62
63
64
65
66
67
  };

  GOptionContext* context = g_option_context_new(" [file] [password]");
  g_option_context_add_main_entries(context, entries, NULL);

  GError* error = NULL;
Moritz Lipp's avatar
Moritz Lipp committed
68
  if (g_option_context_parse(context, &argc, &argv, &error) == false)
Sebastian Ramacher's avatar
Sebastian Ramacher committed
69
70
71
72
  {
    printf("Error parsing command line arguments: %s\n", error->message);
    g_option_context_free(context);
    g_error_free(error);
73
    return NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
74
75
76
  }
  g_option_context_free(context);

77
78
79
80
81
82
83
84
85
86
87
88
89
  /* Fork into the background if the user really wants to ... */
  if (forkback == true)
  {
    int pid = fork();
    if (pid > 0) { /* parent */
      exit(0);
    } else if (pid < 0) { /* error */
      printf("Error: couldn't fork.");
    }

    setsid();
  }

90
91
92
93
94
95
96
97
98
  /* Set log level. */
  if (loglevel == NULL || g_strcmp0(loglevel, "info") == 0) {
    girara_set_debug_level(GIRARA_INFO);
  } else if (g_strcmp0(loglevel, "warning") == 0) {
    girara_set_debug_level(GIRARA_WARNING);
  } else if (g_strcmp0(loglevel, "error") == 0) {
    girara_set_debug_level(GIRARA_ERROR);
  }

99
  zathura_t* zathura = g_malloc0(sizeof(zathura_t));
100
101

  /* plugins */
102
  zathura->plugins.plugins = girara_list_new2(
Sebastian Ramacher's avatar
Sebastian Ramacher committed
103
      (girara_free_function_t)zathura_document_plugin_free);
104
105
  zathura->plugins.path    = girara_list_new2(g_free);
  zathura->plugins.type_plugin_mapping = girara_list_new2(
Sebastian Ramacher's avatar
Sebastian Ramacher committed
106
      (girara_free_function_t)zathura_type_plugin_mapping_free);
107

Moritz Lipp's avatar
Moritz Lipp committed
108
  if (config_dir != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
109
110
111
112
113
114
115
    zathura->config.config_dir = g_strdup(config_dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_CONFIG);
    zathura->config.config_dir = g_build_filename(path, "zathura", NULL);
    g_free(path);
  }

Moritz Lipp's avatar
Moritz Lipp committed
116
  if (data_dir != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
117
118
119
    zathura->config.data_dir = g_strdup(config_dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_DATA);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
120
    zathura->config.data_dir = g_build_filename(path, "zathura", NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
121
122
123
    g_free(path);
  }

124
125
126
127
  /* create zathura (config/data) directory */
  g_mkdir_with_parents(zathura->config.config_dir, 0771);
  g_mkdir_with_parents(zathura->config.data_dir,   0771);

Moritz Lipp's avatar
Moritz Lipp committed
128
  if (plugin_path != NULL) {
129
130
131
    girara_list_t* paths = girara_split_path_array(plugin_path);
    girara_list_merge(zathura->plugins.path, paths);
    girara_list_free(paths);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
132
  } else {
133
#ifdef ZATHURA_PLUGINDIR
134
135
136
    girara_list_t* paths = girara_split_path_array(ZATHURA_PLUGINDIR);
    girara_list_merge(zathura->plugins.path, paths);
    girara_list_free(paths);
137
#endif
Sebastian Ramacher's avatar
Sebastian Ramacher committed
138
139
  }

140
  /* UI */
Moritz Lipp's avatar
Moritz Lipp committed
141
  if ((zathura->ui.session = girara_session_create()) == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
142
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
143
  }
144

Moritz Lipp's avatar
Moritz Lipp committed
145
  zathura->ui.session->global.data  = zathura;
146

Moritz Lipp's avatar
Moritz Lipp committed
147
  /* global settings */
148
149
  zathura->global.recolor            = false;
  zathura->global.update_page_number = true;
Moritz Lipp's avatar
Moritz Lipp committed
150

151
152
153
  /* load plugins */
  zathura_document_plugins_load(zathura);

154
155
156
  /* configuration */
  config_load_default(zathura);

157
  /* load global configuration files */
Moritz Lipp's avatar
Moritz Lipp committed
158
159
  char* config_path = girara_get_xdg_path(XDG_CONFIG_DIRS);
  girara_list_t* config_dirs = girara_split_path_array(config_path);
160
161
162
163
164
165
166
167
  ssize_t size = girara_list_size(config_dirs) - 1;
  for (; size >= 0; --size) {
    const char* dir = girara_list_nth(config_dirs, size);
    char* file = g_build_filename(dir, ZATHURA_RC, NULL);
    config_load_file(zathura, file);
    g_free(file);
  }
  girara_list_free(config_dirs);
Moritz Lipp's avatar
Moritz Lipp committed
168
  g_free(config_path);
169
170
171

  config_load_file(zathura, GLOBAL_RC);

172
173
174
  /* load local configuration files */
  char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  config_load_file(zathura, configuration_file);
175
  g_free(configuration_file);
176
177
178

  /* initialize girara */
  zathura->ui.session->gtk.embed = embed;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
179

Sebastian Ramacher's avatar
Sebastian Ramacher committed
180
  if (girara_session_init(zathura->ui.session, "zathura") == false) {
181
182
183
184
    goto error_out;
  }

  /* girara events */
Moritz Lipp's avatar
Moritz Lipp committed
185
  zathura->ui.session->events.buffer_changed = cb_buffer_changed;
186

187
  /* page view */
188
  zathura->ui.page_widget = gtk_table_new(0, 0, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
189
  if (zathura->ui.page_widget == NULL) {
190
191
192
    goto error_free;
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
195
196
197
198
199
200
  /* callbacks */
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  g_signal_connect(G_OBJECT(view_vadjustment), "value-changed", G_CALLBACK(cb_view_vadjustment_value_changed), zathura);
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  g_signal_connect(G_OBJECT(view_hadjustment), "value-changed", G_CALLBACK(cb_view_vadjustment_value_changed), zathura);

201
  /* page view alignment */
202
  zathura->ui.page_widget_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
Moritz Lipp's avatar
Moritz Lipp committed
203
  if (zathura->ui.page_widget_alignment == NULL) {
204
205
    goto error_free;
  }
206
  gtk_container_add(GTK_CONTAINER(zathura->ui.page_widget_alignment), zathura->ui.page_widget);
207

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

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

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

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

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

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

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

235
236
  gtk_table_set_row_spacings(GTK_TABLE(zathura->ui.page_widget), page_padding);
  gtk_table_set_col_spacings(GTK_TABLE(zathura->ui.page_widget), page_padding);
Moritz Lipp's avatar
Moritz Lipp committed
237

238
  /* database */
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  char* database = NULL;
  girara_setting_get(zathura->ui.session, "database", &database);

  if (g_strcmp0(database, "plain") == 0) {
    girara_info("Using plain database backend.");
    zathura->database = zathura_plaindatabase_new(zathura->config.data_dir);
#ifdef WITH_SQLITE
  } else if (g_strcmp0(database, "sqlite") == 0) {
    girara_info("Using sqlite database backend.");
    char* tmp = g_build_filename(zathura->config.data_dir, "bookmarks.sqlite", NULL);
    zathura->database = zathura_sqldatabase_new(tmp);
    g_free(tmp);
#endif
  } else {
    girara_error("Database backend '%s' is not supported.", database);
  }
  g_free(database);

257
  if (zathura->database == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
258
    girara_error("Unable to initialize database. Bookmarks won't be available.");
259
260
  }

261
  /* bookmarks */
262
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
263
    (girara_free_function_t) zathura_bookmark_free);
264

Moritz Lipp's avatar
Moritz Lipp committed
265
  /* open document if passed */
Moritz Lipp's avatar
Update    
Moritz Lipp committed
266
  if (argc > 1) {
267
    zathura_document_info_t* document_info = g_malloc0(sizeof(zathura_document_info_t));
Moritz Lipp's avatar
Update    
Moritz Lipp committed
268

269
270
271
    document_info->zathura  = zathura;
    document_info->path     = argv[1];
    document_info->password = (argc >= 2) ? argv[2] : NULL;
272
    gdk_threads_add_idle(document_info_open, document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
273
274
  }

Moritz Lipp's avatar
Moritz Lipp committed
275
  return zathura;
Moritz Lipp's avatar
Moritz Lipp committed
276
277
278

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
279
  if (zathura->ui.page_widget != NULL) {
280
    g_object_unref(zathura->ui.page_widget);
281
282
  }

Moritz Lipp's avatar
Moritz Lipp committed
283
  if (zathura->ui.page_widget_alignment != NULL) {
284
    g_object_unref(zathura->ui.page_widget_alignment);
285
  }
Moritz Lipp's avatar
Moritz Lipp committed
286
287
288

error_out:

289
  zathura_free(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
290

Moritz Lipp's avatar
Moritz Lipp committed
291
292
293
294
295
296
297
298
299
300
  return NULL;
}

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

Moritz Lipp's avatar
Moritz Lipp committed
301
  document_close(zathura, false);
302

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

307
308
309
310
311
312
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

313
314
315
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

316
317
318
  /* database */
  zathura_db_free(zathura->database);

319
  /* free print settings */
Moritz Lipp's avatar
Moritz Lipp committed
320
  if (zathura->print.settings != NULL) {
321
322
323
324
325
326
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
328
  /* free registered plugins */
Moritz Lipp's avatar
Moritz Lipp committed
329
  girara_list_free(zathura->plugins.type_plugin_mapping);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
330
  girara_list_free(zathura->plugins.plugins);
331
332
333
334
335
  girara_list_free(zathura->plugins.path);

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

337
  g_free(zathura);
338
339
}

340
341
342
343
344
345
346
347
348
349
static gchar*
prepare_document_open_from_stdin(zathura_t* zathura)
{
  g_return_val_if_fail(zathura, NULL);

  GError* error = NULL;
  gchar* file = NULL;
  gint handle = g_file_open_tmp("zathura.stdin.XXXXXX", &file, &error);
  if (handle == -1)
  {
350
351
352
353
    if (error != NULL) {
      girara_error("Can not create temporary file: %s", error->message);
      g_error_free(error);
    }
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
    return NULL;
  }

  // read from stdin and dump to temporary file
  int stdinfno = fileno(stdin);
  if (stdinfno == -1)
  {
    girara_error("Can not read from stdin.");
    close(handle);
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  char buffer[BUFSIZ];
  ssize_t count = 0;
  while ((count = read(stdinfno, buffer, BUFSIZ)) > 0)
  {
    if (write(handle, buffer, count) != count)
    {
      girara_error("Can not write to temporary file: %s", file);
      close(handle);
      g_unlink(file);
      g_free(file);
      return NULL;
    }
  }
  close(handle);

  if (count != 0)
  {
    girara_error("Can not read from stdin.");
    g_unlink(file);
    g_free(file);
    return NULL;
  }

  return file;
}

394
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
395
396
397
398
399
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);

400
  if (document_info->zathura != NULL && document_info->path != NULL) {
401
402
403
404
405
406
    char* file = NULL;
    if (g_strcmp0(document_info->path, "-") == 0) {
      file = prepare_document_open_from_stdin(document_info->zathura);
      if (file == NULL) {
        girara_notify(document_info->zathura->ui.session, GIRARA_ERROR,
            "Could not read file from stdin and write it to a temporary file.");
407
408
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
409
410
411
412
413
414
415
416
417
      }
    } else {
      file = g_strdup(document_info->path);
    }

    if (file != NULL) {
      document_open(document_info->zathura, file, document_info->password);
      g_free(file);
    }
Moritz Lipp's avatar
Update    
Moritz Lipp committed
418
419
  }

420
  g_free(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
421
  return FALSE;
422
423
}

Moritz Lipp's avatar
Moritz Lipp committed
424
bool
Moritz Lipp's avatar
Moritz Lipp committed
425
document_open(zathura_t* zathura, const char* path, const char* password)
Moritz Lipp's avatar
Moritz Lipp committed
426
{
Moritz Lipp's avatar
Moritz Lipp committed
427
  if (path == NULL) {
428
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
429
430
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
431
  zathura_document_t* document = zathura_document_open(zathura, path, password);
Moritz Lipp's avatar
Moritz Lipp committed
432

Moritz Lipp's avatar
Moritz Lipp committed
433
  if (document == NULL) {
434
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
435
436
  }

Moritz Lipp's avatar
Moritz Lipp committed
437
438
439
440
441
442
443
  /* install file monitor */
  gchar* file_uri = g_filename_to_uri(document->file_path, NULL, NULL);
  if (file_uri == NULL) {
    goto error_free;
  }

  if (zathura->file_monitor.file == NULL) {
444
445
446
447
    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
448
449
450
  }

  if (zathura->file_monitor.monitor == NULL) {
451
452
453
454
455
    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
456
457
458
  }

  if (zathura->file_monitor.file_path == NULL) {
459
460
461
462
    zathura->file_monitor.file_path = g_strdup(document->file_path);
    if (zathura->file_monitor.file_path == NULL) {
      goto error_free;
    }
Moritz Lipp's avatar
Moritz Lipp committed
463
464
465
  }

  if (document->password != NULL) {
466
    g_free(zathura->file_monitor.password);
Moritz Lipp's avatar
Moritz Lipp committed
467
468
469
470
471
472
    zathura->file_monitor.password = g_strdup(document->password);
    if (zathura->file_monitor.password == NULL) {
      goto error_free;
    }
  }

Moritz Lipp's avatar
Moritz Lipp committed
473
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
474

475
  /* view mode */
476
477
  int pages_per_row = 1;
  girara_setting_get(zathura->ui.session, "pages-per-row", &pages_per_row);
478
  page_widget_set_mode(zathura, pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
479

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

Moritz Lipp's avatar
Moritz Lipp committed
482
  /* threads */
Moritz Lipp's avatar
Moritz Lipp committed
483
  zathura->sync.render_thread = render_init(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
484

Moritz Lipp's avatar
Moritz Lipp committed
485
  if (zathura->sync.render_thread == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
486
487
488
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
489
  /* create blank pages */
490
  for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++) {
Moritz Lipp's avatar
Moritz Lipp committed
491
    zathura_page_t* page = document->pages[page_id];
492
    gtk_widget_realize(page->drawing_area);
493
  }
Moritz Lipp's avatar
Moritz Lipp committed
494

495
  /* bookmarks */
Moritz Lipp's avatar
Moritz Lipp committed
496
  zathura_bookmarks_load(zathura, zathura->document->file_path);
497

498
  page_set_delayed(zathura, document->current_page_number);
499
  cb_view_vadjustment_value_changed(NULL, zathura);
500

Moritz Lipp's avatar
Moritz Lipp committed
501
502
503
  /* update title */
  girara_set_window_title(zathura->ui.session, document->file_path);

Moritz Lipp's avatar
Moritz Lipp committed
504
505
  free(file_uri);

Moritz Lipp's avatar
Moritz Lipp committed
506
  return true;
507
508
509

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
510
511
512
513
  if (file_uri != NULL) {
    g_free(file_uri);
  }

514
515
516
517
518
  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
519
520
}

521
522
523
524
525
526
527
528
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);
Moritz Lipp's avatar
Moritz Lipp committed
529
  if ((overwrite == false) && g_file_test(file_path, G_FILE_TEST_EXISTS))
530
  {
531
    girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
532
    g_free(file_path);
533
534
535
    return false;
  }

536
  zathura_plugin_error_t error = zathura_document_save_as(zathura->document, file_path);
537
  g_free(file_path);
538
539

  return (error == ZATHURA_PLUGIN_ERROR_OK) ? true : false;
540
541
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
542
543
544
static void
remove_page_from_table(GtkWidget* page, gpointer permanent)
{
Moritz Lipp's avatar
Moritz Lipp committed
545
  if (permanent == false) {
Pavel Borzenkov's avatar
Pavel Borzenkov committed
546
547
548
    g_object_ref(G_OBJECT(page));
  }

549
  gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(page)), page);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
550
551
}

Moritz Lipp's avatar
Moritz Lipp committed
552
bool
Moritz Lipp's avatar
Moritz Lipp committed
553
document_close(zathura_t* zathura, bool keep_monitor)
Moritz Lipp's avatar
Moritz Lipp committed
554
{
Moritz Lipp's avatar
Moritz Lipp committed
555
  if (zathura == NULL || zathura->document == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
556
557
558
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
  /* remove monitor */
  if (keep_monitor == false) {
    if (zathura->file_monitor.monitor != NULL) {
      g_file_monitor_cancel(zathura->file_monitor.monitor);
      g_object_unref(zathura->file_monitor.monitor);
      zathura->file_monitor.monitor = NULL;
    }

    if (zathura->file_monitor.file != NULL) {
      g_object_unref(zathura->file_monitor.file);
      zathura->file_monitor.file = NULL;
    }

    if (zathura->file_monitor.file_path != NULL) {
      g_free(zathura->file_monitor.file_path);
      zathura->file_monitor.file_path = NULL;
    }

    if (zathura->file_monitor.password != NULL) {
      g_free(zathura->file_monitor.password);
      zathura->file_monitor.password = NULL;
    }
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
583
  /* store last seen page */
584
  zathura_db_set_fileinfo(zathura->database, zathura->document->file_path, zathura->document->current_page_number,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
585
586
      /* zathura->document->offset TODO */ 0, zathura->document->scale,
      zathura->document->rotate);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
587

Pavel Borzenkov's avatar
Pavel Borzenkov committed
588
589
590
  render_free(zathura->sync.render_thread);
  zathura->sync.render_thread = NULL;

591
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer)1);
Moritz Lipp's avatar
Moritz Lipp committed
592

Moritz Lipp's avatar
Moritz Lipp committed
593
  zathura_document_free(zathura->document);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
594
595
  zathura->document = NULL;

596
  gtk_widget_hide(zathura->ui.page_widget);
597
598
599
600

  statusbar_page_number_update(zathura);

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

Moritz Lipp's avatar
Moritz Lipp committed
604
605
606
  /* update title */
  girara_set_window_title(zathura->ui.session, "zathura");

Moritz Lipp's avatar
Moritz Lipp committed
607
608
609
  return true;
}

Sebastian Ramacher's avatar
Sebastian Ramacher committed
610
611
typedef struct page_set_delayed_s
{
612
613
614
615
616
  zathura_t* zathura;
  unsigned int page;
} page_set_delayed_t;

static gboolean
Sebastian Ramacher's avatar
Sebastian Ramacher committed
617
618
page_set_delayed_impl(gpointer data)
{
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
  page_set_delayed_t* p = data;
  page_set(p->zathura, p->page);

  g_free(p);
  return FALSE;
}

bool
page_set_delayed(zathura_t* zathura, unsigned int page_id)
{
  if (zathura == NULL || zathura->document == NULL || zathura->document->pages == NULL ||
      page_id >= zathura->document->number_of_pages) {
    return false;
  }

  page_set_delayed_t* p = g_malloc(sizeof(page_set_delayed_t));
  p->zathura = zathura;
  p->page = page_id;
637
  gdk_threads_add_idle(page_set_delayed_impl, p);
638
639
640
  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
641
bool
Moritz Lipp's avatar
Moritz Lipp committed
642
page_set(zathura_t* zathura, unsigned int page_id)
Moritz Lipp's avatar
Moritz Lipp committed
643
{
644
  if (zathura == NULL || zathura->document == NULL || zathura->document->pages == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
645
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
646
647
  }

Moritz Lipp's avatar
Moritz Lipp committed
648
  if (page_id >= zathura->document->number_of_pages) {
Moritz Lipp's avatar
Moritz Lipp committed
649
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
650
651
  }

652
  /* render page */
Moritz Lipp's avatar
Moritz Lipp committed
653
  zathura_page_t* page = zathura->document->pages[page_id];
Moritz Lipp's avatar
Moritz Lipp committed
654

Moritz Lipp's avatar
Moritz Lipp committed
655
  if (page == NULL) {
656
657
658
    goto error_out;
  }

659
  zathura->document->current_page_number = page_id;
660
  zathura->global.update_page_number = false;
661

662
663
  page_offset_t offset;
  page_calculate_offset(page, &offset);
Moritz Lipp's avatar
Moritz Lipp committed
664

Moritz Lipp's avatar
Moritz Lipp committed
665
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
666
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
667
668
  set_adjustment(view_hadjustment, offset.x);
  set_adjustment(view_vadjustment, offset.y);
669

Moritz Lipp's avatar
Moritz Lipp committed
670
671
  statusbar_page_number_update(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
672
  return true;
Moritz Lipp's avatar
Moritz Lipp committed
673
674
675
676

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
677
678
}

679
680
681
682
683
684
685
void
statusbar_page_number_update(zathura_t* zathura)
{
  if (zathura == NULL || zathura->ui.statusbar.page_number == NULL) {
    return;
  }

686
687
688
689
690
691
692
  if (zathura->document != NULL) {
    char* page_number_text = g_strdup_printf("[%d/%d]", zathura->document->current_page_number + 1, zathura->document->number_of_pages);
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, page_number_text);
    g_free(page_number_text);
  } else {
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.page_number, "");
  }
693
694
}

695
void
696
page_widget_set_mode(zathura_t* zathura, unsigned int pages_per_row)
697
{
Moritz Lipp's avatar
Moritz Lipp committed
698
699
700
701
702
  /* show at least one page */
  if (pages_per_row == 0) {
    pages_per_row = 1;
  }

703
704
705
706
  if (zathura->document == NULL) {
    return;
  }

707
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_widget), remove_page_from_table, (gpointer)0);
708

709
  gtk_table_resize(GTK_TABLE(zathura->ui.page_widget), ceil(zathura->document->number_of_pages / pages_per_row), pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
710
  for (unsigned int i = 0; i < zathura->document->number_of_pages; i++)
711
  {
712
713
    int x = i % pages_per_row;
    int y = i / pages_per_row;
714
    gtk_table_attach(GTK_TABLE(zathura->ui.page_widget), zathura->document->pages[i]->drawing_area, x, x + 1, y, y + 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
715
716
  }

717
  gtk_widget_show_all(zathura->ui.page_widget);
718
}