1 /* SPDX-License-Identifier: Zlib */
2 
3 #include <girara/statusbar.h>
4 #include <girara/session.h>
5 #include <girara/settings.h>
6 #include <girara/utils.h>
7 #include <stdlib.h>
8 #include <gtk/gtk.h>
9 #include <string.h>
10 #include <glib/gi18n.h>
11 #include <math.h>
12 
13 #include "callbacks.h"
14 #include "links.h"
15 #include "zathura.h"
16 #include "render.h"
17 #include "document.h"
18 #include "utils.h"
19 #include "shortcuts.h"
20 #include "page-widget.h"
21 #include "page.h"
22 #include "adjustment.h"
23 #include "synctex.h"
24 #include "dbus-interface.h"
25 
26 gboolean
cb_destroy(GtkWidget * UNUSED (widget),zathura_t * zathura)27 cb_destroy(GtkWidget* UNUSED(widget), zathura_t* zathura)
28 {
29   if (zathura != NULL && zathura->document != NULL) {
30     document_close(zathura, false);
31   }
32 
33   gtk_main_quit();
34   return TRUE;
35 }
36 
37 void
cb_buffer_changed(girara_session_t * session)38 cb_buffer_changed(girara_session_t* session)
39 {
40   g_return_if_fail(session != NULL);
41   g_return_if_fail(session->global.data != NULL);
42 
43   zathura_t* zathura = session->global.data;
44 
45   char* buffer = girara_buffer_get(session);
46   if (buffer != NULL) {
47     girara_statusbar_item_set_text(session, zathura->ui.statusbar.buffer, buffer);
48     free(buffer);
49   } else {
50     girara_statusbar_item_set_text(session, zathura->ui.statusbar.buffer, "");
51   }
52 }
53 
54 void
update_visible_pages(zathura_t * zathura)55 update_visible_pages(zathura_t* zathura)
56 {
57   const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
58 
59   for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
60     zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
61     GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
62     ZathuraPage* zathura_page_widget = ZATHURA_PAGE(page_widget);
63 
64     if (page_is_visible(zathura->document, page_id) == true) {
65       /* make page visible */
66       if (zathura_page_get_visibility(page) == false) {
67         zathura_page_set_visibility(page, true);
68         zathura_page_widget_update_view_time(zathura_page_widget);
69         zathura_renderer_page_cache_add(zathura->sync.render_thread, zathura_page_get_index(page));
70       }
71 
72     } else {
73       /* make page invisible */
74       if (zathura_page_get_visibility(page) == true) {
75         zathura_page_set_visibility(page, false);
76         /* If a page becomes invisible, abort the render request. */
77         zathura_page_widget_abort_render_request(zathura_page_widget);
78       }
79 
80       /* reset current search result */
81       girara_list_t* results = NULL;
82       GObject* obj_page_widget = G_OBJECT(page_widget);
83       g_object_get(obj_page_widget, "search-results", &results, NULL);
84       if (results != NULL) {
85         g_object_set(obj_page_widget, "search-current", 0, NULL);
86       }
87     }
88   }
89 }
90 
91 void
cb_view_hadjustment_value_changed(GtkAdjustment * adjustment,gpointer data)92 cb_view_hadjustment_value_changed(GtkAdjustment* adjustment, gpointer data)
93 {
94   zathura_t* zathura = data;
95   if (zathura == NULL || zathura->document == NULL) {
96     return;
97   }
98 
99   /* Do nothing in index mode */
100   if (girara_mode_get(zathura->ui.session) == zathura->modes.index) {
101     return;
102   }
103 
104   update_visible_pages(zathura);
105 
106   double position_x = zathura_adjustment_get_ratio(adjustment);
107   double position_y = zathura_document_get_position_y(zathura->document);
108   unsigned int page_id = position_to_page_number(zathura->document, position_x, position_y);
109 
110   zathura_document_set_position_x(zathura->document, position_x);
111   zathura_document_set_position_y(zathura->document, position_y);
112   zathura_document_set_current_page_number(zathura->document, page_id);
113 
114   statusbar_page_number_update(zathura);
115 }
116 
117 void
cb_view_vadjustment_value_changed(GtkAdjustment * adjustment,gpointer data)118 cb_view_vadjustment_value_changed(GtkAdjustment* adjustment, gpointer data)
119 {
120   zathura_t* zathura = data;
121   if (zathura == NULL || zathura->document == NULL) {
122     return;
123   }
124 
125   /* Do nothing in index mode */
126   if (girara_mode_get(zathura->ui.session) == zathura->modes.index) {
127     return;
128   }
129 
130   update_visible_pages(zathura);
131 
132   double position_x = zathura_document_get_position_x(zathura->document);
133   double position_y = zathura_adjustment_get_ratio(adjustment);
134   unsigned int page_id = position_to_page_number(zathura->document, position_x, position_y);
135 
136   zathura_document_set_position_x(zathura->document, position_x);
137   zathura_document_set_position_y(zathura->document, position_y);
138   zathura_document_set_current_page_number(zathura->document, page_id);
139 
140   statusbar_page_number_update(zathura);
141 }
142 
143 static void
cb_view_adjustment_changed(GtkAdjustment * adjustment,zathura_t * zathura,bool width)144 cb_view_adjustment_changed(GtkAdjustment* adjustment, zathura_t* zathura,
145     bool width)
146 {
147   /* Do nothing in index mode */
148   if (girara_mode_get(zathura->ui.session) == zathura->modes.index) {
149     return;
150   }
151 
152   const zathura_adjust_mode_t adjust_mode =
153     zathura_document_get_adjust_mode(zathura->document);
154 
155   /* Don't scroll, we're focusing the inputbar. */
156   if (adjust_mode == ZATHURA_ADJUST_INPUTBAR) {
157     return;
158   }
159 
160   /* Save the viewport size */
161   unsigned int size = (unsigned int)floor(gtk_adjustment_get_page_size(adjustment));
162   if (width == true) {
163     zathura_document_set_viewport_width(zathura->document, size);
164   } else {
165     zathura_document_set_viewport_height(zathura->document, size);
166   }
167 
168   /* reset the adjustment, in case bounds have changed */
169   const double ratio = width == true ?
170     zathura_document_get_position_x(zathura->document) :
171     zathura_document_get_position_y(zathura->document);
172   zathura_adjustment_set_value_from_ratio(adjustment, ratio);
173 }
174 
175 void
cb_view_hadjustment_changed(GtkAdjustment * adjustment,gpointer data)176 cb_view_hadjustment_changed(GtkAdjustment* adjustment, gpointer data)
177 {
178   zathura_t* zathura = data;
179   g_return_if_fail(zathura != NULL);
180 
181   cb_view_adjustment_changed(adjustment, zathura, true);
182 }
183 
184 void
cb_view_vadjustment_changed(GtkAdjustment * adjustment,gpointer data)185 cb_view_vadjustment_changed(GtkAdjustment* adjustment, gpointer data)
186 {
187   zathura_t* zathura = data;
188   g_return_if_fail(zathura != NULL);
189 
190   cb_view_adjustment_changed(adjustment, zathura, false);
191 }
192 
193 void
cb_refresh_view(GtkWidget * GIRARA_UNUSED (view),gpointer data)194 cb_refresh_view(GtkWidget* GIRARA_UNUSED(view), gpointer data)
195 {
196   zathura_t* zathura = data;
197   if (zathura == NULL || zathura->document == NULL) {
198     return;
199   }
200 
201   unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
202   zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
203   if (page == NULL) {
204     return;
205   }
206 
207   GtkAdjustment* vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
208   GtkAdjustment* hadj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(zathura->ui.session->gtk.view));
209 
210   double position_x = zathura_document_get_position_x(zathura->document);
211   double position_y = zathura_document_get_position_y(zathura->document);
212 
213   zathura_adjustment_set_value_from_ratio(vadj, position_y);
214   zathura_adjustment_set_value_from_ratio(hadj, position_x);
215 
216   statusbar_page_number_update(zathura);
217 }
218 
219 void
cb_monitors_changed(GdkScreen * screen,gpointer data)220 cb_monitors_changed(GdkScreen* screen, gpointer data)
221 {
222   girara_debug("signal received");
223 
224   zathura_t* zathura = data;
225   if (screen == NULL || zathura == NULL) {
226     return;
227   }
228 
229   zathura_update_view_ppi(zathura);
230 }
231 
232 void
cb_widget_screen_changed(GtkWidget * widget,GdkScreen * previous_screen,gpointer data)233 cb_widget_screen_changed(GtkWidget* widget, GdkScreen* previous_screen, gpointer data)
234 {
235   girara_debug("called");
236 
237   zathura_t* zathura = data;
238   if (widget == NULL || zathura == NULL) {
239     return;
240   }
241 
242   /* disconnect previous screen handler if present */
243   if (previous_screen != NULL && zathura->signals.monitors_changed_handler > 0) {
244     g_signal_handler_disconnect(previous_screen, zathura->signals.monitors_changed_handler);
245     zathura->signals.monitors_changed_handler = 0;
246   }
247 
248   if (gtk_widget_has_screen(widget)) {
249     GdkScreen* screen = gtk_widget_get_screen(widget);
250 
251     /* connect to new screen */
252     zathura->signals.monitors_changed_handler = g_signal_connect(G_OBJECT(screen),
253         "monitors-changed", G_CALLBACK(cb_monitors_changed), zathura);
254   }
255 
256   zathura_update_view_ppi(zathura);
257 }
258 
259 gboolean
cb_widget_configured(GtkWidget * UNUSED (widget),GdkEvent * UNUSED (event),gpointer data)260 cb_widget_configured(GtkWidget* UNUSED(widget), GdkEvent* UNUSED(event), gpointer data)
261 {
262   zathura_t* zathura = data;
263   if (zathura == NULL) {
264     return false;
265   }
266 
267   zathura_update_view_ppi(zathura);
268 
269   return false;
270 }
271 
272 void
cb_scale_factor(GObject * object,GParamSpec * UNUSED (pspec),gpointer data)273 cb_scale_factor(GObject* object, GParamSpec* UNUSED(pspec), gpointer data)
274 {
275   zathura_t* zathura = data;
276   if (zathura == NULL || zathura->document == NULL) {
277     return;
278   }
279 
280   int new_factor = gtk_widget_get_scale_factor(GTK_WIDGET(object));
281   if (new_factor == 0) {
282     girara_debug("Ignoring new device scale factor = 0");
283     return;
284   }
285 
286   zathura_device_factors_t current = zathura_document_get_device_factors(zathura->document);
287   if (fabs(new_factor - current.x) >= DBL_EPSILON ||
288       fabs(new_factor - current.y) >= DBL_EPSILON) {
289     zathura_document_set_device_factors(zathura->document, new_factor, new_factor);
290     girara_debug("New device scale factor: %d", new_factor);
291     zathura_update_view_ppi(zathura);
292     render_all(zathura);
293   }
294 }
295 
296 void
cb_page_layout_value_changed(girara_session_t * session,const char * name,girara_setting_type_t UNUSED (type),const void * value,void * UNUSED (data))297 cb_page_layout_value_changed(girara_session_t* session, const char* name, girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data))
298 {
299   g_return_if_fail(value != NULL);
300   g_return_if_fail(session != NULL);
301   g_return_if_fail(session->global.data != NULL);
302   zathura_t* zathura = session->global.data;
303 
304   /* pages-per-row must not be 0 */
305   if (g_strcmp0(name, "pages-per-row") == 0) {
306     unsigned int pages_per_row = *((const unsigned int*) value);
307     if (pages_per_row == 0) {
308       pages_per_row = 1;
309       girara_setting_set(session, name, &pages_per_row);
310       girara_notify(session, GIRARA_WARNING, _("'%s' must not be 0. Set to 1."), name);
311       return;
312     }
313   }
314 
315   if (zathura->document == NULL) {
316     /* no document has been openend yet */
317     return;
318   }
319 
320   unsigned int pages_per_row = 1;
321   girara_setting_get(session, "pages-per-row", &pages_per_row);
322 
323   /* get list of first_page_column settings */
324   char* first_page_column_list = NULL;
325   girara_setting_get(session, "first-page-column", &first_page_column_list);
326 
327   /* find value for first_page_column */
328   unsigned int first_page_column = find_first_page_column(first_page_column_list, pages_per_row);
329   g_free(first_page_column_list);
330 
331   unsigned int page_padding = 1;
332   girara_setting_get(zathura->ui.session, "page-padding", &page_padding);
333 
334   bool page_right_to_left = false;
335   girara_setting_get(zathura->ui.session, "page-right-to-left", &page_right_to_left);
336 
337   page_widget_set_mode(zathura, page_padding, pages_per_row, first_page_column, page_right_to_left);
338   zathura_document_set_page_layout(zathura->document, page_padding, pages_per_row, first_page_column);
339 }
340 
341 void
cb_index_row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * UNUSED (column),void * data)342 cb_index_row_activated(GtkTreeView* tree_view, GtkTreePath* path,
343                        GtkTreeViewColumn* UNUSED(column), void* data)
344 {
345   zathura_t* zathura = data;
346   if (tree_view == NULL || zathura == NULL || zathura->ui.session == NULL) {
347     return;
348   }
349 
350   GtkTreeModel  *model;
351   GtkTreeIter   iter;
352 
353   g_object_get(G_OBJECT(tree_view), "model", &model, NULL);
354 
355   if(gtk_tree_model_get_iter(model, &iter, path)) {
356     zathura_index_element_t* index_element;
357     gtk_tree_model_get(model, &iter, 2, &index_element, -1);
358 
359     if (index_element == NULL) {
360       return;
361     }
362 
363     sc_toggle_index(zathura->ui.session, NULL, NULL, 0);
364     zathura_link_evaluate(zathura, index_element->link);
365   }
366 
367   g_object_unref(model);
368 }
369 
370 typedef enum zathura_link_action_e
371 {
372   ZATHURA_LINK_ACTION_FOLLOW,
373   ZATHURA_LINK_ACTION_DISPLAY
374 } zathura_link_action_t;
375 
376 static bool
handle_link(GtkEntry * entry,girara_session_t * session,zathura_link_action_t action)377 handle_link(GtkEntry* entry, girara_session_t* session,
378             zathura_link_action_t action)
379 {
380   g_return_val_if_fail(session != NULL, FALSE);
381   g_return_val_if_fail(session->global.data != NULL, FALSE);
382 
383   zathura_t* zathura = session->global.data;
384   bool eval = true;
385 
386   char* input = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
387   if (input == NULL || strlen(input) == 0) {
388     eval = false;
389   }
390 
391   int index = 0;
392   if (eval == true) {
393     index = atoi(input);
394     if (index == 0 && g_strcmp0(input, "0") != 0) {
395       girara_notify(session, GIRARA_WARNING, _("Invalid input '%s' given."), input);
396       eval = false;
397     }
398     index = index - 1;
399   }
400 
401   /* set pages to draw links */
402   bool invalid_index = true;
403   unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
404   for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
405     zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
406     if (page == NULL || zathura_page_get_visibility(page) == false) {
407       continue;
408     }
409 
410     GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
411     g_object_set(G_OBJECT(page_widget), "draw-links", FALSE, NULL);
412 
413     if (eval == true) {
414       zathura_link_t* link = zathura_page_widget_link_get(ZATHURA_PAGE(page_widget), index);
415 
416       if (link != NULL) {
417         invalid_index = false;
418         switch (action) {
419           case ZATHURA_LINK_ACTION_FOLLOW:
420             zathura_link_evaluate(zathura, link);
421             break;
422           case ZATHURA_LINK_ACTION_DISPLAY:
423             zathura_link_display(zathura, link);
424             break;
425         }
426       }
427     }
428   }
429 
430   if (eval == true && invalid_index == true) {
431     girara_notify(session, GIRARA_WARNING, _("Invalid index '%s' given."), input);
432   }
433 
434   g_free(input);
435 
436   return (eval == TRUE) ? TRUE : FALSE;
437 }
438 
439 gboolean
cb_sc_follow(GtkEntry * entry,void * data)440 cb_sc_follow(GtkEntry* entry, void* data)
441 {
442   girara_session_t* session = data;
443   return handle_link(entry, session, ZATHURA_LINK_ACTION_FOLLOW);
444 }
445 
446 gboolean
cb_sc_display_link(GtkEntry * entry,void * data)447 cb_sc_display_link(GtkEntry* entry, void* data)
448 {
449   girara_session_t* session = data;
450   return handle_link(entry, session, ZATHURA_LINK_ACTION_DISPLAY);
451 }
452 
453 static gboolean
file_monitor_reload(void * data)454 file_monitor_reload(void* data)
455 {
456   sc_reload((girara_session_t*) data, NULL, NULL, 0);
457   return FALSE;
458 }
459 
460 void
cb_file_monitor(ZathuraFileMonitor * monitor,girara_session_t * session)461 cb_file_monitor(ZathuraFileMonitor* monitor, girara_session_t* session)
462 {
463   g_return_if_fail(monitor  != NULL);
464   g_return_if_fail(session  != NULL);
465 
466   g_main_context_invoke(NULL, file_monitor_reload, session);
467 }
468 
469 static gboolean
password_dialog(gpointer data)470 password_dialog(gpointer data)
471 {
472   zathura_password_dialog_info_t* dialog = data;
473 
474   if (dialog != NULL) {
475     girara_dialog(
476       dialog->zathura->ui.session,
477       "Incorrect password. Enter password:",
478       true,
479       NULL,
480       cb_password_dialog,
481       dialog
482     );
483   }
484 
485   return FALSE;
486 }
487 
488 gboolean
cb_password_dialog(GtkEntry * entry,void * data)489 cb_password_dialog(GtkEntry* entry, void* data)
490 {
491   if (entry == NULL || data == NULL) {
492     goto error_ret;
493   }
494 
495   zathura_password_dialog_info_t* dialog = data;
496 
497   if (dialog->path == NULL) {
498     free(dialog);
499     goto error_ret;
500   }
501 
502   if (dialog->zathura == NULL) {
503     goto error_free;
504   }
505 
506   char* input = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
507 
508   /* no or empty password: ask again */
509   if (input == NULL || strlen(input) == 0) {
510     if (input != NULL) {
511       g_free(input);
512     }
513 
514     gdk_threads_add_idle(password_dialog, dialog);
515     return false;
516   }
517 
518   /* try to open document again */
519   if (document_open(dialog->zathura, dialog->path, dialog->uri, input,
520                     ZATHURA_PAGE_NUMBER_UNSPECIFIED) == false) {
521     gdk_threads_add_idle(password_dialog, dialog);
522   } else {
523     g_free(dialog->path);
524     g_free(dialog->uri);
525     free(dialog);
526   }
527 
528   g_free(input);
529 
530   return true;
531 
532 error_free:
533 
534   g_free(dialog->path);
535   free(dialog);
536 
537 error_ret:
538 
539   return false;
540 }
541 
542 gboolean
cb_view_resized(GtkWidget * UNUSED (widget),GtkAllocation * UNUSED (allocation),zathura_t * zathura)543 cb_view_resized(GtkWidget* UNUSED(widget), GtkAllocation* UNUSED(allocation), zathura_t* zathura)
544 {
545   if (zathura == NULL || zathura->document == NULL) {
546     return false;
547   }
548 
549   /* adjust the scale according to settings. If nothing needs to be resized,
550      it does not trigger the resize event.
551 
552      The right viewport size is already in the document object, due to a
553      previous call to adjustment_changed. We don't want to use the allocation in
554      here, because we would have to subtract scrollbars, etc. */
555   adjust_view(zathura);
556 
557   return false;
558 }
559 
560 void
cb_setting_recolor_change(girara_session_t * session,const char * name,girara_setting_type_t UNUSED (type),const void * value,void * UNUSED (data))561 cb_setting_recolor_change(girara_session_t* session, const char* name,
562                           girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data))
563 {
564   g_return_if_fail(value != NULL);
565   g_return_if_fail(session != NULL);
566   g_return_if_fail(session->global.data != NULL);
567   g_return_if_fail(name != NULL);
568   zathura_t* zathura = session->global.data;
569 
570   const bool bool_value = *((const bool*) value);
571 
572   if (zathura->sync.render_thread != NULL && zathura_renderer_recolor_enabled(zathura->sync.render_thread) != bool_value) {
573      zathura_renderer_enable_recolor(zathura->sync.render_thread, bool_value);
574      render_all(zathura);
575   }
576 }
577 
578 void
cb_setting_recolor_keep_hue_change(girara_session_t * session,const char * name,girara_setting_type_t UNUSED (type),const void * value,void * UNUSED (data))579 cb_setting_recolor_keep_hue_change(girara_session_t* session, const char* name,
580                                    girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data))
581 {
582   g_return_if_fail(value != NULL);
583   g_return_if_fail(session != NULL);
584   g_return_if_fail(session->global.data != NULL);
585   g_return_if_fail(name != NULL);
586   zathura_t* zathura = session->global.data;
587 
588   const bool bool_value = *((const bool*) value);
589 
590   if (zathura->sync.render_thread != NULL && zathura_renderer_recolor_hue_enabled(zathura->sync.render_thread) != bool_value) {
591      zathura_renderer_enable_recolor_hue(zathura->sync.render_thread, bool_value);
592      render_all(zathura);
593   }
594 }
595 
596 void
cb_setting_recolor_keep_reverse_video_change(girara_session_t * session,const char * name,girara_setting_type_t UNUSED (type),const void * value,void * UNUSED (data))597 cb_setting_recolor_keep_reverse_video_change(girara_session_t* session, const char* name,
598                                    girara_setting_type_t UNUSED(type), const void* value, void* UNUSED(data))
599 {
600   g_return_if_fail(value != NULL);
601   g_return_if_fail(session != NULL);
602   g_return_if_fail(session->global.data != NULL);
603   g_return_if_fail(name != NULL);
604   zathura_t* zathura = session->global.data;
605 
606   const bool bool_value = *((const bool*) value);
607 
608   if (zathura->sync.render_thread != NULL && zathura_renderer_recolor_reverse_video_enabled(zathura->sync.render_thread) != bool_value) {
609      zathura_renderer_enable_recolor_reverse_video(zathura->sync.render_thread, bool_value);
610      render_all(zathura);
611   }
612 }
613 
614 bool
cb_unknown_command(girara_session_t * session,const char * input)615 cb_unknown_command(girara_session_t* session, const char* input)
616 {
617   g_return_val_if_fail(session != NULL, false);
618   g_return_val_if_fail(session->global.data != NULL, false);
619   g_return_val_if_fail(input != NULL, false);
620 
621   zathura_t* zathura = session->global.data;
622 
623   if (zathura->document == NULL) {
624     return false;
625   }
626 
627   /* check for number */
628   const size_t size = strlen(input);
629   for (size_t i = 0; i < size; i++) {
630     if (g_ascii_isdigit(input[i]) == FALSE) {
631       return false;
632     }
633   }
634 
635   zathura_jumplist_add(zathura);
636   page_set(zathura, atoi(input) - 1);
637   zathura_jumplist_add(zathura);
638 
639   return true;
640 }
641 
642 void
cb_page_widget_text_selected(ZathuraPage * page,const char * text,void * data)643 cb_page_widget_text_selected(ZathuraPage* page, const char* text, void* data)
644 {
645   g_return_if_fail(page != NULL);
646   g_return_if_fail(text != NULL);
647   g_return_if_fail(data != NULL);
648 
649   zathura_t* zathura = data;
650   girara_mode_t mode = girara_mode_get(zathura->ui.session);
651   if (mode != zathura->modes.normal && mode != zathura->modes.fullscreen) {
652     return;
653   }
654 
655   GdkAtom* selection = get_selection(zathura);
656 
657   /* copy to clipboard */
658   if (selection != NULL) {
659     gtk_clipboard_set_text(gtk_clipboard_get(*selection), text, -1);
660 
661     bool notification = true;
662     girara_setting_get(zathura->ui.session, "selection-notification", &notification);
663 
664     if (notification == true) {
665       char* target = NULL;
666       girara_setting_get(zathura->ui.session, "selection-clipboard", &target);
667 
668       char* stripped_text = g_strdelimit(g_strdup(text), "\n\t\r\n", ' ');
669       char* escaped_text = g_markup_printf_escaped(
670           _("Copied selected text to selection %s: %s"), target, stripped_text);
671       g_free(target);
672       g_free(stripped_text);
673 
674       girara_notify(zathura->ui.session, GIRARA_INFO, "%s", escaped_text);
675       g_free(escaped_text);
676     }
677   }
678 
679   g_free(selection);
680 }
681 
682 void
cb_page_widget_image_selected(ZathuraPage * page,GdkPixbuf * pixbuf,void * data)683 cb_page_widget_image_selected(ZathuraPage* page, GdkPixbuf* pixbuf, void* data)
684 {
685   g_return_if_fail(page != NULL);
686   g_return_if_fail(pixbuf != NULL);
687   g_return_if_fail(data != NULL);
688 
689   zathura_t* zathura = data;
690   GdkAtom* selection = get_selection(zathura);
691 
692   if (selection != NULL) {
693     gtk_clipboard_set_image(gtk_clipboard_get(*selection), pixbuf);
694 
695     bool notification = true;
696     girara_setting_get(zathura->ui.session, "selection-notification", &notification);
697 
698     if (notification == true) {
699       char* target = NULL;
700       girara_setting_get(zathura->ui.session, "selection-clipboard", &target);
701 
702       char* escaped_text = g_markup_printf_escaped(
703           _("Copied selected image to selection %s"), target);
704       g_free(target);
705 
706       girara_notify(zathura->ui.session, GIRARA_INFO, "%s", escaped_text);
707     }
708 
709     g_free(selection);
710   }
711 }
712 
713 void
cb_page_widget_link(ZathuraPage * page,void * data)714 cb_page_widget_link(ZathuraPage* page, void* data)
715 {
716   g_return_if_fail(page != NULL);
717 
718   bool enter = (bool) data;
719 
720   GdkWindow* window = gtk_widget_get_parent_window(GTK_WIDGET(page));
721   GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(page));
722   GdkCursor* cursor = gdk_cursor_new_for_display(display, enter == true ? GDK_HAND1 : GDK_LEFT_PTR);
723   gdk_window_set_cursor(window, cursor);
724   g_object_unref(cursor);
725 }
726 
727 void
cb_page_widget_scaled_button_release(ZathuraPage * page_widget,GdkEventButton * event,void * data)728 cb_page_widget_scaled_button_release(ZathuraPage* page_widget, GdkEventButton* event,
729     void* data)
730 {
731   zathura_t* zathura = data;
732 
733   zathura_page_t* page = zathura_page_widget_get_page(page_widget);
734 
735   /* set page number (but don't scroll there. it was clicked on, so it's visible) */
736   if (event->button == GDK_BUTTON_PRIMARY) {
737     zathura_document_set_current_page_number(zathura->document, zathura_page_get_index(page));
738     refresh_view(zathura);
739   }
740 
741   if (event->button != GDK_BUTTON_PRIMARY || !(event->state & GDK_CONTROL_MASK)) {
742     return;
743   }
744 
745   bool synctex = false;
746   girara_setting_get(zathura->ui.session, "synctex", &synctex);
747   if (synctex == false) {
748     return;
749   }
750 
751   if (zathura->dbus != NULL) {
752     zathura_dbus_edit(zathura->dbus, zathura_page_get_index(page), event->x, event->y);
753   }
754 
755   char* editor = NULL;
756   girara_setting_get(zathura->ui.session, "synctex-editor-command", &editor);
757   if (editor == NULL || *editor == '\0') {
758     girara_debug("No SyncTeX editor specified.");
759     g_free(editor);
760     return;
761   }
762 
763   synctex_edit(editor, page, event->x, event->y);
764   g_free(editor);
765 }
766 
767 void
cb_window_update_icon(ZathuraRenderRequest * GIRARA_UNUSED (request),cairo_surface_t * surface,void * data)768 cb_window_update_icon(ZathuraRenderRequest* GIRARA_UNUSED(request), cairo_surface_t* surface, void* data)
769 {
770   zathura_t* zathura = data;
771 
772   girara_debug("updating window icon");
773   GdkPixbuf* pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface));
774   if (pixbuf == NULL) {
775     girara_error("Unable to convert cairo surface to Gdk Pixbuf.");
776   }
777 
778   gtk_window_set_icon(GTK_WINDOW(zathura->ui.session->gtk.window), pixbuf);
779   g_object_unref(pixbuf);
780 }
781