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

3
4
5
6
#include <girara/session.h>
#include <girara/settings.h>
#include <girara/datastructures.h>
#include <girara/shortcuts.h>
7
#include <girara/utils.h>
Moritz Lipp's avatar
Moritz Lipp committed
8
#include <gtk/gtk.h>
9
#include <glib/gi18n.h>
Moritz Lipp's avatar
Moritz Lipp committed
10
11

#include "callbacks.h"
12
#include "shortcuts.h"
13
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
14
#include "zathura.h"
15
#include "render.h"
16
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
17
#include "page.h"
Moritz Lipp's avatar
Moritz Lipp committed
18
#include "print.h"
19
#include "page-widget.h"
20
#include "adjustment.h"
21

22
23
24
25
26
27
28
29
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif

#ifndef MAX
#define MAX(a,b) (((a)>(b))?(a):(b))
#endif

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* Helper function; see sc_display_link and sc_follow. */
static bool
draw_links(zathura_t* zathura)
{
  /* set pages to draw links */
  bool show_links = false;
  unsigned int page_offset = 0;
  unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
    if (page == NULL) {
      continue;
    }

    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
Marwan Tanager's avatar
Marwan Tanager committed
45
    g_object_set(page_widget, "draw-search-results", FALSE, NULL);
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    if (zathura_page_get_visibility(page) == true) {
      g_object_set(page_widget, "draw-links", TRUE, NULL);

      int number_of_links = 0;
      g_object_get(page_widget, "number-of-links", &number_of_links, NULL);
      if (number_of_links != 0) {
        show_links = true;
      }
      g_object_set(page_widget, "offset-links", page_offset, NULL);
      page_offset += number_of_links;
    } else {
      g_object_set(page_widget, "draw-links", FALSE, NULL);
    }
  }
  return show_links;
}

Moritz Lipp's avatar
Moritz Lipp committed
63
bool
64
sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
65
         girara_event_t* UNUSED(event), unsigned int UNUSED(t))
66
{
Moritz Lipp's avatar
Moritz Lipp committed
67
  g_return_val_if_fail(session != NULL, false);
68
69
70
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

71
72
73
  bool clear_search = true;
  girara_setting_get(session, "abort-clear-search", &clear_search);

Moritz Lipp's avatar
Moritz Lipp committed
74
  if (zathura->document != NULL) {
75
    unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
76
77
    for (unsigned int page_id = 0; page_id < number_of_pages; ++page_id) {
      zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
Moritz Lipp's avatar
Moritz Lipp committed
78
79
80
81
      if (page == NULL) {
        continue;
      }

Marwan Tanager's avatar
Marwan Tanager committed
82
83
      GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
      g_object_set(page_widget, "draw-links", FALSE, NULL);
84
      if (clear_search == true) {
Marwan Tanager's avatar
Marwan Tanager committed
85
        g_object_set(page_widget, "draw-search-results", FALSE, NULL);
86
      }
87
88
    }
  }
Moritz Lipp's avatar
Moritz Lipp committed
89

90
91
92
93
  /* Setting the mode back here has not worked for ages. We need another way to
   * do this. Let's disable this for now.
   */
  /* girara_mode_set(session, session->modes.normal); */
Moritz Lipp's avatar
Moritz Lipp committed
94
  girara_sc_abort(session, NULL, NULL, 0);
Moritz Lipp's avatar
Moritz Lipp committed
95

Moritz Lipp's avatar
Moritz Lipp committed
96
  return false;
97
98
}

99
100
bool
sc_adjust_window(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
101
                 girara_event_t* UNUSED(event), unsigned int UNUSED(t))
102
{
103
104
105
106
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
107

108
  zathura_document_set_adjust_mode(zathura->document, argument->n);
109
  adjust_view(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
110

111
  return false;
112
113
}

Moritz Lipp's avatar
Moritz Lipp committed
114
bool
Moritz Lipp's avatar
Moritz Lipp committed
115
sc_change_mode(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
116
               girara_event_t* UNUSED(event), unsigned int UNUSED(t))
117
{
Moritz Lipp's avatar
Moritz Lipp committed
118
119
120
121
  g_return_val_if_fail(session != NULL, false);

  girara_mode_set(session, argument->n);

Moritz Lipp's avatar
Moritz Lipp committed
122
  return false;
123
124
}

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
bool
sc_display_link(girara_session_t* session, girara_argument_t* UNUSED(argument),
                girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document == NULL || zathura->ui.session == NULL) {
    return false;
  }

  bool show_links = draw_links(zathura);

  /* ask for input */
  if (show_links) {
141
    zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
142
143
144
145
146
147
148
149
    girara_dialog(zathura->ui.session, "Display link:", FALSE, NULL,
        (girara_callback_inputbar_activate_t) cb_sc_display_link,
        zathura->ui.session);
  }

  return false;
}

