zathura.c 16.7 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>
Moritz Lipp's avatar
Moritz Lipp committed
8

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

16
#include "bookmarks.h"
Moritz Lipp's avatar
Moritz Lipp committed
17
18
#include "callbacks.h"
#include "config.h"
19
#include "database.h"
20
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
21
#include "shortcuts.h"
Moritz Lipp's avatar
Moritz Lipp committed
22
#include "zathura.h"
Moritz Lipp's avatar
Moritz Lipp committed
23
#include "utils.h"
Sebastian Ramacher's avatar
Sebastian Ramacher committed
24
#include "render.h"
Moritz Lipp's avatar
Moritz Lipp committed
25

Moritz Lipp's avatar
Update    
Moritz Lipp committed
26
27
28
29
30
31
32
typedef struct zathura_document_info_s
{
  zathura_t* zathura;
  const char* path;
  const char* password;
} zathura_document_info_t;

33
static gboolean document_info_open(gpointer data);
Moritz Lipp's avatar
Moritz Lipp committed
34

Moritz Lipp's avatar
Moritz Lipp committed
35
/* function implementation */
Moritz Lipp's avatar
Moritz Lipp committed
36
37
zathura_t*
zathura_init(int argc, char* argv[])
38
{
Sebastian Ramacher's avatar
Sebastian Ramacher committed
39
40
41
  /* parse command line options */
  GdkNativeWindow embed = 0;
  gchar* config_dir = NULL, *data_dir = NULL, *plugin_path = NULL;
42
  GOptionEntry entries[] =
Sebastian Ramacher's avatar
Sebastian Ramacher committed
43
44
45
46
47
  {
    { "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" },
48
    { NULL, '\0', 0, 0, NULL, NULL, NULL }
Sebastian Ramacher's avatar
Sebastian Ramacher committed
49
50
51
52
53
54
55
56
57
58
59
  };

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

  GError* error = NULL;
  if (!g_option_context_parse(context, &argc, &argv, &error))
  {
    printf("Error parsing command line arguments: %s\n", error->message);
    g_option_context_free(context);
    g_error_free(error);
60
    return NULL;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
61
62
63
  }
  g_option_context_free(context);

64
  zathura_t* zathura = g_malloc0(sizeof(zathura_t));
65
66

  /* plugins */
67
  zathura->plugins.plugins = girara_list_new2(
Sebastian Ramacher's avatar
Sebastian Ramacher committed
68
      (girara_free_function_t)zathura_document_plugin_free);
69
70
  zathura->plugins.path    = girara_list_new2(g_free);
  zathura->plugins.type_plugin_mapping = girara_list_new2(
Sebastian Ramacher's avatar
Sebastian Ramacher committed
71
      (girara_free_function_t)zathura_type_plugin_mapping_free);
72

Sebastian Ramacher's avatar
Sebastian Ramacher committed
73
74
75
76
77
78
79
80
81
82
83
84
  if (config_dir) {
    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);
  }

  if (data_dir) {
    zathura->config.data_dir = g_strdup(config_dir);
  } else {
    gchar* path = girara_get_xdg_path(XDG_DATA);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
85
    zathura->config.data_dir = g_build_filename(path, "zathura", NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
86
87
88
    g_free(path);
  }

89
90
91
92
  /* create zathura (config/data) directory */
  g_mkdir_with_parents(zathura->config.config_dir, 0771);
  g_mkdir_with_parents(zathura->config.data_dir,   0771);

Sebastian Ramacher's avatar
Sebastian Ramacher committed
93
  if (plugin_path) {
94
95
96
    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
97
  } else {
98
#ifdef ZATHURA_PLUGINDIR
99
100
101
    girara_list_t* paths = girara_split_path_array(ZATHURA_PLUGINDIR);
    girara_list_merge(zathura->plugins.path, paths);
    girara_list_free(paths);
102
#endif
Sebastian Ramacher's avatar
Sebastian Ramacher committed
103
104
  }

105
  /* UI */
Moritz Lipp's avatar
Moritz Lipp committed
106
  if ((zathura->ui.session = girara_session_create()) == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
107
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
108
  }
109

Moritz Lipp's avatar
Moritz Lipp committed
110
  zathura->ui.session->global.data  = zathura;
Moritz Lipp's avatar
Moritz Lipp committed
111
112
113
114
115
  zathura->ui.statusbar.file        = NULL;
  zathura->ui.statusbar.buffer      = NULL;
  zathura->ui.statusbar.page_number = NULL;
  zathura->ui.page_view             = NULL;
  zathura->ui.index                 = NULL;
116

Moritz Lipp's avatar
Moritz Lipp committed
117
  /* print settings */
118
119
120
  zathura->print.settings   = NULL;
  zathura->print.page_setup = NULL;

Moritz Lipp's avatar
Moritz Lipp committed
121
122
123
  /* global settings */
  zathura->global.recolor = false;

124
125
126
  /* load plugins */
  zathura_document_plugins_load(zathura);

127
128
129
  /* configuration */
  config_load_default(zathura);

130
131
132
133
134
135
136
137
138
139
140
141
142
  /* load global configuration files */
  girara_list_t* config_dirs = girara_split_path_array(girara_get_xdg_path(XDG_CONFIG_DIRS));
  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);

  config_load_file(zathura, GLOBAL_RC);

