1 /* SPDX-License-Identifier: Zlib */
2 
3 #include <girara/session.h>
4 #include <girara/settings.h>
5 #include <girara/datastructures.h>
6 #include <girara/shortcuts.h>
7 #include <girara/utils.h>
8 #include <gtk/gtk.h>
9 #include <glib/gi18n.h>
10 
11 #include "callbacks.h"
12 #include "shortcuts.h"
13 #include "document.h"
14 #include "zathura.h"
15 #include "render.h"
16 #include "utils.h"
17 #include "page.h"
18 #include "print.h"
19 #include "page-widget.h"
20 #include "adjustment.h"
21 #include <math.h>
22 
23 /* Helper function; see sc_display_link and sc_follow. */
24 static bool
draw_links(zathura_t * zathura)25 draw_links(zathura_t* zathura)
26 {
27   /* set pages to draw links */
28   bool show_links = false;
29   unsigned int page_offset = 0;
30   const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
31   for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
32     zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
33     if (page == NULL) {
34       continue;
35     }
36 
37     GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
38     GObject* obj_page_widget = G_OBJECT(page_widget);
39     g_object_set(obj_page_widget, "draw-search-results", FALSE, NULL);
40     if (zathura_page_get_visibility(page) == true) {
41       g_object_set(obj_page_widget, "draw-links", TRUE, NULL);
42 
43       int number_of_links = 0;
44       g_object_get(obj_page_widget, "number-of-links", &number_of_links, NULL);
45       if (number_of_links != 0) {
46         show_links = true;
47       }
48       g_object_set(obj_page_widget, "offset-links", page_offset, NULL);
49       page_offset += number_of_links;
50     } else {
51       g_object_set(obj_page_widget, "draw-links", FALSE, NULL);
52     }
53   }
54   return show_links;
55 }
56 
57 bool
sc_abort(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))58 sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument),
59          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
60 {
61   g_return_val_if_fail(session != NULL, false);
62   g_return_val_if_fail(session->global.data != NULL, false);
63   zathura_t* zathura = session->global.data;
64 
65   bool clear_search = true;
66   girara_setting_get(session, "abort-clear-search", &clear_search);
67 
68   if (zathura->document != NULL) {
69     const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
70     for (unsigned int page_id = 0; page_id < number_of_pages; ++page_id) {
71       zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
72       if (page == NULL) {
73         continue;
74       }
75 
76       GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
77       GObject* obj_page_widget = G_OBJECT(page_widget);
78       g_object_set(obj_page_widget, "draw-links", FALSE, NULL);
79       if (clear_search == true) {
80         g_object_set(obj_page_widget, "draw-search-results", FALSE, NULL);
81       }
82     }
83   }
84 
85   /* Setting the mode back here has not worked for ages. We need another way to
86    * do this. Let's disable this for now.
87    */
88   /* girara_mode_set(session, session->modes.normal); */
89   girara_sc_abort(session, NULL, NULL, 0);
90 
91   return false;
92 }
93 
94 bool
sc_adjust_window(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))95 sc_adjust_window(girara_session_t* session, girara_argument_t* argument,
96                  girara_event_t* UNUSED(event), unsigned int UNUSED(t))
97 {
98   g_return_val_if_fail(session != NULL, false);
99   g_return_val_if_fail(session->global.data != NULL, false);
100   zathura_t* zathura = session->global.data;
101   g_return_val_if_fail(argument != NULL, false);
102 
103   if (argument->n < ZATHURA_ADJUST_NONE || argument->n >= ZATHURA_ADJUST_MODE_NUMBER) {
104     girara_error("Invalid adjust mode: %d", argument->n);
105     girara_notify(session, GIRARA_ERROR, _("Invalid adjust mode: %d"), argument->n);
106   } else {
107     girara_debug("Setting adjust mode to: %d", argument->n);
108 
109     zathura_document_set_adjust_mode(zathura->document, argument->n);
110     adjust_view(zathura);
111   }
112 
113   return false;
114 }
115 
116 bool
sc_change_mode(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))117 sc_change_mode(girara_session_t* session, girara_argument_t* argument,
118                girara_event_t* UNUSED(event), unsigned int UNUSED(t))
119 {
120   g_return_val_if_fail(session != NULL, false);
121 
122   girara_mode_set(session, argument->n);
123 
124   return false;
125 }
126 
127 bool
sc_display_link(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))128 sc_display_link(girara_session_t* session, girara_argument_t* UNUSED(argument),
129                 girara_event_t* UNUSED(event), unsigned int UNUSED(t))
130 {
131   g_return_val_if_fail(session != NULL, false);
132   g_return_val_if_fail(session->global.data != NULL, false);
133   zathura_t* zathura = session->global.data;
134 
135   if (zathura->document == NULL || zathura->ui.session == NULL) {
136     return false;
137   }
138 
139   bool show_links = draw_links(zathura);
140 
141   /* ask for input */
142   if (show_links) {
143     zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
144     girara_dialog(zathura->ui.session, "Display link:", FALSE, NULL,
145         cb_sc_display_link,
146         zathura->ui.session);
147   }
148 
149   return false;
150 }
151 
152 bool
sc_focus_inputbar(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))153 sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
154 {
155   g_return_val_if_fail(session != NULL, false);
156   g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
157   g_return_val_if_fail(session->global.data != NULL, false);
158   zathura_t* zathura = session->global.data;
159   g_return_val_if_fail(argument != NULL, false);
160 
161   zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
162 
163   if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
164     gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
165   }
166 
167   if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
168     gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
169   }
170 
171   gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));
172 
173   if (argument->data != NULL) {
174     gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);
175 
176     /* append filepath */
177     if (argument->n == APPEND_FILEPATH && zathura->document != NULL) {
178       const char* file_path = zathura_document_get_path(zathura->document);
179       if (file_path == NULL) {
180         return false;
181       }
182 
183       char* path = g_path_get_dirname(file_path);
184       char* escaped = girara_escape_string(path);
185       char* tmp  = g_strdup_printf("%s%s/", (char*) argument->data, (g_strcmp0(path, "/") == 0) ? "" : escaped);
186       g_free(path);
187       g_free(escaped);
188 
189       gtk_entry_set_text(session->gtk.inputbar_entry, tmp);
190       g_free(tmp);
191     }
192 
193     GdkAtom* selection = get_selection(zathura);
194 
195     /* we save the X clipboard that will be clear by "grab_focus" */
196     gchar* x_clipboard_text = NULL;
197 
198     if (selection != NULL) {
199       x_clipboard_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(*selection));
200     }
201 
202     gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);
203 
204     if (x_clipboard_text != NULL && selection != NULL) {
205       /* we reset the X clipboard with saved text */
206       gtk_clipboard_set_text(gtk_clipboard_get(*selection), x_clipboard_text, -1);
207       g_free(x_clipboard_text);
208     }
209 
210     g_free(selection);
211   }
212 
213   return true;
214 }
215 
216 bool
sc_follow(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))217 sc_follow(girara_session_t* session, girara_argument_t* UNUSED(argument),
218           girara_event_t* UNUSED(event), unsigned int UNUSED(t))
219 {
220   g_return_val_if_fail(session != NULL, false);
221   g_return_val_if_fail(session->global.data != NULL, false);
222   zathura_t* zathura = session->global.data;
223 
224   if (zathura->document == NULL || zathura->ui.session == NULL) {
225     return false;
226   }
227 
228   bool show_links = draw_links(zathura);
229 
230   /* ask for input */
231   if (show_links == true) {
232     zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
233     girara_dialog(zathura->ui.session, "Follow link:", FALSE, NULL, cb_sc_follow, zathura->ui.session);
234   }
235 
236   return false;
237 }
238 
239 bool
sc_goto(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int t)240 sc_goto(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int t)
241 {
242   g_return_val_if_fail(session != NULL, false);
243   g_return_val_if_fail(session->global.data != NULL, false);
244   zathura_t* zathura = session->global.data;
245   g_return_val_if_fail(argument != NULL, false);
246   g_return_val_if_fail(zathura->document != NULL, false);
247 
248   zathura_jumplist_add(zathura);
249   if (t != 0) {
250     /* add offset */
251     t += zathura_document_get_page_offset(zathura->document);
252 
253     page_set(zathura, t - 1);
254   } else if (argument->n == TOP) {
255     page_set(zathura, 0);
256   } else if (argument->n == BOTTOM) {
257     page_set(zathura, zathura_document_get_number_of_pages(zathura->document) - 1);
258   }
259 
260   zathura_jumplist_add(zathura);
261 
262   return false;
263 }
264 
265 bool
sc_mouse_scroll(girara_session_t * session,girara_argument_t * argument,girara_event_t * event,unsigned int t)266 sc_mouse_scroll(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
267 {
268   g_return_val_if_fail(session != NULL, false);
269   g_return_val_if_fail(session->global.data != NULL, false);
270   zathura_t* zathura = session->global.data;
271   g_return_val_if_fail(argument != NULL, false);
272   g_return_val_if_fail(event != NULL, false);
273 
274   if (zathura->document == NULL) {
275     return false;
276   }
277 
278   GtkAdjustment* x_adj = NULL;
279   GtkAdjustment* y_adj = NULL;
280 
281   switch (event->type) {
282       /* scroll */
283     case GIRARA_EVENT_SCROLL_UP:
284     case GIRARA_EVENT_SCROLL_DOWN:
285     case GIRARA_EVENT_SCROLL_LEFT:
286     case GIRARA_EVENT_SCROLL_RIGHT:
287     case GIRARA_EVENT_SCROLL_BIDIRECTIONAL:
288       return sc_scroll(session, argument, event, t);
289 
290       /* drag */
291     case GIRARA_EVENT_BUTTON_PRESS:
292       zathura->shortcut.mouse.x = event->x;
293       zathura->shortcut.mouse.y = event->y;
294       break;
295     case GIRARA_EVENT_BUTTON_RELEASE:
296       zathura->shortcut.mouse.x = 0;
297       zathura->shortcut.mouse.y = 0;
298       break;
299     case GIRARA_EVENT_MOTION_NOTIFY:
300       x_adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
301       y_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
302 
303       if (x_adj == NULL || y_adj == NULL) {
304         return false;
305       }
306 
307       zathura_adjustment_set_value(x_adj,
308           gtk_adjustment_get_value(x_adj) - (event->x - zathura->shortcut.mouse.x));
309       zathura_adjustment_set_value(y_adj,
310           gtk_adjustment_get_value(y_adj) - (event->y - zathura->shortcut.mouse.y));
311       break;
312 
313       /* unhandled events */
314     default:
315       break;
316   }
317 
318   return false;
319 }
320 
321 bool
sc_mouse_zoom(girara_session_t * session,girara_argument_t * argument,girara_event_t * event,unsigned int t)322 sc_mouse_zoom(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
323 {
324   g_return_val_if_fail(session != NULL, false);
325   g_return_val_if_fail(session->global.data != NULL, false);
326   zathura_t* zathura = session->global.data;
327   g_return_val_if_fail(argument != NULL, false);
328   g_return_val_if_fail(event != NULL, false);
329 
330   if (zathura->document == NULL) {
331     return false;
332   }
333 
334   /* scroll event */
335   switch (event->type) {
336     case GIRARA_EVENT_SCROLL_UP:
337       argument->n = ZOOM_IN;
338       break;
339     case GIRARA_EVENT_SCROLL_DOWN:
340       argument->n = ZOOM_OUT;
341       break;
342     case GIRARA_EVENT_SCROLL_BIDIRECTIONAL:
343       argument->n = ZOOM_SMOOTH;
344       break;
345     default:
346       return false;
347   }
348 
349   return sc_zoom(session, argument, event, t);
350 }
351 
352 bool
sc_navigate(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int t)353 sc_navigate(girara_session_t* session, girara_argument_t* argument,
354             girara_event_t* UNUSED(event), unsigned int t)
355 {
356   g_return_val_if_fail(session != NULL, false);
357   g_return_val_if_fail(session->global.data != NULL, false);
358   zathura_t* zathura = session->global.data;
359   g_return_val_if_fail(argument != NULL, false);
360   g_return_val_if_fail(zathura->document != NULL, false);
361 
362   int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
363   int new_page        = zathura_document_get_current_page_number(zathura->document);
364 
365   bool scroll_wrap = false;
366   girara_setting_get(session, "scroll-wrap", &scroll_wrap);
367 
368   bool columns_per_row_offset = false;
369   girara_setting_get(session, "advance-pages-per-row", &columns_per_row_offset);
370 
371   int offset = 1;
372   if (columns_per_row_offset == true) {
373     girara_setting_get(session, "pages-per-row", &offset);
374   }
375 
376   t = (t == 0) ? (unsigned int) offset : t;
377   if (argument->n == NEXT) {
378     if (scroll_wrap == false) {
379       new_page = new_page + t;
380     } else {
381       new_page = (new_page + t) % number_of_pages;
382     }
383   } else if (argument->n == PREVIOUS) {
384     if (scroll_wrap == false) {
385       new_page = new_page - t;
386     } else {
387       new_page = (new_page + number_of_pages - t) % number_of_pages;
388     }
389   }
390 
391   if (!scroll_wrap) {
392     if (new_page <= 0) {
393       new_page = 0;
394     } else if (new_page >= number_of_pages) {
395       new_page = number_of_pages - 1;
396     }
397   }
398 
399   page_set(zathura, new_page);
400 
401   return false;
402 }
403 
404 bool
sc_print(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))405 sc_print(girara_session_t* session, girara_argument_t* UNUSED(argument),
406          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
407 {
408   g_return_val_if_fail(session != NULL, false);
409   g_return_val_if_fail(session->global.data != NULL, false);
410   zathura_t* zathura = session->global.data;
411 
412   if (zathura->document == NULL) {
413     girara_notify(session, GIRARA_ERROR, _("No document opened."));
414     return false;
415   }
416 
417   print(zathura);
418 
419   return true;
420 }
421 
422 bool
sc_recolor(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))423 sc_recolor(girara_session_t* session, girara_argument_t* UNUSED(argument),
424            girara_event_t* UNUSED(event), unsigned int UNUSED(t))
425 {
426   g_return_val_if_fail(session != NULL, false);
427 
428   bool value = false;
429   girara_setting_get(session, "recolor", &value);
430   value = !value;
431   girara_setting_set(session, "recolor", &value);
432 
433   return false;
434 }
435 
436 bool
sc_reload(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))437 sc_reload(girara_session_t* session, girara_argument_t* UNUSED(argument),
438           girara_event_t* UNUSED(event), unsigned int UNUSED(t))
439 {
440   g_return_val_if_fail(session != NULL, false);
441   g_return_val_if_fail(session->global.data != NULL, false);
442   zathura_t* zathura = session->global.data;
443 
444   if (zathura->file_monitor.monitor == NULL) {
445     return false;
446   }
447 
448   /* close current document */
449   document_close(zathura, true);
450 
451   /* reopen document */
452   document_open(
453     zathura, zathura_filemonitor_get_filepath(zathura->file_monitor.monitor),
454     NULL, zathura->file_monitor.password, ZATHURA_PAGE_NUMBER_UNSPECIFIED);
455 
456   return false;
457 }
458 
459 bool
sc_rotate(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int t)460 sc_rotate(girara_session_t* session, girara_argument_t* argument,
461           girara_event_t* UNUSED(event), unsigned int t)
462 {
463   g_return_val_if_fail(session != NULL, false);
464   g_return_val_if_fail(session->global.data != NULL, false);
465   zathura_t* zathura = session->global.data;
466   g_return_val_if_fail(zathura->document != NULL, false);
467 
468   const unsigned int page_number = zathura_document_get_current_page_number(zathura->document);
469 
470   int angle = 90;
471   if (argument != NULL && argument->n == ROTATE_CCW) {
472     angle = 270;
473   }
474 
475   /* update rotate value */
476   t = (t == 0) ? 1 : t;
477   unsigned int rotation = zathura_document_get_rotation(zathura->document);
478   zathura_document_set_rotation(zathura->document, (rotation + angle * t) % 360);
479 
480   /* update scale */
481   girara_argument_t new_argument = { zathura_document_get_adjust_mode(zathura->document), NULL };
482   sc_adjust_window(zathura->ui.session, &new_argument, NULL, 0);
483 
484   /* render all pages again */
485   render_all(zathura);
486 
487   page_set(zathura, page_number);
488 
489   return false;
490 }
491 
492 bool
sc_scroll(girara_session_t * session,girara_argument_t * argument,girara_event_t * event,unsigned int t)493 sc_scroll(girara_session_t* session, girara_argument_t* argument,
494           girara_event_t* event, unsigned int t)
495 {
496   g_return_val_if_fail(session != NULL, false);
497   g_return_val_if_fail(session->global.data != NULL, false);
498   zathura_t* zathura = session->global.data;
499   g_return_val_if_fail(argument != NULL, false);
500   if (zathura->document == NULL) {
501     return false;
502   }
503 
504   /* if TOP or BOTTOM, go there and we are done */
505   if (argument->n == TOP) {
506     position_set(zathura, -1, 0);
507     return false;
508   } else if (argument->n == BOTTOM) {
509     position_set(zathura, -1, 1.0);
510     return false;
511   }
512 
513   /* Retrieve current page and position */
514   const unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
515   double pos_x = zathura_document_get_position_x(zathura->document);
516   double pos_y = zathura_document_get_position_y(zathura->document);
517 
518   /* If PAGE_TOP or PAGE_BOTTOM, go there and we are done */
519   if (argument->n == PAGE_TOP) {
520     double dontcare = 0.5;
521     page_number_to_position(zathura->document, page_id, dontcare, 0.0, &dontcare, &pos_y);
522     position_set(zathura, pos_x, pos_y);
523     return false;
524   } else if (argument->n == PAGE_BOTTOM) {
525     double dontcare = 0.5;
526     page_number_to_position(zathura->document, page_id, dontcare, 1.0, &dontcare, &pos_y);
527     position_set(zathura, pos_x, pos_y);
528     return false;
529   }
530 
531   if (t == 0) {
532     t = 1;
533   }
534 
535   unsigned int view_width  = 0;
536   unsigned int view_height = 0;
537   zathura_document_get_viewport_size(zathura->document, &view_height, &view_width);
538 
539   unsigned int doc_width  = 0;
540   unsigned int doc_height = 0;
541   zathura_document_get_document_size(zathura->document, &doc_height, &doc_width);
542 
543   float scroll_step = 40;
544   girara_setting_get(session, "scroll-step", &scroll_step);
545   float scroll_hstep = -1;
546   girara_setting_get(session, "scroll-hstep", &scroll_hstep);
547   if (scroll_hstep < 0) {
548     scroll_hstep = scroll_step;
549   }
550   float scroll_full_overlap = 0.0;
551   girara_setting_get(session, "scroll-full-overlap", &scroll_full_overlap);
552   bool scroll_page_aware = false;
553   girara_setting_get(session, "scroll-page-aware", &scroll_page_aware);
554 
555   bool scroll_wrap = false;
556   girara_setting_get(session, "scroll-wrap", &scroll_wrap);
557 
558   /* compute the direction of scrolling */
559   double direction = 1.0;
560   if ((argument->n == LEFT) || (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT) ||
561       (argument->n == UP) || (argument->n == FULL_UP) || (argument->n == HALF_UP)) {
562     direction = -1.0;
563   }
564 
565   const double vstep = (double)view_height / (double)doc_height;
566   const double hstep = (double)view_width / (double)doc_width;
567 
568   /* compute new position */
569   switch (argument->n) {
570     case FULL_UP:
571     case FULL_DOWN:
572       pos_y += direction * (1.0 - scroll_full_overlap) * vstep;
573       break;
574 
575     case FULL_LEFT:
576     case FULL_RIGHT:
577       pos_x += direction * (1.0 - scroll_full_overlap) * hstep;
578       break;
579 
580     case HALF_UP:
581     case HALF_DOWN:
582       pos_y += direction * 0.5 * vstep;
583       break;
584 
585     case HALF_LEFT:
586     case HALF_RIGHT:
587       pos_x += direction * 0.5 * hstep;
588       break;
589 
590     case UP:
591     case DOWN:
592       pos_y += direction * t * scroll_step / (double)doc_height;
593       break;
594 
595     case LEFT:
596     case RIGHT:
597       pos_x += direction * t * scroll_hstep / (double)doc_width;
598       break;
599 
600     case BIDIRECTIONAL: {
601       pos_x += event->x * t * scroll_hstep / (double)doc_width;
602       pos_y += event->y * t * scroll_step / (double)doc_height;
603       break;
604     }
605   }
606 
607   /* handle boundaries */
608   const double end_x = 0.5 * (double)view_width / (double)doc_width;
609   const double end_y = 0.5 * (double)view_height / (double)doc_height;
610 
611   const double new_x = scroll_wrap ? 1.0 - end_x : end_x;
612   const double new_y = scroll_wrap ? 1.0 - end_y : end_y;
613 
614   if (pos_x < end_x) {
615     pos_x = new_x;
616   } else if (pos_x > 1.0 - end_x) {
617     pos_x = 1 - new_x;
618   }
619 
620   if (pos_y < end_y) {
621     pos_y = new_y;
622   } else if (pos_y > 1.0 - end_y) {
623     pos_y = 1 - new_y;
624   }
625 
626   /* snap to the border if we change page */
627   const unsigned int new_page_id = position_to_page_number(zathura->document, pos_x, pos_y);
628   if (scroll_page_aware == true && page_id != new_page_id) {
629     double dummy = 0.0;
630     switch(argument->n) {
631       case FULL_LEFT:
632       case HALF_LEFT:
633         page_number_to_position(zathura->document, new_page_id, 1.0, 0.0, &pos_x, &dummy);
634         break;
635 
636       case FULL_RIGHT:
637       case HALF_RIGHT:
638         page_number_to_position(zathura->document, new_page_id, 0.0, 0.0, &pos_x, &dummy);
639         break;
640 
641       case FULL_UP:
642       case HALF_UP:
643         page_number_to_position(zathura->document, new_page_id, 0.0, 1.0, &dummy, &pos_y);
644         break;
645 
646       case FULL_DOWN:
647       case HALF_DOWN:
648         page_number_to_position(zathura->document, new_page_id, 0.0, 0.0, &dummy, &pos_y);
649         break;
650     }
651   }
652 
653   position_set(zathura, pos_x, pos_y);
654   return false;
655 }
656 
657 
658 bool
sc_jumplist(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))659 sc_jumplist(girara_session_t* session, girara_argument_t* argument,
660             girara_event_t* UNUSED(event), unsigned int UNUSED(t))
661 {
662   g_return_val_if_fail(session != NULL, false);
663   g_return_val_if_fail(session->global.data != NULL, false);
664   zathura_t* zathura = session->global.data;
665   g_return_val_if_fail(argument != NULL, false);
666   g_return_val_if_fail(zathura->document != NULL, false);
667 
668   /* if no jumps in the jumplist */
669   if (zathura->jumplist.size == 0) {
670     return true;
671   }
672 
673   double x = zathura_document_get_position_x(zathura->document);
674   double y = zathura_document_get_position_y(zathura->document);
675 
676   zathura_jump_t* jump = NULL;
677   zathura_jump_t* prev_jump = zathura_jumplist_current(zathura);
678   bool go_to_current = false;
679 
680   if (zathura_jumplist_has_next(zathura) == false || zathura_jumplist_has_previous(zathura) == false) {
681     if (x == prev_jump->x && y == prev_jump->y) {
682       go_to_current = false;
683     } else {
684       go_to_current = true;
685     }
686   }
687 
688   switch(argument->n) {
689     case FORWARD:
690       if (go_to_current == true && zathura_jumplist_has_previous(zathura) == false) {
691         jump = zathura_jumplist_current(zathura);
692       } else {
693         zathura_jumplist_forward(zathura);
694         jump = zathura_jumplist_current(zathura);
695       }
696       break;
697     case BACKWARD:
698       if (go_to_current == true && zathura_jumplist_has_next(zathura) == false) {
699         jump = zathura_jumplist_current(zathura);
700       } else {
701         zathura_jumplist_backward(zathura);
702         jump = zathura_jumplist_current(zathura);
703       }
704       break;
705   }
706 
707   if (jump == prev_jump) {
708     if ((zathura_jumplist_has_previous(zathura) == false && argument->n == BACKWARD) ||
709         (zathura_jumplist_has_next(zathura) == false && argument->n == FORWARD)) {
710       jump = NULL;
711     }
712   }
713 
714   if (jump != NULL) {
715     page_set(zathura, jump->page);
716     position_set(zathura, jump->x, jump->y);
717   }
718 
719   return false;
720 }
721 
722 
723 bool
sc_bisect(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int t)724 sc_bisect(girara_session_t* session, girara_argument_t* argument,
725           girara_event_t* UNUSED(event), unsigned int t)
726 {
727   g_return_val_if_fail(session != NULL, false);
728   g_return_val_if_fail(session->global.data != NULL, false);
729   zathura_t* zathura = session->global.data;
730   g_return_val_if_fail(argument != NULL, false);
731   g_return_val_if_fail(zathura->document != NULL, false);
732 
733   const unsigned int num_pages = zathura_document_get_number_of_pages(zathura->document);
734   const unsigned int cur_page = zathura_document_get_current_page_number(zathura->document);
735 
736   /* process arguments */
737   int direction;
738   if (t > 0 && t <= num_pages) {
739     /* bisect between cur_page and t */
740     t -= 1;
741     if (t == cur_page) {
742       /* nothing to do */
743       return false;
744     }
745     else if (t > cur_page) {
746       zathura->bisect.start = cur_page;
747       zathura->bisect.end = t;
748       direction = BACKWARD;
749     } else {
750       zathura->bisect.start = t;
751       zathura->bisect.end = cur_page;
752       direction = FORWARD;
753     }
754   } else {
755     direction = argument->n;
756 
757     /* setup initial bisect range */
758     zathura_jump_t* jump = zathura_jumplist_current(zathura);
759     if (jump == NULL) {
760       girara_debug("bisecting between first and last page because there are no jumps");
761       zathura->bisect.start = 0;
762       zathura->bisect.end = num_pages - 1;
763     } else if (jump->page != cur_page || jump->page != zathura->bisect.last_jump) {
764       girara_debug("last jump doesn't match up, starting new bisecting");
765       zathura->bisect.start = 0;
766       zathura->bisect.end = num_pages - 1;
767 
768       unsigned int prev_page;
769       if (direction == FORWARD) {
770         prev_page = num_pages - 1;
771       } else {
772         prev_page = 0;
773       }
774 
775       /* check if we have previous jumps */
776       if (zathura_jumplist_has_previous(zathura) == true) {
777         zathura_jumplist_backward(zathura);
778         jump = zathura_jumplist_current(zathura);
779         if (jump != NULL) {
780           prev_page = jump->page;
781         }
782         zathura_jumplist_forward(zathura);
783       }
784 
785       zathura->bisect.start = MIN(prev_page, cur_page);
786       zathura->bisect.end = MAX(prev_page, cur_page);
787       zathura->bisect.last_jump = cur_page;
788     }
789   }
790 
791   girara_debug("bisecting between %d and %d, at %d", zathura->bisect.start, zathura->bisect.end, cur_page);
792   if (zathura->bisect.start == zathura->bisect.end) {
793     /* nothing to do */
794     return false;
795   }
796 
797   unsigned int next_page = cur_page;
798   unsigned int next_start = zathura->bisect.start;
799   unsigned int next_end = zathura->bisect.end;
800 
801   /* here we have next_start <= next_page <= next_end */
802 
803   /* bisect step */
804   switch(direction) {
805     case FORWARD:
806       if (cur_page != zathura->bisect.end) {
807         next_page = (cur_page + zathura->bisect.end) / 2;
808         if (next_page == cur_page) {
809           ++next_page;
810         }
811         next_start = cur_page;
812       }
813       break;
814 
815     case BACKWARD:
816       if (cur_page != zathura->bisect.start) {
817         next_page = (cur_page + zathura->bisect.start) / 2;
818         if (next_page == cur_page) {
819           --next_page;
820         }
821         next_end = cur_page;
822       }
823       break;
824   }
825 
826   if (next_page == cur_page) {
827     /* nothing to do */
828     return false;
829   }
830 
831   girara_debug("bisecting between %d and %d, jumping to %d", zathura->bisect.start, zathura->bisect.end, next_page);
832   zathura->bisect.last_jump = next_page;
833   zathura->bisect.start = next_start;
834   zathura->bisect.end = next_end;
835 
836   zathura_jumplist_add(zathura);
837   page_set(zathura, next_page);
838   zathura_jumplist_add(zathura);
839 
840   return false;
841 }
842 
843 
844 bool
sc_search(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))845 sc_search(girara_session_t* session, girara_argument_t* argument,
846           girara_event_t* UNUSED(event), unsigned int UNUSED(t))
847 {
848   g_return_val_if_fail(session != NULL, false);
849   g_return_val_if_fail(session->global.data != NULL, false);
850   zathura_t* zathura = session->global.data;
851   g_return_val_if_fail(argument != NULL, false);
852   g_return_val_if_fail(zathura->document != NULL, false);
853 
854   const unsigned int num_pages = zathura_document_get_number_of_pages(zathura->document);
855   const unsigned int cur_page  = zathura_document_get_current_page_number(zathura->document);
856   GtkWidget* cur_page_widget   = zathura_page_get_widget(zathura, zathura_document_get_page(zathura->document, cur_page));
857   bool nohlsearch              = false;
858   bool first_time_after_abort  = false;
859 
860   girara_setting_get(session, "nohlsearch", &nohlsearch);
861   if (nohlsearch == false) {
862     gboolean draw = FALSE;
863     g_object_get(G_OBJECT(cur_page_widget), "draw-search-results", &draw, NULL);
864 
865     if (draw == false) {
866       first_time_after_abort = true;
867     }
868 
869     document_draw_search_results(zathura, true);
870   }
871 
872   int diff = argument->n == FORWARD ? 1 : -1;
873   if (zathura->global.search_direction == BACKWARD) {
874     diff = -diff;
875   }
876 
877   zathura_page_t* target_page = NULL;
878   int target_idx = 0;
879 
880   for (unsigned int page_id = 0; page_id < num_pages; ++page_id) {
881     int tmp = cur_page + diff * page_id;
882     zathura_page_t* page = zathura_document_get_page(zathura->document, (tmp + num_pages) % num_pages);
883     if (page == NULL) {
884       continue;
885     }
886 
887     GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
888 
889     int num_search_results = 0, current = -1;
890     g_object_get(G_OBJECT(page_widget), "search-current", &current, "search-length", &num_search_results, NULL);
891     if (num_search_results == 0 || current == -1) {
892       continue;
893     }
894 
895     if (first_time_after_abort == true || (tmp + num_pages) % num_pages != cur_page) {
896       target_page = page;
897       target_idx = diff == 1 ? 0 : num_search_results - 1;
898       break;
899     }
900 
901     if (diff == 1 && current < num_search_results - 1) {
902       /* the next result is on the same page */
903       target_page = page;
904       target_idx = current + 1;
905     } else if (diff == -1 && current > 0) {
906       target_page = page;
907       target_idx = current - 1;
908     } else {
909       /* the next result is on a different page */
910       g_object_set(G_OBJECT(page_widget), "search-current", -1, NULL);
911 
912       for (int npage_id = 1; page_id < num_pages; ++npage_id) {
913         int ntmp = cur_page + diff * (page_id + npage_id);
914         zathura_page_t* npage = zathura_document_get_page(zathura->document, (ntmp + 2*num_pages) % num_pages);
915         GtkWidget* npage_page_widget = zathura_page_get_widget(zathura, npage);
916         g_object_get(G_OBJECT(npage_page_widget), "search-length", &num_search_results, NULL);
917         if (num_search_results != 0) {
918           target_page = npage;
919           target_idx = diff == 1 ? 0 : num_search_results - 1;
920           break;
921         }
922       }
923     }
924 
925     break;
926   }
927 
928   if (target_page != NULL) {
929     girara_list_t* results = NULL;
930     GtkWidget* page_widget = zathura_page_get_widget(zathura, target_page);
931     GObject* obj_page_widget = G_OBJECT(page_widget);
932     g_object_set(obj_page_widget, "search-current", target_idx, NULL);
933     g_object_get(obj_page_widget, "search-results", &results, NULL);
934 
935     /* Need to adjust rectangle to page scale and orientation */
936     zathura_rectangle_t* rect = girara_list_nth(results, target_idx);
937     zathura_rectangle_t rectangle = recalc_rectangle(target_page, *rect);
938 
939     bool search_hadjust = true;
940     girara_setting_get(session, "search-hadjust", &search_hadjust);
941 
942     /* compute the position of the center of the page */
943     double pos_x = 0;
944     double pos_y = 0;
945     page_number_to_position(zathura->document, zathura_page_get_index(target_page),
946                             0.5, 0.5, &pos_x, &pos_y);
947 
948     /* correction to center the current result                          */
949     /* NOTE: rectangle is in viewport units, already scaled and rotated */
950     unsigned int cell_height = 0;
951     unsigned int cell_width = 0;
952     zathura_document_get_cell_size(zathura->document, &cell_height, &cell_width);
953 
954     unsigned int doc_height = 0;
955     unsigned int doc_width = 0;
956     zathura_document_get_document_size(zathura->document, &doc_height, &doc_width);
957 
958     /* compute the center of the rectangle, which will be aligned to the center
959        of the viewport */
960     double center_x = (rectangle.x1 + rectangle.x2) / 2;
961     double center_y = (rectangle.y1 + rectangle.y2) / 2;
962 
963     pos_y += (center_y - (double)cell_height/2) / (double)doc_height;
964     if (search_hadjust == true) {
965       pos_x += (center_x - (double)cell_width/2) / (double)doc_width;
966     }
967 
968     /* move to position */
969     zathura_jumplist_add(zathura);
970     position_set(zathura, pos_x, pos_y);
971     zathura_jumplist_add(zathura);
972   } else if (target_page == NULL && argument->data != NULL) {
973     const char* input = argument->data;
974     girara_notify(session, GIRARA_ERROR, _("Pattern not found: %s"), input);
975   }
976 
977   return false;
978 }
979 
980 bool
sc_navigate_index(girara_session_t * session,girara_argument_t * argument,girara_event_t * UNUSED (event),unsigned int UNUSED (t))981 sc_navigate_index(girara_session_t* session, girara_argument_t* argument,
982                   girara_event_t* UNUSED(event), unsigned int UNUSED(t))
983 {
984   g_return_val_if_fail(session != NULL, false);
985   g_return_val_if_fail(session->global.data != NULL, false);
986   zathura_t* zathura = session->global.data;
987   g_return_val_if_fail(argument != NULL, false);
988   g_return_val_if_fail(zathura->document != NULL, false);
989 
990   if(zathura->ui.index == NULL) {
991     return false;
992   }
993 
994   GtkTreeView *tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.index))->data;
995   GtkTreePath *path;
996 
997   gtk_tree_view_get_cursor(tree_view, &path, NULL);
998   if (path == NULL) {
999     return false;
1000   }
1001 
1002   GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
1003   GtkTreeIter   iter;
1004   GtkTreeIter   child_iter;
1005 
1006   gboolean is_valid_path = TRUE;
1007 
1008   switch(argument->n) {
1009     case TOP:
1010       /* go to the first node */
1011       gtk_tree_path_free(path);
1012       path = gtk_tree_path_new_first();
1013       break;
1014     case BOTTOM:
1015       /* go to the last visiible node */
1016       gtk_tree_path_free(path);
1017       path = gtk_tree_path_new_from_indices(gtk_tree_model_iter_n_children(model, NULL) - 1, -1);
1018       gtk_tree_model_get_iter(model, &iter, path);
1019       while (gtk_tree_model_iter_has_child(model, &iter) == TRUE &&
1020           gtk_tree_view_row_expanded(tree_view, path) == TRUE) {
1021           gtk_tree_path_append_index(path, gtk_tree_model_iter_n_children(model, &iter) - 1);
1022       }
1023       break;
1024     case UP:
1025       if (gtk_tree_path_prev(path) == FALSE) {
1026         /* For some reason gtk_tree_path_up returns TRUE although we're not
1027          * moving anywhere. */
1028         is_valid_path = gtk_tree_path_up(path) && (gtk_tree_path_get_depth(path) > 0);
1029       } else { /* row above */
1030         while (gtk_tree_view_row_expanded(tree_view, path)) {
1031           gtk_tree_model_get_iter(model, &iter, path);
1032           /* select last child */
1033           gtk_tree_model_iter_nth_child(model, &child_iter, &iter,
1034                                         gtk_tree_model_iter_n_children(model, &iter)-1);
1035           gtk_tree_path_free(path);
1036           path = gtk_tree_model_get_path(model, &child_iter);
1037         }
1038       }
1039       break;
1040     case COLLAPSE:
1041       if (gtk_tree_view_collapse_row(tree_view, path) == FALSE
1042           && gtk_tree_path_get_depth(path) > 1) {
1043         gtk_tree_path_up(path);
1044         gtk_tree_view_collapse_row(tree_view, path);
1045       }
1046       break;
1047     case DOWN:
1048       if (gtk_tree_view_row_expanded(tree_view, path) == TRUE) {
1049         gtk_tree_path_down(path);
1050       } else {
1051         do {
1052           gtk_tree_model_get_iter(model, &iter, path);
1053           if (gtk_tree_model_iter_next(model, &iter)) {
1054             gtk_tree_path_free(path);
1055             path = gtk_tree_model_get_path(model, &iter);
1056             break;
1057           }
1058         } while((is_valid_path = (gtk_tree_path_get_depth(path) > 1))
1059                 && gtk_tree_path_up(path));
1060       }
1061       break;
1062     case EXPAND:
1063       if (gtk_tree_view_expand_row(tree_view, path, FALSE)) {
1064         gtk_tree_path_down(path);
1065       }
1066       break;
1067     case EXPAND_ALL:
1068       gtk_tree_view_expand_all(tree_view);
1069       break;
1070     case COLLAPSE_ALL:
1071       gtk_tree_view_collapse_all(tree_view);
1072       gtk_tree_path_free(path);
1073       path = gtk_tree_path_new_first();
1074       gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
1075       break;
1076     case TOGGLE:
1077       gtk_tree_model_get_iter(model, &iter, path);
1078       if (gtk_tree_model_iter_has_child(model, &iter) == TRUE) {
1079         if (gtk_tree_view_row_expanded(tree_view, path) == TRUE) {
1080           gtk_tree_view_collapse_row(tree_view, path);
1081         } else {
1082           gtk_tree_view_expand_row(tree_view, path, FALSE);
1083         }
1084       }
1085       break;
1086     case SELECT:
1087       cb_index_row_activated(tree_view, path, NULL, zathura);
1088       gtk_tree_path_free(path);
1089       return false;
1090   }
1091 
1092   if (is_valid_path == TRUE) {
1093     gtk_tree_view_set_cursor(tree_view, path, NULL, FALSE);
1094   }
1095 
1096   gtk_tree_path_free(path);
1097 
1098   return false;
1099 }
1100 
1101 bool
sc_toggle_index(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))1102 sc_toggle_index(girara_session_t* session, girara_argument_t* UNUSED(argument),
1103                 girara_event_t* UNUSED(event), unsigned int UNUSED(t))
1104 {
1105   g_return_val_if_fail(session != NULL, false);
1106   g_return_val_if_fail(session->global.data != NULL, false);
1107   zathura_t* zathura = session->global.data;
1108   if (zathura->document == NULL) {
1109     return false;
1110   }
1111 
1112   girara_tree_node_t* document_index = NULL;
1113   GtkWidget* treeview                = NULL;
1114   GtkTreeModel* model                = NULL;
1115   GtkCellRenderer* renderer          = NULL;
1116   GtkCellRenderer* renderer2         = NULL;
1117 
1118   if (zathura->ui.index == NULL) {
1119     /* create new index widget */
1120     zathura->ui.index = gtk_scrolled_window_new(NULL, NULL);
1121 
1122     if (zathura->ui.index == NULL) {
1123       goto error_ret;
1124     }
1125 
1126     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(zathura->ui.index),
1127                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1128 
1129     /* create index */
1130     document_index = zathura_document_index_generate(zathura->document, NULL);
1131     if (document_index == NULL) {
1132       girara_notify(session, GIRARA_WARNING, _("This document does not contain any index"));
1133       goto error_free;
1134     }
1135 
1136     model = GTK_TREE_MODEL(gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER));
1137     if (model == NULL) {
1138       goto error_free;
1139     }
1140 
1141     treeview = gtk_tree_view_new_with_model(model);
1142     if (treeview == NULL) {
1143       goto error_free;
1144     }
1145 
1146     gtk_style_context_add_class(gtk_widget_get_style_context(treeview),
1147         "indexmode");
1148 
1149     g_object_unref(model);
1150 
1151     renderer = gtk_cell_renderer_text_new();
1152     if (renderer == NULL) {
1153       goto error_free;
1154     }
1155 
1156     renderer2 = gtk_cell_renderer_text_new();
1157     if (renderer2 == NULL) {
1158       goto error_free;
1159     }
1160 
1161     document_index_build(model, NULL, document_index);
1162     girara_node_free(document_index);
1163 
1164     /* setup widget */
1165     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (treeview), 0, "Title", renderer, "markup", 0, NULL);
1166     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (treeview), 1, "Target", renderer2, "text", 1, NULL);
1167 
1168     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
1169     g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1170     g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0)), "expand", TRUE, NULL);
1171     gtk_tree_view_column_set_alignment(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1), 1.0f);
1172     gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), gtk_tree_path_new_first(), NULL, FALSE);
1173     g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(cb_index_row_activated), zathura);
1174 
1175     gtk_container_add(GTK_CONTAINER(zathura->ui.index), treeview);
1176     gtk_widget_show(treeview);
1177   }
1178 
1179   if (gtk_widget_get_visible(GTK_WIDGET(zathura->ui.index))) {
1180     girara_set_view(session, zathura->ui.page_widget);
1181     gtk_widget_hide(GTK_WIDGET(zathura->ui.index));
1182     girara_mode_set(zathura->ui.session, zathura->modes.normal);
1183 
1184     /* refresh view */
1185     refresh_view(zathura);
1186   } else {
1187     /* save current position to the jumplist */
1188     zathura_jumplist_add(zathura);
1189 
1190     girara_set_view(session, zathura->ui.index);
1191     gtk_widget_show(GTK_WIDGET(zathura->ui.index));
1192     girara_mode_set(zathura->ui.session, zathura->modes.index);
1193   }
1194 
1195   return false;
1196 
1197 error_free:
1198 
1199   if (zathura->ui.index != NULL) {
1200     g_object_ref_sink(zathura->ui.index);
1201     zathura->ui.index = NULL;
1202   }
1203 
1204   if (document_index != NULL) {
1205     girara_node_free(document_index);
1206   }
1207 
1208 error_ret:
1209 
1210   return false;
1211 }
1212 
1213 bool
sc_toggle_page_mode(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))1214 sc_toggle_page_mode(girara_session_t* session, girara_argument_t*
1215                     UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
1216 {
1217   g_return_val_if_fail(session != NULL, false);
1218   g_return_val_if_fail(session->global.data != NULL, false);
1219   zathura_t* zathura = session->global.data;
1220 
1221   if (zathura->document == NULL) {
1222     girara_notify(session, GIRARA_WARNING, _("No document opened."));
1223     return false;
1224   }
1225 
1226   unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
1227 
1228   int pages_per_row = 1;
1229   girara_setting_get(zathura->ui.session, "pages-per-row", &pages_per_row);
1230 
1231   int value = 1;
1232   if (pages_per_row == 1) {
1233     value = zathura->shortcut.toggle_page_mode.pages;
1234   } else {
1235     zathura->shortcut.toggle_page_mode.pages = pages_per_row;
1236   }
1237 
1238   girara_setting_set(zathura->ui.session, "pages-per-row", &value);
1239   adjust_view(zathura);
1240 
1241   page_set(zathura, page_id);
1242   render_all(zathura);
1243   refresh_view(zathura);
1244 
1245   return true;
1246 }
1247 
1248 bool
sc_toggle_fullscreen(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))1249 sc_toggle_fullscreen(girara_session_t* session, girara_argument_t*
1250                      UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
1251 {
1252   g_return_val_if_fail(session != NULL, false);
1253   g_return_val_if_fail(session->global.data != NULL, false);
1254   zathura_t* zathura = session->global.data;
1255 
1256   if (zathura->document == NULL) {
1257     girara_notify(session, GIRARA_WARNING, _("No document opened."));
1258     return false;
1259   }
1260 
1261   const girara_mode_t old_mode = girara_mode_get(session);
1262   if (old_mode == zathura->modes.fullscreen) {
1263     gtk_window_unfullscreen(GTK_WINDOW(session->gtk.window));
1264     refresh_view(zathura);
1265     girara_mode_set(session, zathura->modes.normal);
1266   } else if (old_mode == zathura->modes.normal) {
1267     gtk_window_fullscreen(GTK_WINDOW(session->gtk.window));
1268     refresh_view(zathura);
1269     girara_mode_set(session, zathura->modes.fullscreen);
1270   }
1271 
1272   return false;
1273 }
1274 
1275 bool
sc_toggle_presentation(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))1276 sc_toggle_presentation(girara_session_t* session, girara_argument_t*
1277                      UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
1278 {
1279   g_return_val_if_fail(session != NULL, false);
1280   g_return_val_if_fail(session->global.data != NULL, false);
1281   zathura_t* zathura = session->global.data;
1282 
1283   if (zathura->document == NULL) {
1284     girara_notify(session, GIRARA_WARNING, _("No document opened."));
1285     return false;
1286   }
1287 
1288   const girara_mode_t old_mode = girara_mode_get(session);
1289   if (old_mode == zathura->modes.presentation) {
1290     /* reset pages per row */
1291     girara_setting_set(session, "pages-per-row", &zathura->shortcut.toggle_presentation_mode.pages);
1292 
1293     /* reset first page column */
1294     if (zathura->shortcut.toggle_presentation_mode.first_page_column_list != NULL) {
1295       girara_setting_set(session, "first-page-column", zathura->shortcut.toggle_presentation_mode.first_page_column_list);
1296     }
1297 
1298     /* show status bar */
1299     gtk_widget_show(GTK_WIDGET(session->gtk.statusbar));
1300 
1301     /* set full screen */
1302     gtk_window_unfullscreen(GTK_WINDOW(session->gtk.window));
1303 
1304     /* reset zoom */
1305     zathura_document_set_zoom(zathura->document, zathura->shortcut.toggle_presentation_mode.zoom);
1306     render_all(zathura);
1307     refresh_view(zathura);
1308 
1309     /* set mode */
1310     girara_mode_set(session, zathura->modes.normal);
1311   } else if (old_mode == zathura->modes.normal) {
1312     /* backup pages per row */
1313     girara_setting_get(session, "pages-per-row", &zathura->shortcut.toggle_presentation_mode.pages);
1314 
1315     /* backup first page column */
1316     g_free(zathura->shortcut.toggle_presentation_mode.first_page_column_list);
1317     zathura->shortcut.toggle_presentation_mode.first_page_column_list = NULL;
1318     /* this will leak. we need to move the values somewhere else */
1319     girara_setting_get(session, "first-page-column", &zathura->shortcut.toggle_presentation_mode.first_page_column_list);
1320 
1321     /* set single view */
1322     int int_value = 1;
1323     girara_setting_set(session, "pages-per-row", &int_value);
1324 
1325     /* back up zoom */
1326     zathura->shortcut.toggle_presentation_mode.zoom = zathura_document_get_zoom(zathura->document);
1327 
1328     /* adjust window */
1329     girara_argument_t argument = { ZATHURA_ADJUST_BESTFIT, NULL };
1330     sc_adjust_window(session, &argument, NULL, 0);
1331 
1332     /* hide status and inputbar */
1333     gtk_widget_hide(GTK_WIDGET(session->gtk.inputbar));
1334     gtk_widget_hide(GTK_WIDGET(session->gtk.statusbar));
1335 
1336     /* set full screen */
1337     gtk_window_fullscreen(GTK_WINDOW(session->gtk.window));
1338     refresh_view(zathura);
1339 
1340     /* set mode */
1341     girara_mode_set(session, zathura->modes.presentation);
1342   }
1343 
1344   return false;
1345 }
1346 
1347 bool
sc_quit(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))1348 sc_quit(girara_session_t* session, girara_argument_t* UNUSED(argument),
1349         girara_event_t* UNUSED(event), unsigned int UNUSED(t))
1350 {
1351   g_return_val_if_fail(session != NULL, false);
1352 
1353   girara_argument_t arg = { GIRARA_HIDE, NULL };
1354   girara_isc_completion(session, &arg, NULL, 0);
1355 
1356   cb_destroy(NULL, NULL);
1357 
1358   return false;
1359 }
1360 
1361 bool
sc_zoom(girara_session_t * session,girara_argument_t * argument,girara_event_t * event,unsigned int t)1362 sc_zoom(girara_session_t* session, girara_argument_t* argument, girara_event_t*
1363         event, unsigned int t)
1364 {
1365   g_return_val_if_fail(session != NULL, false);
1366   g_return_val_if_fail(session->global.data != NULL, false);
1367   zathura_t* zathura = session->global.data;
1368   g_return_val_if_fail(argument != NULL, false);
1369   g_return_val_if_fail(zathura->document != NULL, false);
1370 
1371   zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_NONE);
1372 
1373   /* retrieve zoom step value */
1374   int value = 1;
1375   girara_setting_get(zathura->ui.session, "zoom-step", &value);
1376 
1377   const int nt = (t == 0) ? 1 : t;
1378   const double zoom_step = 1.0 + value / 100.0 * nt;
1379   const double old_zoom = zathura_document_get_zoom(zathura->document);
1380 
1381   /* specify new zoom value */
1382   if (argument->n == ZOOM_IN) {
1383     girara_debug("Increasing zoom by %0.2f.", zoom_step - 1.0);
1384     zathura_document_set_zoom(zathura->document, old_zoom * zoom_step);
1385   } else if (argument->n == ZOOM_OUT) {
1386     girara_debug("Decreasing zoom by %0.2f.", zoom_step - 1.0);
1387     zathura_document_set_zoom(zathura->document, old_zoom / zoom_step);
1388   } else if (argument->n == ZOOM_SPECIFIC) {
1389     if (t == 0) {
1390       girara_debug("Setting zoom to 1.");
1391       zathura_document_set_zoom(zathura->document, 1.0);
1392     } else {
1393       girara_debug("Setting zoom to %0.2f.", t / 100.0);
1394       zathura_document_set_zoom(zathura->document, t / 100.0);
1395     }
1396   } else if (argument->n == ZOOM_SMOOTH) {
1397     const double dy = (event != NULL) ? event->y : 1.0;
1398     const double z = pow(zoom_step, -dy);
1399     girara_debug("Increasing zoom by %0.2f.",  z - 1.0);
1400     zathura_document_set_zoom(zathura->document, old_zoom * z);
1401   } else {
1402     girara_debug("Setting zoom to 1.");
1403     zathura_document_set_zoom(zathura->document, 1.0);
1404   }
1405 
1406   /* zoom limitations */
1407   const double zoom = zathura_document_get_zoom(zathura->document);
1408   zathura_document_set_zoom(zathura->document, zathura_correct_zoom_value(session, zoom));
1409 
1410   const double new_zoom = zathura_document_get_zoom(zathura->document);
1411   if (fabs(new_zoom - old_zoom) <= DBL_EPSILON) {
1412     girara_debug("New and old zoom level are too close: %0.2f vs. %0.2f", new_zoom, old_zoom);
1413     return false;
1414   }
1415 
1416   girara_debug("Re-rendering with new zoom level %0.2f.", new_zoom);
1417   render_all(zathura);
1418   refresh_view(zathura);
1419 
1420   return false;
1421 }
1422 
1423 bool
sc_exec(girara_session_t * session,girara_argument_t * argument,girara_event_t * event,unsigned int t)1424 sc_exec(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
1425 {
1426   g_return_val_if_fail(session != NULL, false);
1427   g_return_val_if_fail(session->global.data != NULL, false);
1428   zathura_t* zathura = session->global.data;
1429 
1430   if (argument == NULL || argument->data == NULL) {
1431     return false;
1432   }
1433 
1434   if (zathura->document != NULL) {
1435     const char* path = zathura_document_get_path(zathura->document);
1436 
1437     girara_argument_t new_argument = *argument;
1438 
1439     char* r = girara_replace_substring(argument->data, "$FILE", path);
1440     if (r == NULL) {
1441       return false;
1442     }
1443 
1444     char* s = girara_replace_substring(r, "%", path);
1445     g_free(r);
1446 
1447     if (s == NULL) {
1448       return false;
1449     }
1450 
1451     new_argument.data = s;
1452     const bool ret = girara_sc_exec(session, &new_argument, event, t);
1453     g_free(s);
1454     return ret;
1455   }
1456 
1457   return girara_sc_exec(session, argument, event, t);
1458 }
1459 
1460 bool
sc_nohlsearch(girara_session_t * session,girara_argument_t * UNUSED (argument),girara_event_t * UNUSED (event),unsigned int UNUSED (t))1461 sc_nohlsearch(girara_session_t* session, girara_argument_t* UNUSED(argument), girara_event_t* UNUSED(event), unsigned int UNUSED(t))
1462 {
1463   g_return_val_if_fail(session != NULL, false);
1464   g_return_val_if_fail(session->global.data != NULL, false);
1465   zathura_t* zathura = session->global.data;
1466 
1467   document_draw_search_results(zathura, false);
1468   render_all(zathura);
1469 
1470   return false;
1471 }
1472