150
151
152
153
154
155
156
157
158
bool
sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);

159
160
  zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
    gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
  }

  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
    gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
  }

  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));

  if (argument->data != NULL) {
    gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);

    /* append filepath */
    if (argument->n == APPEND_FILEPATH && zathura->document != NULL) {
176
177
178
179
      const char* file_path = zathura_document_get_path(zathura->document);
      if (file_path == NULL) {
        return false;
      }
180

181
182
183
184
185
      char* path = g_path_get_dirname(file_path);
      char* escaped = girara_escape_string(path);
      char* tmp  = g_strdup_printf("%s%s/", (char*) argument->data, (g_strcmp0(path, "/") == 0) ? "" : escaped);
      g_free(path);
      g_free(escaped);
186
187
188
189
190

      gtk_entry_set_text(session->gtk.inputbar_entry, tmp);
      g_free(tmp);
    }

191
192
    GdkAtom* selection = get_selection(zathura);

193
    /* we save the X clipboard that will be clear by "grab_focus" */
194
    gchar* x_clipboard_text = NULL;
195
196
197
198

    if (selection != NULL) {
      x_clipboard_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(*selection));
    }
199
200
201

    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);

202
    if (x_clipboard_text != NULL && selection != NULL) {
203
      /* we reset the X clipboard with saved text */
204
      gtk_clipboard_set_text(gtk_clipboard_get(*selection), x_clipboard_text, -1);
205
206
      g_free(x_clipboard_text);
    }
207
208

    g_free(selection);
209
210
211
212
213
  }

  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