143
144
145
  /* load local configuration files */
  char* configuration_file = g_build_filename(zathura->config.config_dir, ZATHURA_RC, NULL);
  config_load_file(zathura, configuration_file);
146
  g_free(configuration_file);
147
148
149
150
151
152
153
154
155
156

  /* initialize girara */
  zathura->ui.session->gtk.embed = embed;
  if (girara_session_init(zathura->ui.session) == false) {
    goto error_out;
  }

  /* girara events */
  zathura->ui.session->events.buffer_changed = buffer_changed;

157
  /* page view */
158
  zathura->ui.page_view = gtk_table_new(0, 0, TRUE);
Moritz Lipp's avatar
Moritz Lipp committed
159
  if (!zathura->ui.page_view) {
160
161
162
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
163
164
165
166
167
168
  /* 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);

Moritz Lipp's avatar
Moritz Lipp committed
169
  gtk_widget_show(zathura->ui.page_view);
Moritz Lipp's avatar
Moritz Lipp committed
170

171
  /* statusbar */
Moritz Lipp's avatar
Moritz Lipp committed
172
173
  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
174
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
175
176
  }

Moritz Lipp's avatar
Moritz Lipp committed
177
178
  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
179
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
180
181
  }

Moritz Lipp's avatar
Moritz Lipp committed
182
183
  zathura->ui.statusbar.page_number = girara_statusbar_item_add(zathura->ui.session, FALSE, FALSE, FALSE, NULL);
  if (!zathura->ui.statusbar.page_number) {
Moritz Lipp's avatar
Moritz Lipp committed
184
    goto error_free;
Moritz Lipp's avatar
Moritz Lipp committed
185
186
  }

Moritz Lipp's avatar
Moritz Lipp committed
187
  girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, "[No Name]");
Moritz Lipp's avatar
Moritz Lipp committed
188

Moritz Lipp's avatar
Moritz Lipp committed
189
  /* signals */
Moritz Lipp's avatar
Moritz Lipp committed
190
  g_signal_connect(G_OBJECT(zathura->ui.session->gtk.window), "destroy", G_CALLBACK(cb_destroy), NULL);
191

Moritz Lipp's avatar
Moritz Lipp committed
192
  /* save page padding */
Moritz Lipp's avatar
Moritz Lipp committed
193
  int* page_padding = girara_setting_get(zathura->ui.session, "page-padding");
Moritz Lipp's avatar
Moritz Lipp committed
194
  zathura->global.page_padding = (page_padding) ? *page_padding : 1;
195
  g_free(page_padding);
Moritz Lipp's avatar
Moritz Lipp committed
196

Moritz Lipp's avatar
Moritz Lipp committed
197
198
199
  gtk_table_set_row_spacings(GTK_TABLE(zathura->ui.page_view), zathura->global.page_padding);
  gtk_table_set_col_spacings(GTK_TABLE(zathura->ui.page_view), zathura->global.page_padding);

Moritz Lipp's avatar
Moritz Lipp committed
200
  /* parse colors */
Moritz Lipp's avatar
Moritz Lipp committed
201
  char* string_value = girara_setting_get(zathura->ui.session, "recolor-darkcolor");
Moritz Lipp's avatar
Moritz Lipp committed
202
203
  if (string_value != NULL) {
    gdk_color_parse(string_value, &(zathura->ui.colors.recolor_dark_color));
204
    g_free(string_value);
Moritz Lipp's avatar
Moritz Lipp committed
205
206
  }

Moritz Lipp's avatar
Moritz Lipp committed
207
  string_value = girara_setting_get(zathura->ui.session, "recolor-lightcolor");