214
bool
215
sc_follow(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
216
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
217
{
Moritz Lipp's avatar
Moritz Lipp committed
218
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
219
220
221
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

Moritz Lipp's avatar
Moritz Lipp committed
222
  if (zathura->document == NULL || zathura->ui.session == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
223
224
225
    return false;
  }

226
  bool show_links = draw_links(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
227

Moritz Lipp's avatar
Moritz Lipp committed
228
  /* ask for input */
Moritz Lipp's avatar
Moritz Lipp committed
229
  if (show_links == true) {
230
    zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
Moritz Lipp's avatar
Moritz Lipp committed
231
    girara_dialog(zathura->ui.session, "Follow link:", FALSE, NULL, (girara_callback_inputbar_activate_t) cb_sc_follow, zathura->ui.session);
Moritz Lipp's avatar
Moritz Lipp committed
232
  }
Moritz Lipp's avatar
Moritz Lipp committed
233

Moritz Lipp's avatar
Moritz Lipp committed
234
235
  return false;
}
236

Moritz Lipp's avatar
Moritz Lipp committed
237
bool
Moritz Lipp's avatar
Moritz Lipp committed
238
sc_goto(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int t)
Moritz Lipp's avatar
Moritz Lipp committed
239
{
Moritz Lipp's avatar
Moritz Lipp committed
240
241
242
243
244
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
245

246
  zathura_jumplist_add(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
247
  if (t != 0) {
248
    /* add offset */
Moritz Lipp's avatar
Moritz Lipp committed
249
    t += zathura_document_get_page_offset(zathura->document);
250

Moritz Lipp's avatar
Moritz Lipp committed
251
    page_set(zathura, t - 1);
Moritz Lipp's avatar
Moritz Lipp committed
252
  } else if (argument->n == TOP) {
Moritz Lipp's avatar
Moritz Lipp committed
253
    page_set(zathura, 0);
Moritz Lipp's avatar
Moritz Lipp committed
254
  } else if (argument->n == BOTTOM) {
255
    page_set(zathura, zathura_document_get_number_of_pages(zathura->document) - 1);
256
257
  }

258
259
  zathura_jumplist_add(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  return false;
}

bool
sc_mouse_scroll(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(event != NULL, false);

  if (zathura->document == NULL) {
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
276
277
278
279
280
281
282
  static int x = 0;
  static int y = 0;

  GtkAdjustment* x_adj = NULL;
  GtkAdjustment* y_adj = NULL;

  switch (event->type) {
Moritz Lipp's avatar
Moritz Lipp committed
283
      /* scroll */
Moritz Lipp's avatar
Moritz Lipp committed
284
285
286
287
    case GIRARA_EVENT_SCROLL_UP:
    case GIRARA_EVENT_SCROLL_DOWN:
    case GIRARA_EVENT_SCROLL_LEFT:
    case GIRARA_EVENT_SCROLL_RIGHT:
288
    case GIRARA_EVENT_SCROLL_BIDIRECTIONAL:
289
      return sc_scroll(session, argument, event, t);
Moritz Lipp's avatar
Moritz Lipp committed
290

Moritz Lipp's avatar
Moritz Lipp committed
291
      /* drag */
Moritz Lipp's avatar
Moritz Lipp committed
292
293
294
295
296
297
298
299
300
301
    case GIRARA_EVENT_BUTTON_PRESS:
      x = event->x;
      y = event->y;
      break;
    case GIRARA_EVENT_BUTTON_RELEASE:
      x = 0;
      y = 0;
      break;
    case GIRARA_EVENT_MOTION_NOTIFY:
      x_adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
302
      y_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
303
304
305
306

      if (x_adj == NULL || y_adj == NULL) {
        return false;
      }
Moritz Lipp's avatar
Moritz Lipp committed
307

308
309
310
311
      zathura_adjustment_set_value(x_adj,
          gtk_adjustment_get_value(x_adj) - (event->x - x));
      zathura_adjustment_set_value(y_adj,
          gtk_adjustment_get_value(y_adj) - (event->y - y));
Moritz Lipp's avatar
Moritz Lipp committed
312
313
      break;

Moritz Lipp's avatar
Moritz Lipp committed
314
      /* unhandled events */
Moritz Lipp's avatar
Moritz Lipp committed
315
316
    default:
      break;
Moritz Lipp's avatar
Moritz Lipp committed
317
318
  }

Moritz Lipp's avatar
Moritz Lipp committed
319
  return false;
320
321
}

Moritz Lipp's avatar
Moritz Lipp committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
bool
sc_mouse_zoom(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(event != NULL, false);

  if (zathura->document == NULL) {
    return false;
  }

  /* scroll event */
336
337
338
339
340
341
342
  switch (event->type) {
    case GIRARA_EVENT_SCROLL_UP:
      argument->n = ZOOM_IN;
      break;
    case GIRARA_EVENT_SCROLL_DOWN:
      argument->n = ZOOM_OUT;
      break;
343
344
345
    case GIRARA_EVENT_SCROLL_BIDIRECTIONAL:
      argument->n = ZOOM_SMOOTH;
      break;
346
347
    default:
      return false;
Moritz Lipp's avatar
Moritz Lipp committed
348
349
  }

350
  return sc_zoom(session, argument, event, t);
351
352
}

Moritz Lipp's avatar
Moritz Lipp committed
353
bool
Moritz Lipp's avatar
Moritz Lipp committed
354
sc_navigate(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
355
            girara_event_t* UNUSED(event), unsigned int t)
356
{
Moritz Lipp's avatar
Moritz Lipp committed
357
358
359
360
361
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
362

363
364
  int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  int new_page        = zathura_document_get_current_page_number(zathura->document);
Moritz Lipp's avatar
Moritz Lipp committed
365

366
367
368
  bool scroll_wrap = false;
  girara_setting_get(session, "scroll-wrap", &scroll_wrap);

369
370
371
372
373
374
375
376
  bool columns_per_row_offset = false;
  girara_setting_get(session, "advance-pages-per-row", &columns_per_row_offset);

  int offset = 1;
  if (columns_per_row_offset == true) {
    girara_setting_get(session, "pages-per-row", &offset);
  }

Moritz Lipp's avatar
Moritz Lipp committed
377
  t = (t == 0) ? (unsigned int) offset : t;
Moritz Lipp's avatar
Moritz Lipp committed
378
  if (argument->n == NEXT) {
Benoît Knecht's avatar
Benoît Knecht committed
379
    if (scroll_wrap == false) {
380
381
382
383
      new_page = new_page + t;
    } else {
      new_page = (new_page + t) % number_of_pages;
    }
Moritz Lipp's avatar
Moritz Lipp committed
384
  } else if (argument->n == PREVIOUS) {
Benoît Knecht's avatar
Benoît Knecht committed
385
    if (scroll_wrap == false) {
386
387
388
389
390
391
      new_page = new_page - t;
    } else {
      new_page = (new_page + number_of_pages - t) % number_of_pages;
    }
  }

392
393
394
395
396
397
  if (!scroll_wrap) {
    if (new_page <= 0) {
      new_page = 0;
    } else if (new_page >= number_of_pages) {
      new_page = number_of_pages - 1;
    }
Moritz Lipp's avatar
Moritz Lipp committed
398
399
  }

400
  page_set(zathura, new_page);
Moritz Lipp's avatar
Moritz Lipp committed
401

Moritz Lipp's avatar
Moritz Lipp committed
402
  return false;
403
404
}

Moritz Lipp's avatar
Moritz Lipp committed
405
406
bool
sc_print(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
407
         girara_event_t* UNUSED(event), unsigned int UNUSED(t))
Moritz Lipp's avatar
Moritz Lipp committed
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document == NULL) {
    girara_notify(session, GIRARA_ERROR, _("No document opened."));
    return false;
  }

  print(zathura);

  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
423
bool
424
sc_recolor(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
425
           girara_event_t* UNUSED(event), unsigned int UNUSED(t))
426
{
Moritz Lipp's avatar
Moritz Lipp committed
427
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
428

Moritz Lipp's avatar
Moritz Lipp committed
429
430
431
432
  bool value = false;
  girara_setting_get(session, "recolor", &value);
  value = !value;
  girara_setting_set(session, "recolor", &value);
Moritz Lipp's avatar
Moritz Lipp committed
433

Moritz Lipp's avatar
Moritz Lipp committed
434
  return false;
435
436
}

Moritz Lipp's avatar
Moritz Lipp committed
437
bool
438
sc_reload(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
439
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
440
{
Moritz Lipp's avatar
Moritz Lipp committed
441
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
442
443
444
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

Moritz Lipp's avatar
Moritz Lipp committed
445
  if (zathura->file_monitor.file_path == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
446
447
448
449
    return false;
  }

  /* close current document */
Moritz Lipp's avatar
Moritz Lipp committed
450
  document_close(zathura, true);
Moritz Lipp's avatar
Moritz Lipp committed
451
452

  /* reopen document */
453
454
455
  document_open(zathura, zathura->file_monitor.file_path,
                zathura->file_monitor.password,
                ZATHURA_PAGE_NUMBER_UNSPECIFIED);
Moritz Lipp's avatar
Moritz Lipp committed
456

Moritz Lipp's avatar
Moritz Lipp committed
457
  return false;
458
459
}

Moritz Lipp's avatar
Moritz Lipp committed
460
bool
461
sc_rotate(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
462
          girara_event_t* UNUSED(event), unsigned int t)
463
{
Moritz Lipp's avatar
Moritz Lipp committed
464
465
466
467
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(zathura->document != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
468

469
  unsigned int page_number = zathura_document_get_current_page_number(zathura->document);
470

471
472
473
474
475
  int angle = 90;
  if (argument != NULL && argument->n == ROTATE_CCW) {
    angle = 270;
  }

Moritz Lipp's avatar
Moritz Lipp committed
476
  /* update rotate value */
Moritz Lipp's avatar
Moritz Lipp committed
477
  t = (t == 0) ? 1 : t;
478
  unsigned int rotation = zathura_document_get_rotation(zathura->document);
Moritz Lipp's avatar
Moritz Lipp committed
479
  zathura_document_set_rotation(zathura->document, (rotation + angle * t) % 360);
Moritz Lipp's avatar
Moritz Lipp committed
480

481
482
483
484
  /* update scale */
  girara_argument_t new_argument = { zathura_document_get_adjust_mode(zathura->document), NULL };
  sc_adjust_window(zathura->ui.session, &new_argument, NULL, 0);

Moritz Lipp's avatar
Moritz Lipp committed
485
  /* render all pages again */
486
  render_all(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
487

488
  page_set(zathura, page_number);
489

Moritz Lipp's avatar
Moritz Lipp committed
490
  return false;
491
492
}

Moritz Lipp's avatar
Moritz Lipp committed
493
bool
Moritz Lipp's avatar
Moritz Lipp committed
494
sc_scroll(girara_session_t* session, girara_argument_t* argument,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
495
          girara_event_t* event, unsigned int t)
496
{
Moritz Lipp's avatar
Moritz Lipp committed
497
498
499
500
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
501
502
503
  if (zathura->document == NULL) {
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
504

505
506
507
508
509
510
511
512
513
  /* if TOP or BOTTOM, go there and we are done */
  if (argument->n == TOP) {
    position_set(zathura, -1, 0);
    return false;
  } else if (argument->n == BOTTOM) {
    position_set(zathura, -1, 1.0);
    return false;
  }

514
515
516
517
518
519
520
  /* Retrieve current page and position */
  const unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
  double pos_x = zathura_document_get_position_x(zathura->document);
  double pos_y = zathura_document_get_position_y(zathura->document);

  /* If PAGE_TOP or PAGE_BOTTOM, go there and we are done */
  if (argument->n == PAGE_TOP) {
521
522
523
    double dontcare = 0.5;
    page_number_to_position(zathura->document, page_id, dontcare, 0.0, &dontcare, &pos_y);
    position_set(zathura, pos_x, pos_y);
524
525
    return false;
  } else if (argument->n == PAGE_BOTTOM) {
526
527
528
    double dontcare = 0.5;
    page_number_to_position(zathura->document, page_id, dontcare, 1.0, &dontcare, &pos_y);
    position_set(zathura, pos_x, pos_y);
529
530
531
    return false;
  }

Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
532
  if (t == 0) {
533
534
535
    t = 1;
  }

Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
536
537
  unsigned int view_width  = 0;
  unsigned int view_height = 0;
538
  zathura_document_get_viewport_size(zathura->document, &view_height, &view_width);
Moritz Lipp's avatar
Moritz Lipp committed
539

Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
540
541
  unsigned int doc_width  = 0;
  unsigned int doc_height = 0;
542
  zathura_document_get_document_size(zathura->document, &doc_height, &doc_width);
543

544
545
  float scroll_step = 40;
  girara_setting_get(session, "scroll-step", &scroll_step);
Julian Orth's avatar
Julian Orth committed
546
547
548
549
550
  float scroll_hstep = -1;
  girara_setting_get(session, "scroll-hstep", &scroll_hstep);
  if (scroll_hstep < 0) {
    scroll_hstep = scroll_step;
  }
551
  float scroll_full_overlap = 0.0;
552
  girara_setting_get(session, "scroll-full-overlap", &scroll_full_overlap);
Jonas Hoersch's avatar
Jonas Hoersch committed
553
554
  bool scroll_page_aware = false;
  girara_setting_get(session, "scroll-page-aware", &scroll_page_aware);
555

556
557
558
  bool scroll_wrap = false;
  girara_setting_get(session, "scroll-wrap", &scroll_wrap);

559
  /* compute the direction of scrolling */
560
561
562
  double direction = 1.0;
  if ((argument->n == LEFT) || (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT) ||
      (argument->n == UP) || (argument->n == FULL_UP) || (argument->n == HALF_UP)) {
563
564
565
    direction = -1.0;
  }

566
567
  const double vstep = (double)view_height / (double)doc_height;
  const double hstep = (double)view_width / (double)doc_width;
568
569

  /* compute new position */
570
  switch (argument->n) {
Moritz Lipp's avatar
Moritz Lipp committed
571
572
    case FULL_UP:
    case FULL_DOWN:
573
574
575
576
      pos_y += direction * (1.0 - scroll_full_overlap) * vstep;
      break;

    case FULL_LEFT:
577
    case FULL_RIGHT:
578
      pos_x += direction * (1.0 - scroll_full_overlap) * hstep;
Moritz Lipp's avatar
Moritz Lipp committed
579
      break;
580

Moritz Lipp's avatar
Moritz Lipp committed
581
582
    case HALF_UP:
    case HALF_DOWN:
583
      pos_y += direction * 0.5 * vstep;
Moritz Lipp's avatar
Moritz Lipp committed
584
      break;
585
586
587
588

    case HALF_LEFT:
    case HALF_RIGHT:
      pos_x += direction * 0.5 * hstep;
Julian Orth's avatar
Julian Orth committed
589
      break;
590

Moritz Lipp's avatar
Moritz Lipp committed
591
592
    case UP:
    case DOWN:
593
      pos_y += direction * t * scroll_step / (double)doc_height;
Moritz Lipp's avatar
Moritz Lipp committed
594
      break;
595
596
597
598

    case LEFT:
    case RIGHT:
      pos_x += direction * t * scroll_hstep / (double)doc_width;
Moritz Lipp's avatar
Moritz Lipp committed
599
      break;
600
601

    case BIDIRECTIONAL: {
602
603
      pos_x += event->x * t * scroll_hstep / (double)doc_width;
      pos_y += event->y * t * scroll_step / (double)doc_height;
604
605
      break;
    }
Moritz Lipp's avatar
Moritz Lipp committed
606
607
  }

608
  /* handle boundaries */
609
610
  const double end_x = 0.5 * (double)view_width / (double)doc_width;
  const double end_y = 0.5 * (double)view_height / (double)doc_height;
611

612
613
  const double new_x = scroll_wrap ? 1.0 - end_x : end_x;
  const double new_y = scroll_wrap ? 1.0 - end_y : end_y;
Jonas Hoersch's avatar
Jonas Hoersch committed
614

615
616
617
618
619
  if (pos_x < end_x) {
    pos_x = new_x;
  } else if (pos_x > 1.0 - end_x) {
    pos_x = 1 - new_x;
  }
Jonas Hoersch's avatar
Jonas Hoersch committed
620

621
622
623
624
625
  if (pos_y < end_y) {
    pos_y = new_y;
  } else if (pos_y > 1.0 - end_y) {
    pos_y = 1 - new_y;
  }
Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
626

627
  /* snap to the border if we change page */
628
  const unsigned int new_page_id = position_to_page_number(zathura->document, pos_x, pos_y);
629
  if (scroll_page_aware == true && page_id != new_page_id) {
630
    double dummy = 0.0;
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
    switch(argument->n) {
      case FULL_LEFT:
      case HALF_LEFT:
        page_number_to_position(zathura->document, new_page_id, 1.0, 0.0, &pos_x, &dummy);
        break;

      case FULL_RIGHT:
      case HALF_RIGHT:
        page_number_to_position(zathura->document, new_page_id, 0.0, 0.0, &pos_x, &dummy);
        break;

      case FULL_UP:
      case HALF_UP:
        page_number_to_position(zathura->document, new_page_id, 0.0, 1.0, &dummy, &pos_y);
        break;

      case FULL_DOWN:
      case HALF_DOWN:
        page_number_to_position(zathura->document, new_page_id, 0.0, 0.0, &dummy, &pos_y);
        break;
Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
651
    }
Jonas Hoersch's avatar
Jonas Hoersch committed
652
653
  }

654
  position_set(zathura, pos_x, pos_y);
Moritz Lipp's avatar
Moritz Lipp committed
655
  return false;
656
657
}

658
659

bool
660
661
sc_jumplist(girara_session_t* session, girara_argument_t* argument,
            girara_event_t* UNUSED(event), unsigned int UNUSED(t))
662
663
664
665
666
667
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
668
669
670

  /* if no jumps in the jumplist */
  if (zathura->jumplist.size == 0) {
Moritz Lipp's avatar
Moritz Lipp committed
671
    return true;
672
  }
673

674
675
676
  double x = zathura_document_get_position_x(zathura->document);
  double y = zathura_document_get_position_y(zathura->document);

677
  zathura_jump_t* jump = NULL;
678
679
680
  zathura_jump_t* prev_jump = zathura_jumplist_current(zathura);
  bool go_to_current = false;

Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
681
  if (zathura_jumplist_has_next(zathura) == false || zathura_jumplist_has_previous(zathura) == false) {
682
683
684
685
686
687
688
    if (x == prev_jump->x && y == prev_jump->y) {
      go_to_current = false;
    } else {
      go_to_current = true;
    }
  }

689
690
  switch(argument->n) {
    case FORWARD:
691
692
693
694
695
696
      if (go_to_current == true && zathura_jumplist_has_previous(zathura) == false) {
        jump = zathura_jumplist_current(zathura);
      } else {
        zathura_jumplist_forward(zathura);
        jump = zathura_jumplist_current(zathura);
      }
697
698
      break;
    case BACKWARD:
699
700
701
702
703
704
      if (go_to_current == true && zathura_jumplist_has_next(zathura) == false) {
        jump = zathura_jumplist_current(zathura);
      } else {
        zathura_jumplist_backward(zathura);
        jump = zathura_jumplist_current(zathura);
      }
705
706
707
      break;
  }

708
  if (jump == prev_jump) {
Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
709
710
    if ((zathura_jumplist_has_previous(zathura) == false && argument->n == BACKWARD) ||
        (zathura_jumplist_has_next(zathura) == false && argument->n == FORWARD)) {
711
712
713
714
      jump = NULL;
    }
  }

715
  if (jump != NULL) {
716
717
718
    page_set(zathura, jump->page);
    position_set(zathura, jump->x, jump->y);
  }
719
720
721
722

  return false;
}

723
724
725
726
727
728
729
730
731
732
733

bool
sc_bisect(girara_session_t* session, girara_argument_t* argument,
          girara_event_t* UNUSED(event), unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

734
735
  const unsigned int num_pages = zathura_document_get_number_of_pages(zathura->document);
  const unsigned int cur_page = zathura_document_get_current_page_number(zathura->document);
736
737
738

  /* process arguments */
  int direction;
739
740
741
742
743
744
745
746
747
748
  if (t > 0 && t <= num_pages) {
    /* bisect between cur_page and t */
    t -= 1;
    if (t == cur_page) {
      /* nothing to do */
      return false;
    }
    else if (t > cur_page) {
      zathura->bisect.start = cur_page;
      zathura->bisect.end = t;
749
750
      direction = BACKWARD;
    } else {
751
752
      zathura->bisect.start = t;
      zathura->bisect.end = cur_page;
753
754
755
756
757
      direction = FORWARD;
    }
  } else if (argument != NULL)  {
    direction = argument->n;

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
758
    /* setup initial bisect range */
759
760
761
762
763
764
    zathura_jump_t* jump = zathura_jumplist_current(zathura);
    if (jump == NULL) {
      girara_debug("bisecting between first and last page because there are no jumps");
      zathura->bisect.start = 0;
      zathura->bisect.end = num_pages - 1;

Moritz Lipp's avatar
Moritz Lipp committed
765
    } else if (jump->page != cur_page || jump->page != zathura->bisect.last_jump) {
Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
766
767
768
      girara_debug("last jump doesn't match up, starting new bisecting");
      zathura->bisect.start = 0;
      zathura->bisect.end = num_pages - 1;
769

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
770
771
772
773
774
775
776
777
778
779
780
781
782
      unsigned int prev_page;
      if (direction == FORWARD) {
        prev_page = num_pages - 1;
      } else {
        prev_page = 0;
      }

      /* check if we have previous jumps */
      if (zathura_jumplist_has_previous(zathura) == true) {
        zathura_jumplist_backward(zathura);
        jump = zathura_jumplist_current(zathura);
        if (jump != NULL) {
          prev_page = jump->page;
783
        }
Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
784
        zathura_jumplist_forward(zathura);
785
      }
Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
786
787
788
789

      zathura->bisect.start = MIN(prev_page, cur_page);
      zathura->bisect.end = MAX(prev_page, cur_page);
      zathura->bisect.last_jump = cur_page;
790
    }
791
792
793
794
795
796
797
798
  } else {
    return false;
  }

  girara_debug("bisecting between %d and %d, at %d", zathura->bisect.start, zathura->bisect.end, cur_page);
  if (zathura->bisect.start == zathura->bisect.end) {
    /* nothing to do */
    return false;
799
800
  }

801
802
803
804
  unsigned int next_page = cur_page;
  unsigned int next_start = zathura->bisect.start;
  unsigned int next_end = zathura->bisect.end;

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
805
806
807
  /* here we have next_start <= next_page <= next_end */

  /* bisect step */
808
809
  switch(direction) {
    case FORWARD:
810
811
812
813
      if (cur_page != zathura->bisect.end) {
        next_page = (cur_page + zathura->bisect.end) / 2;
        if (next_page == cur_page) {
          ++next_page;
814
        }
815
        next_start = cur_page;
816
817
818
819
      }
      break;

    case BACKWARD:
820
821
822
823
      if (cur_page != zathura->bisect.start) {
        next_page = (cur_page + zathura->bisect.start) / 2;
        if (next_page == cur_page) {
          --next_page;
824
        }
825
        next_end = cur_page;
826
827
828
      }
      break;
  }
829

830
831
832
833
834
835
836
837
838
839
  if (next_page == cur_page) {
    /* nothing to do */
    return false;
  }

  girara_debug("bisecting between %d and %d, jumping to %d", zathura->bisect.start, zathura->bisect.end, next_page);
  zathura->bisect.last_jump = next_page;
  zathura->bisect.start = next_start;
  zathura->bisect.end = next_end;

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
840
  zathura_jumplist_add(zathura);
841
842
843
  page_set(zathura, next_page);
  zathura_jumplist_add(zathura);

844
845
846
847
  return false;
}


Moritz Lipp's avatar
Moritz Lipp committed
848
bool
Moritz Lipp's avatar
Moritz Lipp committed
849
sc_search(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
850
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
851
{
Moritz Lipp's avatar
Moritz Lipp committed
852
853
854
855
856
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
857

Marwan Tanager's avatar
Marwan Tanager committed
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
  const unsigned int num_pages = zathura_document_get_number_of_pages(zathura->document);
  const unsigned int cur_page  = zathura_document_get_current_page_number(zathura->document);
  GtkWidget *cur_page_widget = zathura_page_get_widget(zathura, zathura_document_get_page(zathura->document, cur_page));
  bool nohlsearch, first_time_after_abort, draw;

  nohlsearch = first_time_after_abort = draw = false;
  girara_setting_get(session, "nohlsearch", &nohlsearch);

  if (nohlsearch == false) {
    g_object_get(cur_page_widget, "draw-search-results", &draw, NULL);

    if (draw == false) {
      first_time_after_abort = true;
    }

    document_draw_search_results(zathura, true);
  }
875

876
  int diff = argument->n == FORWARD ? 1 : -1;
877
878
  if (zathura->global.search_direction == BACKWARD)
    diff = -diff;
879

Sebastian Ramacher's avatar
Sebastian Ramacher committed
880
881
882
  zathura_page_t* target_page = NULL;
  int target_idx = 0;

Marwan Tanager's avatar
Marwan Tanager committed
883
  for (unsigned int page_id = 0; page_id < num_pages; ++page_id) {
884
    int tmp = cur_page + diff * page_id;
885
    zathura_page_t* page = zathura_document_get_page(zathura->document, (tmp + num_pages) % num_pages);
886
887
888
889
    if (page == NULL) {
      continue;
    }

890
    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
891

892
    int num_search_results = 0, current = -1;
Marwan Tanager's avatar
Marwan Tanager committed
893
    g_object_get(page_widget, "search-current", &current, "search-length", &num_search_results, NULL);
894
895
896
897
    if (num_search_results == 0 || current == -1) {
      continue;
    }

Marwan Tanager's avatar
Marwan Tanager committed
898
    if (first_time_after_abort == true || (tmp + num_pages) % num_pages != cur_page) {
Marwan Tanager's avatar
Marwan Tanager committed
899
900
901
902
903
      target_page = page;
      target_idx = diff == 1 ? 0 : num_search_results - 1;
      break;
    }

904
905
    if (diff == 1 && current < num_search_results - 1) {
      /* the next result is on the same page */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
906
907
      target_page = page;
      target_idx = current + 1;
908
    } else if (diff == -1 && current > 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
909
910
      target_page = page;
      target_idx = current - 1;
911
912
    } else {
      /* the next result is on a different page */
Moritz Lipp's avatar
Moritz Lipp committed
913
      g_object_set(page_widget, "search-current", -1, NULL);
914
915
916

      for (int npage_id = 1; page_id < num_pages; ++npage_id) {
        int ntmp = cur_page + diff * (page_id + npage_id);
917
        zathura_page_t* npage = zathura_document_get_page(zathura->document, (ntmp + 2*num_pages) % num_pages);
918
        GtkWidget* npage_page_widget = zathura_page_get_widget(zathura, npage);
Moritz Lipp's avatar
Moritz Lipp committed
919
        g_object_get(npage_page_widget, "search-length", &num_search_results, NULL);
920
        if (num_search_results != 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
921
922
          target_page = npage;
          target_idx = diff == 1 ? 0 : num_search_results - 1;
923
924
925
926
          break;
        }
      }
    }
927

928
929
    break;
  }
Moritz Lipp's avatar
Moritz Lipp committed
930

Sebastian Ramacher's avatar
Sebastian Ramacher committed
931
932
  if (target_page != NULL) {
    girara_list_t* results = NULL;
933
    GtkWidget* page_widget = zathura_page_get_widget(zathura, target_page);
Moritz Lipp's avatar
Moritz Lipp committed
934
935
    g_object_set(page_widget, "search-current", target_idx, NULL);
    g_object_get(page_widget, "search-results", &results, NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
936

937
    /* Need to adjust rectangle to page scale and orientation */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
938
939
    zathura_rectangle_t* rect = girara_list_nth(results, target_idx);
    zathura_rectangle_t rectangle = recalc_rectangle(target_page, *rect);
Julian Orth's avatar
Julian Orth committed
940
941
942

    bool search_hadjust = true;
    girara_setting_get(session, "search-hadjust", &search_hadjust);
943
944

    /* compute the position of the center of the page */
Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
945
946
    double pos_x = 0;
    double pos_y = 0;
947
948
949
950
951
    page_number_to_position(zathura->document, zathura_page_get_index(target_page),
                            0.5, 0.5, &pos_x, &pos_y);

    /* correction to center the current result                          */
    /* NOTE: rectangle is in viewport units, already scaled and rotated */
Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
952
953
    unsigned int cell_height = 0;
    unsigned int cell_width = 0;
954
955
    zathura_document_get_cell_size(zathura->document, &cell_height, &cell_width);

Sebastian Ramacher's avatar
CS    
Sebastian Ramacher committed
956
957
    unsigned int doc_height = 0;
    unsigned int doc_width = 0;
958
959
    zathura_document_get_document_size(zathura->document, &doc_height, &doc_width);

960
961
962
963
    /* compute the center of the rectangle, which will be aligned to the center
       of the viewport */
    double center_x = (rectangle.x1 + rectangle.x2) / 2;
    double center_y = (rectangle.y1 + rectangle.y2) / 2;
964

965
    pos_y += (center_y - (double)cell_height/2) / (double)doc_height;
Julian Orth's avatar
Julian Orth committed
966
    if (search_hadjust == true) {
967
      pos_x += (center_x - (double)cell_width/2) / (double)doc_width;
Julian Orth's avatar
Julian Orth committed
968
    }
969

970
971
972
    /* move to position */
    zathura_jumplist_add(zathura);
    position_set(zathura, pos_x, pos_y);
973
    zathura_jumplist_add(zathura);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
974
975
  }

Moritz Lipp's avatar
Moritz Lipp committed
976
  return false;
977
978
}

Moritz Lipp's avatar
Moritz Lipp committed
979
bool
980
sc_navigate_index(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
981
                  girara_event_t* UNUSED(event), unsigned int UNUSED(t))
982
{
Moritz Lipp's avatar
Moritz Lipp committed
983
984
985
986
987
988
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

Moritz Lipp's avatar
Moritz Lipp committed
989
990
991
992
993
994
995
996
  if(zathura->ui.index == NULL) {
    return false;
  }

  GtkTreeView *tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.index))->data;
  GtkTreePath *path;

  gtk_tree_view_get_cursor(tree_view, &path, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
997
  if (path == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
998
999
1000
1001
1002
1003
1004
1005
1006
1007
    return false;
  }

  GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
  GtkTreeIter   iter;
  GtkTreeIter   child_iter;

  gboolean is_valid_path = TRUE;

  switch(argument->n) {
1008
    case TOP:
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1009
      /* go to the first node */
1010
1011
1012
1013
      gtk_tree_path_free(path);
      path = gtk_tree_path_new_first();
      break;
    case BOTTOM:
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1014
      /* go to the last visiible node */
1015
1016
1017
1018
1019
1020
1021
1022
      gtk_tree_path_free(path);
      path = gtk_tree_path_new_from_indices(gtk_tree_model_iter_n_children(model,