Moritz Lipp's avatar
Moritz Lipp committed
208
209
  if (string_value != NULL) {
    gdk_color_parse(string_value, &(zathura->ui.colors.recolor_light_color));
210
    g_free(string_value);
Moritz Lipp's avatar
Moritz Lipp committed
211
212
  }

213
  /* database */
214
  zathura->database = zathura_db_init(zathura->config.data_dir);
215
  if (zathura->database == NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
216
    girara_error("Unable to initialize database. Bookmarks won't be available.");
217
218
  }

219
  /* bookmarks */
220
  zathura->bookmarks.bookmarks = girara_sorted_list_new2((girara_compare_function_t) zathura_bookmarks_compare,
221
    (girara_free_function_t) zathura_bookmark_free);
222

Moritz Lipp's avatar
Moritz Lipp committed
223
  /* open document if passed */
Moritz Lipp's avatar
Update    
Moritz Lipp committed
224
  if (argc > 1) {
225
    zathura_document_info_t* document_info = g_malloc0(sizeof(zathura_document_info_t));
Moritz Lipp's avatar
Update    
Moritz Lipp committed
226

227
228
229
230
    document_info->zathura  = zathura;
    document_info->path     = argv[1];
    document_info->password = (argc >= 2) ? argv[2] : NULL;
    g_idle_add(document_info_open, document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
231
232
  }

Moritz Lipp's avatar
Moritz Lipp committed
233
  return zathura;
Moritz Lipp's avatar
Moritz Lipp committed
234
235
236

error_free:

Moritz Lipp's avatar
Moritz Lipp committed
237
238
  if (zathura->ui.page_view) {
    g_object_unref(zathura->ui.page_view);
239
240
  }

Moritz Lipp's avatar
Moritz Lipp committed
241
242
243

error_out:

244
  zathura_free(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
245

Moritz Lipp's avatar
Moritz Lipp committed
246
247
248
249
250
251
252
253
254
255
  return NULL;
}

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

256
257
  document_close(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
258
259
260
261
  if (zathura->ui.session != NULL) {
    girara_session_destroy(zathura->ui.session);
  }

262
263
264
265
266
267
  /* stdin support */
  if (zathura->stdin_support.file != NULL) {
    g_unlink(zathura->stdin_support.file);
    g_free(zathura->stdin_support.file);
  }

268
269
270
  /* bookmarks */
  girara_list_free(zathura->bookmarks.bookmarks);

271
272
273
  /* database */
  zathura_db_free(zathura->database);

274
  /* free print settings */
275
276
277
278
279
280
281
  if(zathura->print.settings != NULL) {
    g_object_unref(zathura->print.settings);
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
283
  /* free registered plugins */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
284
  girara_list_free(zathura->plugins.plugins);
285
286
287
288
289
  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
290

291
  g_free(zathura);
292
293
}

294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
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)
  {
    girara_error("Can not create temporary file: %s", error->message);
    g_error_free(error);
    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;
}

346
static gboolean
Moritz Lipp's avatar
Update    
Moritz Lipp committed
347
348
349
350
351
document_info_open(gpointer data)
{
  zathura_document_info_t* document_info = data;
  g_return_val_if_fail(document_info != NULL, FALSE);

352
  if (document_info->zathura != NULL && document_info->path != NULL) {
353
354
355
356
357
358
    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.");
359
360
      } else {
        document_info->zathura->stdin_support.file = g_strdup(file);
361
362
363
364
365
366
367
368
369
      }
    } 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
370
371
  }

372
  g_free(document_info);
Moritz Lipp's avatar
Update    
Moritz Lipp committed
373
  return FALSE;
374
375
}

Moritz Lipp's avatar
Moritz Lipp committed
376
bool
Moritz Lipp's avatar
Moritz Lipp committed
377
document_open(zathura_t* zathura, const char* path, const char* password)
Moritz Lipp's avatar
Moritz Lipp committed
378
{
Moritz Lipp's avatar
Moritz Lipp committed
379
  if (!path) {
380
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
381
382
  }

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

Moritz Lipp's avatar
Moritz Lipp committed
385
  if (!document) {
386
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
387
388
  }

Moritz Lipp's avatar
Moritz Lipp committed
389
  zathura->document = document;
Moritz Lipp's avatar
Moritz Lipp committed
390

391
  /* view mode */
Moritz Lipp's avatar
Moritz Lipp committed
392
  int* value = girara_setting_get(zathura->ui.session, "pages-per-row");
393
  int pages_per_row = (value) ? *value : 1;
394
  g_free(value);
Moritz Lipp's avatar
Moritz Lipp committed
395
  page_view_set_mode(zathura, pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
396

Moritz Lipp's avatar
Moritz Lipp committed
397
  girara_set_view(zathura->ui.session, zathura->ui.page_view);
Moritz Lipp's avatar
Moritz Lipp committed
398

Moritz Lipp's avatar
Moritz Lipp committed
399
  /* threads */
Moritz Lipp's avatar
Moritz Lipp committed
400
  zathura->sync.render_thread = render_init(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
401

Moritz Lipp's avatar
Moritz Lipp committed
402
  if (!zathura->sync.render_thread) {
Moritz Lipp's avatar
Moritz Lipp committed
403
404
405
    goto error_free;
  }

Moritz Lipp's avatar
Moritz Lipp committed
406
  /* create blank pages */
407
  for (unsigned int page_id = 0; page_id < document->number_of_pages; page_id++) {
Moritz Lipp's avatar
Moritz Lipp committed
408
409
    zathura_page_t* page = document->pages[page_id];
    gtk_widget_realize(page->event_box);
410
  }
Moritz Lipp's avatar
Moritz Lipp committed
411

412
413
414
415
416
  /* bookmarks */
  if (!zathura_bookmarks_load(zathura, zathura->document->file_path)) {
    girara_warning("Failed to load bookmarks for %s.\n", zathura->document->file_path);
  }

Pavel Borzenkov's avatar
Pavel Borzenkov committed
417
  page_set_delayed(zathura, document->current_page_number - 1);
418

Moritz Lipp's avatar
Moritz Lipp committed
419
  return true;
420
421
422
423
424
425
426
427

error_free:

  zathura_document_free(document);

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
428
429
}

430
431
432
433
434
435
436
437
438
439
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);
  if (!overwrite && g_file_test(file_path, G_FILE_TEST_EXISTS))
  {
440
    girara_error("File already exists: %s. Use :write! to overwrite it.", file_path);
441
    g_free(file_path);
442
443
444
445
446
447
448
449
    return false;
  }

  bool res = zathura_document_save_as(zathura->document, file_path);
  g_free(file_path);
  return res;
}

Pavel Borzenkov's avatar
Pavel Borzenkov committed
450
451
452
453
454
455
456
457
458
459
static void
remove_page_from_table(GtkWidget* page, gpointer permanent)
{
  if (!permanent) {
    g_object_ref(G_OBJECT(page));
  }

  gtk_container_remove(GTK_CONTAINER(page->parent), page);
}

Moritz Lipp's avatar
Moritz Lipp committed
460
bool
Moritz Lipp's avatar
Moritz Lipp committed
461
document_close(zathura_t* zathura)
Moritz Lipp's avatar
Moritz Lipp committed
462
{
Moritz Lipp's avatar
Moritz Lipp committed
463
  if (!zathura->document) {
Moritz Lipp's avatar
Moritz Lipp committed
464
465
466
    return false;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
467
  /* store last seen page */
468
  zathura_db_set_fileinfo(zathura->database, zathura->document->file_path, zathura->document->current_page_number + 1,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
469
470
      /* zathura->document->offset TODO */ 0, zathura->document->scale);

Pavel Borzenkov's avatar
Pavel Borzenkov committed
471
472
473
474
  render_free(zathura->sync.render_thread);
  zathura->sync.render_thread = NULL;

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

Moritz Lipp's avatar
Moritz Lipp committed
476
  zathura_document_free(zathura->document);
Pavel Borzenkov's avatar
Pavel Borzenkov committed
477
478
  zathura->document = NULL;

479
480
481
482
483
484
485
  gtk_widget_hide(zathura->ui.page_view);

  statusbar_page_number_update(zathura);

  if (zathura->ui.session != NULL && zathura->ui.statusbar.file != NULL) {
    girara_statusbar_item_set_text(zathura->ui.session, zathura->ui.statusbar.file, "[No name]");
  }
Moritz Lipp's avatar
Moritz Lipp committed
486
487
488
489

  return true;
}

490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
typedef struct page_set_delayed_s {
  zathura_t* zathura;
  unsigned int page;
} page_set_delayed_t;

static gboolean
page_set_delayed_impl(gpointer data) {
  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;
  g_idle_add(page_set_delayed_impl, p);
  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
519
bool
Moritz Lipp's avatar
Moritz Lipp committed
520
page_set(zathura_t* zathura, unsigned int page_id)
Moritz Lipp's avatar
Moritz Lipp committed
521
{
522
  if (zathura == NULL || zathura->document == NULL || zathura->document->pages == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
523
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
524
525
  }

Moritz Lipp's avatar
Moritz Lipp committed
526
  if (page_id >= zathura->document->number_of_pages) {
Moritz Lipp's avatar
Moritz Lipp committed
527
    goto error_out;
Moritz Lipp's avatar
Moritz Lipp committed
528
529
  }

530
  /* render page */
Moritz Lipp's avatar
Moritz Lipp committed
531
  zathura_page_t* page = zathura->document->pages[page_id];
Moritz Lipp's avatar
Moritz Lipp committed
532

Moritz Lipp's avatar
Moritz Lipp committed
533
  if (!page) {
534
535
536
    goto error_out;
  }

Moritz Lipp's avatar
Moritz Lipp committed
537
538
539
540
541
  page_offset_t* offset = page_calculate_offset(page);
  if (offset == NULL) {
    goto error_out;
  }

Moritz Lipp's avatar
Moritz Lipp committed
542
  GtkAdjustment* view_vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
543
544
545
  GtkAdjustment* view_hadjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
  gtk_adjustment_set_value(view_hadjustment, offset->x);
  gtk_adjustment_set_value(view_vadjustment, offset->y);
546
547

  /* update page number */
Moritz Lipp's avatar
Moritz Lipp committed
548
  zathura->document->current_page_number = page_id;
549
  statusbar_page_number_update(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
550
551

  return true;
Moritz Lipp's avatar
Moritz Lipp committed
552
553
554
555

error_out:

  return false;
Moritz Lipp's avatar
Moritz Lipp committed
556
557
}

558
559
560
561
562
563
564
void
statusbar_page_number_update(zathura_t* zathura)
{
  if (zathura == NULL || zathura->ui.statusbar.page_number == NULL) {
    return;
  }

565
566
567
568
569
570
571
  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, "");
  }
572
573
}

574
void
Moritz Lipp's avatar
Moritz Lipp committed
575
page_view_set_mode(zathura_t* zathura, unsigned int pages_per_row)
576
{
Moritz Lipp's avatar
Moritz Lipp committed
577
578
579
580
581
  /* show at least one page */
  if (pages_per_row == 0) {
    pages_per_row = 1;
  }

582
583
584
585
  if (zathura->document == NULL) {
    return;
  }

Pavel Borzenkov's avatar
Pavel Borzenkov committed
586
  gtk_container_foreach(GTK_CONTAINER(zathura->ui.page_view), remove_page_from_table, (gpointer)0);
587

588
  gtk_table_resize(GTK_TABLE(zathura->ui.page_view), zathura->document->number_of_pages / pages_per_row + 1, pages_per_row);
Moritz Lipp's avatar
Moritz Lipp committed
589
  for (unsigned int i = 0; i < zathura->document->number_of_pages; i++)
590
  {
591
592
    int x = i % pages_per_row;
    int y = i / pages_per_row;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
593
    gtk_table_attach(GTK_TABLE(zathura->ui.page_view), zathura->document->pages[i]->event_box, x, x + 1, y, y + 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
594
595
  }

Moritz Lipp's avatar
Moritz Lipp committed
596
  gtk_widget_show_all(zathura->ui.page_view);
597
598
}

Moritz Lipp's avatar
Moritz Lipp committed
599
/* main function */
600
int main(int argc, char* argv[])
Moritz Lipp's avatar
Moritz Lipp committed
601
{
602
603
  g_thread_init(NULL);
  gdk_threads_init();
Moritz Lipp's avatar
Moritz Lipp committed
604
605
  gtk_init(&argc, &argv);

Moritz Lipp's avatar
Moritz Lipp committed
606
607
  zathura_t* zathura = zathura_init(argc, argv);
  if (zathura == NULL) {
Pavel Borzenkov's avatar
Pavel Borzenkov committed
608
    printf("error: could not initialize zathura\n");
Moritz Lipp's avatar
Moritz Lipp committed
609
610
    return -1;
  }
Moritz Lipp's avatar
Moritz Lipp committed
611

Moritz Lipp's avatar
Moritz Lipp committed
612
613
614
  gdk_threads_enter();
  gtk_main();
  gdk_threads_leave();
615

Moritz Lipp's avatar
Moritz Lipp committed
616
617
  zathura_free(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
618
619
  return 0;
}
620