removed some old commented out code
[k8zathura.git] / src / zathura.c
bloba2ebfd9259c2b0e791684e131c31801cff97d5c1
1 /* See LICENSE file for license and copyright information */
2 #ifndef _BSD_SOURCE
3 # define _BSD_SOURCE
4 #endif
5 #if defined(_XOPEN_SOURCE) && _XOPEN_SOURCE < 500
6 # undef _XOPEN_SOURCE
7 #endif
8 #ifndef _XOPEN_SOURCE
9 # define _XOPEN_SOURCE 500
10 #endif
12 #ifndef _DEFAULT_SOURCE
13 # define _DEFAULT_SOURCE
14 #endif
16 #include <libgen.h>
17 #include <limits.h>
18 #include <math.h>
19 #include <regex.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
24 #include <poppler/glib/poppler.h>
25 #include <cairo.h>
27 #include <glib/gstdio.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
32 /* macros */
33 #define NORETURN __attribute__((noreturn))
34 #define LENGTH(x) (sizeof(x)/sizeof((x)[0]))
35 #define CLEAN(m) (m&~(GDK_MOD5_MASK|GDK_MOD2_MASK|GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK|GDK_BUTTON4_MASK|GDK_BUTTON5_MASK|GDK_LEAVE_NOTIFY_MASK))
37 #define MTLOCK_PDFOBJ() do { g_static_mutex_lock(&(Zathura.Lock.pdf_obj_lock)); } while (0)
38 #define MTUNLOCK_PDFOBJ() do { g_static_mutex_unlock(&(Zathura.Lock.pdf_obj_lock)); } while (0)
40 #define MTLOCK_PDFLIB() do { g_static_mutex_lock(&(Zathura.Lock.pdflib_lock)); } while (0)
41 #define MTUNLOCK_PDFLIB() do { g_static_mutex_unlock(&(Zathura.Lock.pdflib_lock)); } while (0)
43 #define MTLOCK_SEARCH() do { g_static_mutex_lock(&(Zathura.Lock.search_lock)); } while (0)
44 #define MTUNLOCK_SEARCH() do { g_static_mutex_unlock(&(Zathura.Lock.search_lock)); } while (0)
46 #define MTLOCK_SELECT() do { g_static_mutex_lock(&(Zathura.Lock.select_lock)); } while (0)
47 #define MTUNLOCK_SELECT() do { g_static_mutex_unlock(&(Zathura.Lock.select_lock)); } while (0)
50 // just remove the #if-#define-#endif block if support for poppler versions
51 // before 0.15 is dropped
52 #if !POPPLER_CHECK_VERSION(0,15,0)
53 # define poppler_page_get_selected_text poppler_page_get_text
54 #endif
57 //**************************************************************************
59 // surface cache
61 //**************************************************************************
63 // maximum surfaces in page surface cache
64 #define SURF_CACHE_SIZE (16)
66 typedef struct PageSurfCache_t {
67 cairo_surface_t *surf;
68 int page_id;
69 double scale;
70 struct PageSurfCache_t *prev;
71 struct PageSurfCache_t *next;
72 } PageSurfCache;
75 PageSurfCache *surfCacheHead = NULL;
76 PageSurfCache *surfCacheTail = NULL;
79 //==========================================================================
81 // surfCacheInit
83 // initialise page cache, if necessary
85 //==========================================================================
86 static void surfCacheInit (void) {
87 if (surfCacheHead) return;
88 surfCacheHead = (PageSurfCache *)g_malloc0(sizeof(PageSurfCache));
89 memset(surfCacheHead, 0, sizeof(PageSurfCache));
90 surfCacheHead->page_id = -1;
91 surfCacheHead->scale = 0;
92 PageSurfCache *last = surfCacheHead;
93 for (int f = 1; f < SURF_CACHE_SIZE; ++f) {
94 PageSurfCache *c = (PageSurfCache *)g_malloc0(sizeof(PageSurfCache));
95 memset(c, 0, sizeof(PageSurfCache));
96 c->page_id = -1;
97 c->scale = 0;
98 last->next = c;
99 c->prev = last;
100 last = c;
102 surfCacheTail = last;
106 //==========================================================================
108 // surfCacheDestroy
110 //==========================================================================
111 static void surfCacheDestroy (void) {
112 while (surfCacheHead) {
113 PageSurfCache *c = surfCacheHead;
114 surfCacheHead = c->next;
115 if (c->surf) cairo_surface_destroy(c->surf);
116 g_free(c);
118 surfCacheHead = surfCacheTail = NULL;
122 //==========================================================================
124 // surfCacheRemove
126 //==========================================================================
127 static void surfCacheRemove (PageSurfCache *c) {
128 if (!c) return;
129 if (c->prev) c->prev->next = c->next; else surfCacheHead = c->next;
130 if (c->next) c->next->prev = c->prev; else surfCacheTail = c->prev;
131 c->prev = c->next = NULL;
135 //==========================================================================
137 // surfCacheToFront
139 //==========================================================================
140 static void surfCacheToFront (PageSurfCache *c) {
141 if (!c || c == surfCacheHead) return; // nothing to do
142 surfCacheRemove(c);
143 // make first
144 c->next = surfCacheHead;
145 surfCacheHead->prev = c;
146 surfCacheHead = c;
150 //==========================================================================
152 // surfCacheGet
154 // this either returns an existing cached item, or creates a new
155 // one, of evicts and clears the old one
156 // the item is also moved to the front of the list
157 // destroys the surface if it is invalid (i.e. check the `surf`
158 // field in the returned struct)
160 //==========================================================================
161 static PageSurfCache *surfCacheGet (int pgid, const double scale) {
162 surfCacheInit();
163 for (PageSurfCache *c = surfCacheHead; c; c = c->next) {
164 if (c->page_id == pgid) {
165 surfCacheToFront(c);
166 if (c->scale != scale) {
167 if (c->surf) cairo_surface_destroy(c->surf);
168 c->surf = NULL;
169 c->scale = scale;
171 return c;
174 // not found, use tail
175 PageSurfCache *res = surfCacheTail;
176 surfCacheToFront(res);
177 if (res->surf) cairo_surface_destroy(res->surf);
178 res->surf = NULL;
179 res->page_id = pgid;
180 res->scale = scale;
181 return res;
185 /* enums */
186 enum {
187 NEXT, PREVIOUS, LEFT, RIGHT, UP, DOWN, BOTTOM, TOP, HIDE, HIGHLIGHT,
188 DELETE_LAST_WORD, DELETE_LAST_CHAR, DEFAULT, ERROR, WARNING, NEXT_GROUP,
189 PREVIOUS_GROUP, ZOOM_IN, ZOOM_OUT, ZOOM_ORIGINAL, ZOOM_SPECIFIC, FORWARD,
190 BACKWARD, ADJUST_BESTFIT, ADJUST_WIDTH, ADJUST_NONE, CONTINUOUS, DELETE_LAST,
191 ADD_MARKER, EVAL_MARKER, EXPAND, COLLAPSE, SELECT, GOTO_DEFAULT, GOTO_LABELS,
192 GOTO_OFFSET, HALF_UP, HALF_DOWN, FULL_UP, FULL_DOWN, NEXT_CHAR, PREVIOUS_CHAR,
193 DELETE_TO_LINE_START, APPEND_FILEPATH, NO_SEARCH,
195 WINDOW_UP, WINDOW_DOWN,
197 CENTER_LEFT_TOP, CENTER_CENTER, CENTER_RIGHT_BOTTOM,
201 /* define modes */
202 #define ALL (1<<0)
203 #define FULLSCREEN (1<<1)
204 #define INDEX (1<<2)
205 #define NORMAL (1<<3)
208 /* typedefs */
209 typedef struct CElement {
210 char *value;
211 char *description;
212 struct CElement *next;
213 } CompletionElement;
216 typedef struct CGroup {
217 char *value;
218 CompletionElement *elements;
219 struct CGroup *next;
220 } CompletionGroup;
224 typedef struct {
225 CompletionGroup *groups;
226 } Completion;
229 typedef struct {
230 char *command;
231 char *description;
232 int command_id;
233 gboolean is_group;
234 GtkWidget *row;
235 } CompletionRow;
238 typedef struct {
239 int n;
240 void *data;
241 } Argument;
244 typedef struct {
245 char *name;
246 int argument;
247 } ArgumentName;
250 typedef struct {
251 int mask;
252 int key;
253 void (*function) (Argument *);
254 int mode;
255 Argument argument;
256 } Shortcut;
259 typedef struct {
260 char *name;
261 int mode;
262 char *display;
263 } ModeName;
266 typedef struct {
267 char *name;
268 void (*function) (Argument *);
269 } ShortcutName;
272 typedef struct {
273 int mask;
274 int key;
275 void (*function) (Argument *);
276 Argument argument;
277 } InputbarShortcut;
280 typedef struct {
281 int direction;
282 void (*function) (Argument *);
283 Argument argument;
284 } MouseScrollEvent;
287 typedef struct {
288 char *command;
289 char *abbr;
290 gboolean (*function) (int, char **);
291 Completion* (*completion) (char *);
292 char *description;
293 } Command;
296 typedef struct {
297 char *regex;
298 void (*function) (char *, Argument *);
299 Argument argument;
300 } BufferCommand;
303 typedef struct {
304 char identifier;
305 gboolean (*function) (char *, Argument *);
306 int always;
307 Argument argument;
308 } SpecialCommand;
311 typedef struct SCList {
312 Shortcut element;
313 struct SCList *next;
314 } ShortcutList;
317 typedef struct {
318 char *identifier;
319 int key;
320 } GDKKey;
323 typedef struct {
324 PopplerPage *page;
325 int id;
326 char *label;
327 double width;
328 double height;
329 } Page;
332 typedef struct {
333 char *name;
334 void *variable;
335 char type;
336 gboolean render;
337 gboolean reinit;
338 char *description;
339 } Setting;
342 typedef struct {
343 int id;
344 int page;
345 } Marker;
348 typedef struct {
349 char *id;
350 int page;
351 } Bookmark;
354 /* zathura */
355 static struct {
356 struct {
357 GtkWidget *window;
358 GtkBox *box;
359 GtkBox *continuous;
360 GtkScrolledWindow *view;
361 GtkViewport *viewport;
362 GtkWidget *statusbar;
363 GtkBox *statusbar_entries;
364 GtkEntry *inputbar;
365 GtkWidget *index;
366 GtkWidget *information;
367 GtkWidget *drawing_area;
368 GtkWidget *document;
369 GdkNativeWindow embed;
370 } UI;
372 struct {
373 GdkColor default_fg;
374 GdkColor default_bg;
375 GdkColor inputbar_fg;
376 GdkColor inputbar_bg;
377 GdkColor statusbar_fg;
378 GdkColor statusbar_bg;
379 GdkColor completion_fg;
380 GdkColor completion_bg;
381 GdkColor completion_g_bg;
382 GdkColor completion_g_fg;
383 GdkColor completion_hl_fg;
384 GdkColor completion_hl_bg;
385 GdkColor notification_e_fg;
386 GdkColor notification_e_bg;
387 GdkColor notification_w_fg;
388 GdkColor notification_w_bg;
389 GdkColor recolor_darkcolor;
390 GdkColor recolor_lightcolor;
391 GdkColor search_highlight;
392 GdkColor select_text;
393 PangoFontDescription *font;
394 } Style;
396 struct {
397 GtkLabel *status_text;
398 GtkLabel *status_buffer;
399 GtkLabel *status_state;
400 GString *buffer;
401 GList *history;
402 int mode;
403 int viewing_mode;
404 gboolean recolor;
405 gboolean enable_labelmode;
406 int goto_mode;
407 int adjust_mode;
408 gboolean show_index;
409 gboolean show_statusbar;
410 gboolean show_inputbar;
411 int xcenter_mode;
412 } Global;
414 struct {
415 ShortcutList *sclist;
416 } Bindings;
418 struct {
419 gdouble x;
420 gdouble y;
421 } SelectPoint;
423 struct {
424 char *filename;
425 char *pages;
426 int scroll_percentage;
427 } State;
429 struct {
430 Marker *markers;
431 int number_of_markers;
432 int last;
433 } Marker;
435 struct {
436 GFileMonitor *monitor;
437 GFile *file;
438 } FileMonitor;
440 struct {
441 GKeyFile *data;
442 char *file;
443 Bookmark *bookmarks;
444 int number_of_bookmarks;
445 } Bookmarks;
447 struct {
448 PopplerDocument *document;
449 char *file;
450 char *password;
451 Page **pages;
452 int page_number;
453 double page_yskip_t; // [0..1]
454 int page_yskip_pix; // in screen pixels; for a gap
455 int number_of_pages;
456 int scale;
457 int rotate;
458 int page_gap; // usually 8
459 //TODO: cache total height and other things?
460 } PDF;
462 struct {
463 GStaticMutex pdflib_lock;
464 GStaticMutex pdf_obj_lock;
465 GStaticMutex search_lock;
466 GStaticMutex select_lock;
467 } Lock;
469 struct {
470 GList *results;
471 int page;
472 gboolean draw;
473 gchar *query;
474 } Search;
476 struct {
477 GThread *search_thread;
478 gboolean search_thread_running;
479 GThread *inotify_thread;
480 } Thread;
482 struct {
483 guint inputbar_activate;
484 guint inputbar_key_press_event;
485 } Handler;
487 struct {
488 gchar *config_dir;
489 gchar *data_dir;
490 } Config;
492 struct {
493 gchar *file;
494 } StdinSupport;
495 } Zathura;
498 /* function declarations */
499 void init_look (void);
500 void init_directories (void);
501 void init_bookmarks (void);
502 void init_keylist (void);
503 void init_settings (void);
504 void init_zathura (void);
505 void add_marker (int);
506 void build_index (GtkTreeModel *, GtkTreeIter *, PopplerIndexIter *);
507 void change_mode (int);
508 void calculate_offset (GtkWidget *, double *, double *);
509 void close_file (gboolean);
510 void enter_password (void);
511 void highlight_result (int, PopplerRectangle *);
512 cairo_surface_t *create_page_surface (int page_id);
513 void draw (int page_id);
514 void eval_marker (int);
515 void notify (int, const char *);
516 gboolean open_file (char *, char *);
517 gboolean open_stdin (gchar *);
518 void open_uri (char *);
519 void out_of_memory (void) NORETURN;
520 void update_status (void);
521 void read_bookmarks_file (void);
522 void write_bookmarks_file (void);
523 void free_bookmarks (void);
524 void read_configuration_file (const char *);
525 void read_configuration (void);
526 void recalc_rectangle (int, PopplerRectangle *);
527 void set_completion_row_color (GtkBox *, int, int);
528 void set_page (int);
529 void set_page_keep_ofs (int page);
530 void switch_view (GtkWidget *);
531 GtkEventBox *create_completion_row (GtkBox *, char *, char *, gboolean);
532 gchar *fix_path (const gchar *);
533 gchar *path_from_env (const gchar *);
534 gchar *get_home_dir (void);
536 Completion *completion_init (void);
537 CompletionGroup *completion_group_create (char*);
538 void completion_add_group (Completion *, CompletionGroup *);
539 void completion_free (Completion *);
540 void completion_group_add_element (CompletionGroup *, char *, char *);
542 /* thread declaration */
543 void *search (void *);
545 /* shortcut declarations */
546 void sc_abort (Argument *);
547 void sc_adjust_window (Argument *);
548 void sc_xcenter_window (Argument *);
549 //void sc_ycenter_window (Argument *);
550 void sc_change_buffer (Argument *);
551 void sc_change_mode (Argument *);
552 void sc_focus_inputbar (Argument *);
553 void sc_follow (Argument *);
554 void sc_navigate (Argument *);
555 void sc_recolor (Argument *);
556 void sc_reload (Argument *);
557 void sc_rotate (Argument *);
558 void sc_scroll (Argument *);
559 void sc_search (Argument *);
560 void sc_switch_goto_mode (Argument *);
561 void sc_navigate_index (Argument *);
562 void sc_toggle_index (Argument *);
563 void sc_toggle_inputbar (Argument *);
564 void sc_toggle_fullscreen (Argument *);
565 void sc_toggle_statusbar (Argument *);
566 void sc_quit (Argument *);
567 void sc_zoom (Argument *);
569 /* inputbar shortcut declarations */
570 void isc_abort (Argument *);
571 void isc_command_history (Argument *);
572 void isc_completion (Argument *);
573 void isc_string_manipulation (Argument *);
575 /* command declarations */
576 gboolean cmd_bookmark (int, char **);
577 gboolean cmd_open_bookmark (int, char **);
578 gboolean cmd_close (int, char **);
579 gboolean cmd_delete_bookmark (int, char **);
580 gboolean cmd_export (int, char **);
581 gboolean cmd_info (int, char **);
582 gboolean cmd_map (int, char **);
583 gboolean cmd_open (int, char **);
584 gboolean cmd_print (int, char **);
585 gboolean cmd_rotate (int, char **);
586 gboolean cmd_set (int, char **);
587 gboolean cmd_quit (int, char **);
588 gboolean cmd_save (int, char **);
589 gboolean cmd_savef (int, char **);
591 /* completion commands */
592 Completion *cc_bookmark (char *);
593 Completion *cc_export (char *);
594 Completion *cc_open (char *);
595 Completion *cc_print (char *);
596 Completion *cc_set (char *);
598 /* buffer command declarations */
599 void bcmd_evalmarker (char *, Argument *);
600 void bcmd_goto (char *, Argument *);
601 void bcmd_scroll (char *, Argument *);
602 void bcmd_setmarker (char *, Argument *);
603 void bcmd_zoom (char *, Argument *);
605 /* special command delcarations */
606 gboolean scmd_search (gchar *, Argument *);
608 /* callback declarations */
609 gboolean cb_destroy (GtkWidget *, gpointer);
610 gboolean cb_draw (GtkWidget *, GdkEventExpose *, gpointer);
611 gboolean cb_index_row_activated (GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, gpointer);
612 gboolean cb_inputbar_kb_pressed (GtkWidget *, GdkEventKey *, gpointer);
613 gboolean cb_inputbar_activate (GtkEntry *, gpointer);
614 gboolean cb_inputbar_form_activate (GtkEntry *, gpointer);
615 gboolean cb_inputbar_password_activate (GtkEntry *, gpointer);
616 gboolean cb_view_kb_pressed (GtkWidget *, GdkEventKey *, gpointer);
617 gboolean cb_view_resized (GtkWidget *, GtkAllocation *, gpointer);
618 gboolean cb_view_button_pressed (GtkWidget *, GdkEventButton *, gpointer);
619 gboolean cb_view_button_release (GtkWidget *, GdkEventButton *, gpointer);
620 gboolean cb_view_motion_notify (GtkWidget *, GdkEventMotion *, gpointer);
621 gboolean cb_view_scrolled (GtkWidget *, GdkEventScroll *, gpointer);
622 gboolean cb_watch_file (GFileMonitor *, GFile *, GFile *, GFileMonitorEvent, gpointer);
625 /* configuration */
626 #include "config.h"
629 //==========================================================================
631 // safe_realloc
633 //==========================================================================
634 static void *safe_realloc (void **ptr, size_t nmemb, size_t size) {
635 static const size_t limit = ~((size_t)0u);
636 void *tmp = NULL;
637 /* Check for overflow. */
638 if (nmemb > limit/size) goto failure;
639 tmp = realloc(*ptr, nmemb*size);
640 /* Check for out of memory. */
641 if (!tmp) goto failure;
642 *ptr = tmp;
643 return *ptr;
644 /* Error handling. */
645 failure:
646 free(*ptr);
647 *ptr = NULL;
648 return NULL;
652 //==========================================================================
654 // init_look
656 //==========================================================================
657 void init_look (void) {
658 /* parse */
659 gdk_color_parse(default_fgcolor, &(Zathura.Style.default_fg));
660 gdk_color_parse(default_bgcolor, &(Zathura.Style.default_bg));
661 gdk_color_parse(inputbar_fgcolor, &(Zathura.Style.inputbar_fg));
662 gdk_color_parse(inputbar_bgcolor, &(Zathura.Style.inputbar_bg));
663 gdk_color_parse(statusbar_fgcolor, &(Zathura.Style.statusbar_fg));
664 gdk_color_parse(statusbar_bgcolor, &(Zathura.Style.statusbar_bg));
665 gdk_color_parse(completion_fgcolor, &(Zathura.Style.completion_fg));
666 gdk_color_parse(completion_bgcolor, &(Zathura.Style.completion_bg));
667 gdk_color_parse(completion_g_fgcolor, &(Zathura.Style.completion_g_fg));
668 gdk_color_parse(completion_g_fgcolor, &(Zathura.Style.completion_g_fg));
669 gdk_color_parse(completion_hl_fgcolor, &(Zathura.Style.completion_hl_fg));
670 gdk_color_parse(completion_hl_bgcolor, &(Zathura.Style.completion_hl_bg));
671 gdk_color_parse(notification_e_fgcolor, &(Zathura.Style.notification_e_fg));
672 gdk_color_parse(notification_e_bgcolor, &(Zathura.Style.notification_e_bg));
673 gdk_color_parse(notification_w_fgcolor, &(Zathura.Style.notification_w_fg));
674 gdk_color_parse(notification_w_bgcolor, &(Zathura.Style.notification_w_bg));
675 gdk_color_parse(recolor_darkcolor, &(Zathura.Style.recolor_darkcolor));
676 gdk_color_parse(recolor_lightcolor, &(Zathura.Style.recolor_lightcolor));
677 gdk_color_parse(search_highlight, &(Zathura.Style.search_highlight));
678 gdk_color_parse(select_text, &(Zathura.Style.select_text));
680 pango_font_description_free(Zathura.Style.font);
681 Zathura.Style.font = pango_font_description_from_string(font);
683 /* window and viewport */
684 gtk_widget_modify_bg(GTK_WIDGET(Zathura.UI.window), GTK_STATE_NORMAL, &(Zathura.Style.default_bg));
685 gtk_widget_modify_bg(GTK_WIDGET(Zathura.UI.viewport), GTK_STATE_NORMAL, &(Zathura.Style.default_bg));
687 /* drawing area */
688 gtk_widget_modify_bg(GTK_WIDGET(Zathura.UI.drawing_area), GTK_STATE_NORMAL, &(Zathura.Style.default_bg));
690 /* statusbar */
691 gtk_widget_modify_bg(GTK_WIDGET(Zathura.UI.statusbar), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_bg));
693 gtk_widget_modify_fg(GTK_WIDGET(Zathura.Global.status_text), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_fg));
694 gtk_widget_modify_fg(GTK_WIDGET(Zathura.Global.status_state), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_fg));
695 gtk_widget_modify_fg(GTK_WIDGET(Zathura.Global.status_buffer), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_fg));
697 gtk_widget_modify_font(GTK_WIDGET(Zathura.Global.status_text), Zathura.Style.font);
698 gtk_widget_modify_font(GTK_WIDGET(Zathura.Global.status_state), Zathura.Style.font);
699 gtk_widget_modify_font(GTK_WIDGET(Zathura.Global.status_buffer), Zathura.Style.font);
701 /* inputbar */
702 gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_bg));
703 gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_fg));
704 gtk_widget_modify_font(GTK_WIDGET(Zathura.UI.inputbar), Zathura.Style.font);
706 g_object_set(G_OBJECT(Zathura.UI.inputbar), "has-frame", FALSE, NULL);
708 /* scrollbars */
709 if (show_scrollbars) {
710 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.UI.view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
711 } else {
712 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.UI.view), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
715 /* inputbar */
716 if (Zathura.Global.show_inputbar) {
717 gtk_widget_show(GTK_WIDGET(Zathura.UI.inputbar));
718 } else {
719 gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
722 /* statusbar */
723 if (Zathura.Global.show_statusbar) {
724 gtk_widget_show(GTK_WIDGET(Zathura.UI.statusbar));
725 } else {
726 gtk_widget_hide(GTK_WIDGET(Zathura.UI.statusbar));
731 //==========================================================================
733 // fix_path
735 //==========================================================================
736 gchar *fix_path (const gchar *path) {
737 if (!path) return NULL;
738 if (path[0] == '~') {
739 gchar *home_path = get_home_dir();
740 gchar *res = g_build_filename(home_path, path+1, NULL);
741 g_free(home_path);
742 return res;
744 return g_strdup(path);
748 //==========================================================================
750 // path_from_env
752 //==========================================================================
753 gchar *path_from_env (const gchar *var) {
754 gchar *env = fix_path(g_getenv(var)), *res;
755 if (!env) return NULL;
756 res = g_build_filename(env, "zathura", NULL);
757 g_free(env);
758 return res;
762 //==========================================================================
764 // get_home_dir
766 //==========================================================================
767 gchar *get_home_dir (void) {
768 const gchar *homedir = g_getenv("HOME");
769 return g_strdup(homedir ? homedir : g_get_home_dir());
773 //==========================================================================
775 // init_directories
777 //==========================================================================
778 void init_directories (void) {
779 /* setup directories */
780 if (!Zathura.Config.config_dir) {
781 #ifndef ZATHURA_NO_XDG
782 gchar *env = path_from_env("XDG_CONFIG_HOME");
783 if (env) Zathura.Config.config_dir = env;
784 else
785 #endif
786 Zathura.Config.config_dir = fix_path(CONFIG_DIR);
788 if (!Zathura.Config.data_dir) {
789 #ifndef ZATHURA_NO_XDG
790 gchar *env = path_from_env("XDG_DATA_HOME");
791 if (env) Zathura.Config.data_dir = env;
792 else
793 #endif
794 Zathura.Config.data_dir = fix_path(DATA_DIR);
797 /* create zathura (config/data) directory */
798 g_mkdir_with_parents(Zathura.Config.config_dir, 0771);
799 g_mkdir_with_parents(Zathura.Config.data_dir, 0771);
803 //==========================================================================
805 // init_bookmarks
807 //==========================================================================
808 void init_bookmarks (void) {
809 /* init variables */
810 Zathura.Bookmarks.number_of_bookmarks = 0;
811 Zathura.Bookmarks.bookmarks = NULL;
813 Zathura.Bookmarks.file = g_build_filename(Zathura.Config.data_dir, BOOKMARK_FILE, NULL);
814 //fprintf(stderr, "CFG: %s\n", Zathura.Bookmarks.file);
815 read_bookmarks_file();
819 //==========================================================================
821 // is_reserved_bm_name
823 //==========================================================================
824 static gboolean is_reserved_bm_name (const char *bm_name) {
825 for (int i = 0; i < BM_MAX; ++i) if (strcmp(bm_reserved_names[i], bm_name) == 0) return TRUE;
826 return FALSE;
830 //==========================================================================
832 // init_keylist
834 //==========================================================================
835 void init_keylist (void) {
836 ShortcutList *e = NULL, *p = NULL;
837 for (int i = 0; i < LENGTH(shortcuts); ++i) {
838 e = malloc(sizeof(ShortcutList));
839 if (!e) out_of_memory();
840 e->element = shortcuts[i];
841 e->next = NULL;
842 if (!Zathura.Bindings.sclist) Zathura.Bindings.sclist = e;
843 if (p) p->next = e;
844 p = e;
849 //==========================================================================
851 // init_settings
853 //==========================================================================
854 void init_settings (void) {
855 Zathura.State.filename = g_strdup((char*) default_text);
856 Zathura.Global.adjust_mode = adjust_open;
857 Zathura.Global.xcenter_mode = xcenter_open;
859 gtk_window_set_default_size(GTK_WINDOW(Zathura.UI.window), default_width, default_height);
860 if (default_maximize) gtk_window_maximize(GTK_WINDOW(Zathura.UI.window));
864 //==========================================================================
866 // init_zathura
868 //==========================================================================
869 void init_zathura (void) {
870 /* init mutexes */
871 g_static_mutex_init(&(Zathura.Lock.pdflib_lock));
872 g_static_mutex_init(&(Zathura.Lock.search_lock));
873 g_static_mutex_init(&(Zathura.Lock.pdf_obj_lock));
874 g_static_mutex_init(&(Zathura.Lock.select_lock));
876 /* other */
877 Zathura.Global.mode = NORMAL;
878 Zathura.Global.viewing_mode = NORMAL;
879 Zathura.Global.recolor = 0;
880 Zathura.Global.goto_mode = GOTO_MODE;
881 Zathura.Global.show_index = FALSE;
882 Zathura.Global.show_inputbar = TRUE;
883 Zathura.Global.show_statusbar = TRUE;
885 Zathura.State.pages = g_strdup("");
886 Zathura.State.scroll_percentage = 0;
888 Zathura.Marker.markers = NULL;
889 Zathura.Marker.number_of_markers = 0;
890 Zathura.Marker.last = -1;
892 Zathura.Search.results = NULL;
893 Zathura.Search.page = 0;
894 Zathura.Search.draw = FALSE;
895 Zathura.Search.query = NULL;
897 Zathura.FileMonitor.monitor = NULL;
898 Zathura.FileMonitor.file = NULL;
900 Zathura.StdinSupport.file = NULL;
902 /* window */
903 if (Zathura.UI.embed) {
904 Zathura.UI.window = gtk_plug_new(Zathura.UI.embed);
905 } else {
906 Zathura.UI.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
909 /* UI */
910 Zathura.UI.box = GTK_BOX(gtk_vbox_new(FALSE, 0));
911 Zathura.UI.continuous = GTK_BOX(gtk_vbox_new(FALSE, 0));
912 Zathura.UI.view = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
913 Zathura.UI.viewport = GTK_VIEWPORT(gtk_viewport_new(NULL, NULL));
914 Zathura.UI.drawing_area = gtk_drawing_area_new();
915 Zathura.UI.statusbar = gtk_event_box_new();
916 Zathura.UI.statusbar_entries = GTK_BOX(gtk_hbox_new(FALSE, 0));
917 Zathura.UI.inputbar = GTK_ENTRY(gtk_entry_new());
918 Zathura.UI.document = gtk_event_box_new();
920 /* window */
921 gtk_window_set_title(GTK_WINDOW(Zathura.UI.window), "zathura");
922 GdkGeometry hints = { 1, 1 };
923 gtk_window_set_geometry_hints(GTK_WINDOW(Zathura.UI.window), NULL, &hints, GDK_HINT_MIN_SIZE);
924 g_signal_connect(G_OBJECT(Zathura.UI.window), "destroy", G_CALLBACK(cb_destroy), NULL);
926 /* box */
927 gtk_box_set_spacing(Zathura.UI.box, 0);
928 gtk_container_add(GTK_CONTAINER(Zathura.UI.window), GTK_WIDGET(Zathura.UI.box));
930 /* continuous */
931 gtk_box_set_spacing(Zathura.UI.continuous, 5);
933 /* events */
934 gtk_container_add(GTK_CONTAINER(Zathura.UI.document), GTK_WIDGET(Zathura.UI.drawing_area));
935 gtk_widget_add_events(GTK_WIDGET(Zathura.UI.document), GDK_POINTER_MOTION_MASK|GDK_POINTER_MOTION_HINT_MASK|GDK_BUTTON_PRESS_MASK|GDK_BUTTON_RELEASE_MASK);
937 g_signal_connect(G_OBJECT(Zathura.UI.document), "button-press-event", G_CALLBACK(cb_view_button_pressed), NULL);
938 g_signal_connect(G_OBJECT(Zathura.UI.document), "button-release-event", G_CALLBACK(cb_view_button_release), NULL);
939 g_signal_connect(G_OBJECT(Zathura.UI.document), "motion-notify-event", G_CALLBACK(cb_view_motion_notify), NULL);
940 gtk_widget_show(Zathura.UI.document);
942 /* view */
943 g_signal_connect(G_OBJECT(Zathura.UI.view), "key-press-event", G_CALLBACK(cb_view_kb_pressed), NULL);
944 g_signal_connect(G_OBJECT(Zathura.UI.view), "size-allocate", G_CALLBACK(cb_view_resized), NULL);
945 g_signal_connect(G_OBJECT(Zathura.UI.view), "scroll-event", G_CALLBACK(cb_view_scrolled), NULL);
946 gtk_container_add(GTK_CONTAINER(Zathura.UI.view), GTK_WIDGET(Zathura.UI.viewport));
947 gtk_viewport_set_shadow_type(Zathura.UI.viewport, GTK_SHADOW_NONE);
949 /* drawing area */
950 gtk_widget_show(Zathura.UI.drawing_area);
951 g_signal_connect(G_OBJECT(Zathura.UI.drawing_area), "expose-event", G_CALLBACK(cb_draw), NULL);
953 /* statusbar */
954 Zathura.Global.status_text = GTK_LABEL(gtk_label_new(NULL));
955 Zathura.Global.status_state = GTK_LABEL(gtk_label_new(NULL));
956 Zathura.Global.status_buffer = GTK_LABEL(gtk_label_new(NULL));
958 gtk_misc_set_alignment(GTK_MISC(Zathura.Global.status_text), 0.0, 0.0);
959 gtk_misc_set_alignment(GTK_MISC(Zathura.Global.status_state), 1.0, 0.0);
960 gtk_misc_set_alignment(GTK_MISC(Zathura.Global.status_buffer), 1.0, 0.0);
962 gtk_misc_set_padding(GTK_MISC(Zathura.Global.status_text), 2.0, 4.0);
963 gtk_misc_set_padding(GTK_MISC(Zathura.Global.status_state), 2.0, 4.0);
964 gtk_misc_set_padding(GTK_MISC(Zathura.Global.status_buffer), 2.0, 4.0);
966 gtk_label_set_use_markup(Zathura.Global.status_text, TRUE);
967 gtk_label_set_use_markup(Zathura.Global.status_state, TRUE);
968 gtk_label_set_use_markup(Zathura.Global.status_buffer, TRUE);
970 gtk_box_pack_start(Zathura.UI.statusbar_entries, GTK_WIDGET(Zathura.Global.status_text), TRUE, TRUE, 2);
971 gtk_box_pack_start(Zathura.UI.statusbar_entries, GTK_WIDGET(Zathura.Global.status_buffer), FALSE, FALSE, 2);
972 gtk_box_pack_start(Zathura.UI.statusbar_entries, GTK_WIDGET(Zathura.Global.status_state), FALSE, FALSE, 2);
974 gtk_container_add(GTK_CONTAINER(Zathura.UI.statusbar), GTK_WIDGET(Zathura.UI.statusbar_entries));
976 /* inputbar */
977 gtk_entry_set_inner_border(Zathura.UI.inputbar, NULL);
978 gtk_entry_set_has_frame( Zathura.UI.inputbar, FALSE);
979 gtk_editable_set_editable( GTK_EDITABLE(Zathura.UI.inputbar), TRUE);
981 Zathura.Handler.inputbar_key_press_event = g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "key-press-event", G_CALLBACK(cb_inputbar_kb_pressed), NULL);
982 Zathura.Handler.inputbar_activate = g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "activate", G_CALLBACK(cb_inputbar_activate), NULL);
984 /* packing */
985 gtk_box_pack_start(Zathura.UI.box, GTK_WIDGET(Zathura.UI.view), TRUE, TRUE, 0);
986 gtk_box_pack_start(Zathura.UI.box, GTK_WIDGET(Zathura.UI.statusbar), FALSE, FALSE, 0);
987 gtk_box_pack_end(Zathura.UI.box, GTK_WIDGET(Zathura.UI.inputbar), FALSE, FALSE, 0);
991 //==========================================================================
993 // add_marker
995 //==========================================================================
996 void add_marker (int id) {
997 if (id < 0x30 || id > 0x7A) return;
998 /* current information */
999 int page_number = Zathura.PDF.page_number;
1000 /* search if entry already exists */
1001 for (int i = 0; i < Zathura.Marker.number_of_markers; ++i) {
1002 if (Zathura.Marker.markers[i].id == id) {
1003 Zathura.Marker.markers[i].page = page_number;
1004 Zathura.Marker.last = page_number;
1005 return;
1008 /* add new marker */
1009 int marker_index = Zathura.Marker.number_of_markers++;
1010 Zathura.Marker.markers = safe_realloc((void**)&Zathura.Marker.markers, Zathura.Marker.number_of_markers, sizeof(Marker));
1011 if (!Zathura.Marker.markers) out_of_memory();
1012 Zathura.Marker.markers[marker_index].id = id;
1013 Zathura.Marker.markers[marker_index].page = page_number;
1014 Zathura.Marker.last = page_number;
1018 //==========================================================================
1020 // build_index
1022 //==========================================================================
1023 void build_index (GtkTreeModel *model, GtkTreeIter *parent, PopplerIndexIter *index_iter) {
1024 do {
1025 GtkTreeIter tree_iter;
1026 PopplerIndexIter *child;
1027 PopplerAction *action;
1028 gchar *markup;
1030 action = poppler_index_iter_get_action(index_iter);
1031 if (!action) continue;
1033 markup = g_markup_escape_text (action->any.title, -1);
1035 gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent);
1036 gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, 0, markup, 1, action, -1);
1037 g_object_weak_ref(G_OBJECT(model), (GWeakNotify) poppler_action_free, action);
1038 g_free(markup);
1040 child = poppler_index_iter_get_child(index_iter);
1041 if (child) build_index(model, &tree_iter, child);
1042 poppler_index_iter_free(child);
1043 } while (poppler_index_iter_next(index_iter));
1047 //==========================================================================
1049 // get_current_page_screen_ytop
1051 //==========================================================================
1052 int get_current_page_screen_ytop (void) {
1053 if (!Zathura.PDF.document || Zathura.PDF.number_of_pages < 1) return 0;
1054 const int page_id = Zathura.PDF.page_number;
1055 if (page_id < 0) return 0;
1056 const double scale = ((double)Zathura.PDF.scale/100.0);
1057 if (page_id >= Zathura.PDF.number_of_pages) {
1058 // beyond the last page, show the whole last page
1059 if (!Zathura.UI.view) return 0;
1060 GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
1061 int window_y = gtk_adjustment_get_page_size(adjustment);
1062 Page *current_page = Zathura.PDF.pages[Zathura.PDF.number_of_pages-1];
1063 const double height = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width)*scale+Zathura.PDF.page_gap;
1064 return (height >= window_y ? height-window_y : 0);
1065 } else {
1066 Page *current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
1067 const double height = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width);
1068 return -(height*Zathura.PDF.page_yskip_t*scale+Zathura.PDF.page_yskip_pix);
1073 //==========================================================================
1075 // calc_current_document_screen_offset
1077 //==========================================================================
1078 int calc_current_document_screen_offset (void) {
1079 if (!Zathura.PDF.document || Zathura.PDF.number_of_pages == 0) return 0;
1081 int dest_page_id = Zathura.PDF.page_number;
1082 if (dest_page_id < 0) dest_page_id = 1;
1083 if (dest_page_id >= Zathura.PDF.number_of_pages) dest_page_id = Zathura.PDF.number_of_pages-1;
1085 //fprintf(stderr, "curr_page_id=%d; t=%g; px=%d\n", dest_page_id, Zathura.PDF.page_yskip_t, Zathura.PDF.page_yskip_pix);
1087 const double scale = ((double)Zathura.PDF.scale/100.0);
1088 Page *current_page;
1090 int curryofs = 0;
1091 for (int page_id = 0; page_id < dest_page_id; ++page_id) {
1092 current_page = Zathura.PDF.pages[page_id];
1093 const int scheight = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width)*scale;
1094 curryofs += scheight+Zathura.PDF.page_gap;
1097 current_page = Zathura.PDF.pages[dest_page_id];
1098 curryofs += ((Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width)*Zathura.PDF.page_yskip_t*scale+Zathura.PDF.page_yskip_pix);
1100 //fprintf(stderr, "curr_page_id=%d; t=%g; px=%d; curryofs=%d\n", dest_page_id, Zathura.PDF.page_yskip_t, Zathura.PDF.page_yskip_pix, curryofs);
1101 return curryofs;
1105 //==========================================================================
1107 // convert_screen_document_offset_to_pos
1109 //==========================================================================
1110 void convert_screen_document_offset_to_pos (int docofs, int *out_page_id, double *out_page_yskip_t, int *out_page_yskip_pix) {
1111 int tmppgid = 0, tmppgofs = 0;
1112 double tmpt = 0;
1113 if (!out_page_id) out_page_id = &tmppgid;
1114 if (!out_page_yskip_t) out_page_yskip_t = &tmpt;
1115 if (!out_page_yskip_pix) out_page_yskip_pix = &tmppgofs;
1117 if (!Zathura.PDF.document || Zathura.PDF.number_of_pages < 1) {
1118 *out_page_id = 0;
1119 *out_page_yskip_t = 0;
1120 *out_page_yskip_pix = 0;
1121 return;
1124 if (docofs < 0) docofs = 0;
1126 const double scale = ((double)Zathura.PDF.scale/100.0);
1128 //fprintf(stderr, "docofs=%d\n", docofs);
1130 int curryofs = 0;
1131 for (int page_id = 0; page_id < Zathura.PDF.number_of_pages; ++page_id) {
1132 Page *current_page = Zathura.PDF.pages[page_id];
1133 const double height = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width);
1134 const int scheight = height*scale;
1135 const int pgend = curryofs+scheight;
1136 if (docofs >= curryofs && docofs < pgend+Zathura.PDF.page_gap) {
1137 *out_page_id = page_id;
1138 if (docofs < pgend) {
1139 // some part of the page is visible
1140 *out_page_yskip_t = (double)(docofs-curryofs)/(double)scheight;
1141 *out_page_yskip_pix = 0;
1142 } else {
1143 // no page visible, we are inside a gap
1144 *out_page_yskip_t = 1;
1145 *out_page_yskip_pix = docofs-pgend;
1147 //fprintf(stderr, "docofs=%d; curryofs=%d; pgend=%d; t=%g; px=%d\n", docofs, curryofs, pgend+Zathura.PDF.page_gap, *out_page_yskip_t, *out_page_yskip_pix);
1148 return;
1150 curryofs = pgend+Zathura.PDF.page_gap;
1153 // beyond the last page, show the whole last page
1155 int page_id = Zathura.PDF.number_of_pages-1;
1156 *out_page_id = page_id;
1157 *out_page_yskip_t = 0;
1158 *out_page_yskip_pix = 0;
1159 if (Zathura.UI.view) {
1160 GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
1161 int window_y = gtk_adjustment_get_page_size(adjustment);
1162 Page *current_page = Zathura.PDF.pages[page_id];
1163 const double height = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width);
1164 const int scheight = height*scale+Zathura.PDF.page_gap;
1165 if (scheight > window_y) {
1166 *out_page_yskip_pix = Zathura.PDF.page_gap;
1167 window_y -= Zathura.PDF.page_gap;
1168 *out_page_yskip_t = height*scale/(double)window_y;
1175 //==========================================================================
1177 // scroll_up_pixels_no_draw
1179 //==========================================================================
1180 void scroll_up_pixels_no_draw (int count) {
1181 if (!count) return;
1182 int currypos = calc_current_document_screen_offset();
1183 currypos -= count;
1184 convert_screen_document_offset_to_pos(currypos, &Zathura.PDF.page_number, &Zathura.PDF.page_yskip_t, &Zathura.PDF.page_yskip_pix);
1188 //==========================================================================
1190 // scroll_down_pixels_no_draw
1192 //==========================================================================
1193 void scroll_down_pixels_no_draw (int count) {
1194 if (!count) return;
1195 int currypos = calc_current_document_screen_offset();
1196 currypos += count;
1197 convert_screen_document_offset_to_pos(currypos, &Zathura.PDF.page_number, &Zathura.PDF.page_yskip_t, &Zathura.PDF.page_yskip_pix);
1201 //==========================================================================
1203 // create_page_surface
1205 //==========================================================================
1206 cairo_surface_t *create_page_surface (int page_id) {
1207 if (!Zathura.PDF.document || page_id < 0 || page_id >= Zathura.PDF.number_of_pages) return NULL;
1209 const double scale = ((double)Zathura.PDF.scale/100.0);
1211 PageSurfCache *cc = surfCacheGet(page_id, scale);
1212 // create new surface if necessary
1213 if (!cc->surf) {
1214 //fprintf(stderr, "creating new page surface for page #%d\n", page_id);
1215 int rotate = Zathura.PDF.rotate;
1216 Page *current_page = Zathura.PDF.pages[page_id];
1217 cairo_surface_t *surf = NULL;
1218 const double page_width = current_page->width;
1219 const double page_height = current_page->height;
1221 double width, height;
1222 if (rotate == 0 || rotate == 180) {
1223 width = page_width*scale;
1224 height = page_height*scale;
1225 } else {
1226 width = page_height*scale;
1227 height = page_width*scale;
1230 surf = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
1232 cairo_t *cairo = cairo_create(surf);
1234 cairo_save(cairo);
1235 cairo_set_source_rgb(cairo, 1, 1, 1);
1236 cairo_rectangle(cairo, 0, 0, width, height);
1237 cairo_fill(cairo);
1238 cairo_restore(cairo);
1239 cairo_save(cairo);
1241 switch (rotate) {
1242 case 90: cairo_translate(cairo, width, 0); break;
1243 case 180: cairo_translate(cairo, width, height); break;
1244 case 270: cairo_translate(cairo, 0, height); break;
1245 default: cairo_translate(cairo, 0, 0); break;
1248 if (scale != 1.0) cairo_scale(cairo, scale, scale);
1249 if (rotate != 0) cairo_rotate(cairo, rotate*G_PI/180.0);
1251 MTLOCK_PDFLIB();
1252 poppler_page_render(current_page->page, cairo);
1253 MTUNLOCK_PDFLIB();
1255 cairo_restore(cairo);
1256 cairo_destroy(cairo);
1258 if (Zathura.Global.recolor) {
1259 unsigned char *image = cairo_image_surface_get_data(surf);
1260 int width = cairo_image_surface_get_width(surf);
1261 int height = cairo_image_surface_get_height(surf);
1262 int rowstride = cairo_image_surface_get_stride(surf);
1264 /* recolor code based on qimageblitz library flatten() function
1265 (http://sourceforge.net/projects/qimageblitz/) */
1267 int r1 = Zathura.Style.recolor_darkcolor.red/257;
1268 int g1 = Zathura.Style.recolor_darkcolor.green/257;
1269 int b1 = Zathura.Style.recolor_darkcolor.blue/257;
1270 int r2 = Zathura.Style.recolor_lightcolor.red/257;
1271 int g2 = Zathura.Style.recolor_lightcolor.green/257;
1272 int b2 = Zathura.Style.recolor_lightcolor.blue/257;
1274 int min = 0x00;
1275 int max = 0xFF;
1276 int mean;
1278 float sr = ((float)r2-r1)/(max-min);
1279 float sg = ((float)g2-g1)/(max-min);
1280 float sb = ((float)b2-b1)/(max-min);
1282 for (int y = 0; y < height; ++y) {
1283 unsigned char *data = image+y*rowstride;
1284 for (int x = 0; x < width; ++x) {
1285 mean = (data[0]+data[1]+data[2])/3;
1286 data[2] = sr*(mean-min)+r1+0.5;
1287 data[1] = sg*(mean-min)+g1+0.5;
1288 data[0] = sb*(mean-min)+b1+0.5;
1289 data += 4;
1294 cc->surf = surf;
1295 } else {
1296 //fprintf(stderr, "reusing new page surface for page #%d\n", page_id);
1299 return cc->surf;
1303 //==========================================================================
1305 // draw
1307 //==========================================================================
1308 void draw (int page_id) {
1309 cairo_surface_t *surf = create_page_surface(page_id);
1310 if (!surf) return;
1312 int w = cairo_image_surface_get_width(surf);
1313 int h = cairo_image_surface_get_height(surf);
1315 gtk_widget_set_size_request(Zathura.UI.drawing_area, w, h);
1316 gtk_widget_queue_draw(Zathura.UI.drawing_area);
1320 //==========================================================================
1322 // change_mode
1324 //==========================================================================
1325 void change_mode (int mode) {
1326 char *mode_text = 0;
1327 for (unsigned int i = 0; i != LENGTH(mode_names); ++i) {
1328 if (mode_names[i].mode == mode) {
1329 mode_text = mode_names[i].display;
1330 break;
1333 if (!mode_text) {
1334 switch(mode) {
1335 case ADD_MARKER: mode_text = ""; break;
1336 case EVAL_MARKER: mode_text = ""; break;
1337 default: mode_text = ""; mode = NORMAL; break;
1340 Zathura.Global.mode = mode;
1341 notify(DEFAULT, mode_text);
1345 //==========================================================================
1347 // calculate_offset
1349 //==========================================================================
1350 void calculate_offset (GtkWidget *widget, double *offset_x, double *offset_y) {
1351 double width, height;
1352 double scale = ((double)Zathura.PDF.scale/100.0);
1353 int window_x, window_y;
1355 Page *current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
1356 const double page_width = current_page->width;
1357 const double page_height = current_page->height;
1359 if (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180) {
1360 width = page_width*scale;
1361 height = page_height*scale;
1362 } else {
1363 width = page_height*scale;
1364 height = page_width*scale;
1367 gdk_drawable_get_size(widget->window, &window_x, &window_y);
1369 *offset_x = (window_x > width ? (window_x-width)/2 : 0);
1370 *offset_y = (window_y > height ? (window_y-height)/2 : 0);
1374 //==========================================================================
1376 // close_file
1378 //==========================================================================
1379 void close_file (gboolean keep_monitor) {
1380 if (!Zathura.PDF.document) return;
1382 /* clean up pages */
1383 for (int i = 0; i < Zathura.PDF.number_of_pages; ++i) {
1384 Page *current_page = Zathura.PDF.pages[i];
1385 g_object_unref(current_page->page);
1386 if (current_page->label) g_free(current_page->label);
1387 free(current_page);
1390 /* save bookmarks */
1391 if (Zathura.Bookmarks.data) {
1392 read_bookmarks_file();
1393 if (save_position) {
1394 /* set current page */
1395 g_key_file_set_integer(Zathura.Bookmarks.data, Zathura.PDF.file, bm_reserved_names[BM_PAGE_ENTRY], Zathura.PDF.page_number);
1396 /* set page offset */
1397 g_key_file_set_double(Zathura.Bookmarks.data, Zathura.PDF.file, bm_reserved_names[BM_PAGE_OFFSET], Zathura.PDF.page_yskip_t+Zathura.PDF.page_yskip_pix);
1399 if (save_zoom_level) {
1400 /* set zoom level */
1401 g_key_file_set_integer(Zathura.Bookmarks.data, Zathura.PDF.file, bm_reserved_names[BM_PAGE_SCALE], Zathura.PDF.scale);
1403 write_bookmarks_file();
1404 free_bookmarks();
1407 /* inotify */
1408 if (!keep_monitor) {
1409 g_object_unref(Zathura.FileMonitor.monitor);
1410 Zathura.FileMonitor.monitor = NULL;
1411 if (Zathura.FileMonitor.file) {
1412 g_object_unref(Zathura.FileMonitor.file);
1413 Zathura.FileMonitor.file = NULL;
1417 /* reset values */
1418 g_free(Zathura.PDF.pages);
1419 g_object_unref(Zathura.PDF.document);
1420 g_free(Zathura.State.pages);
1421 gtk_window_set_title(GTK_WINDOW(Zathura.UI.window), "zathura");
1423 Zathura.State.pages = g_strdup("");
1424 g_free(Zathura.State.filename);
1425 Zathura.State.filename = g_strdup((char *)default_text);
1427 MTLOCK_PDFOBJ();
1428 Zathura.PDF.document = NULL;
1430 if (!keep_monitor) {
1431 g_free(Zathura.PDF.file);
1432 g_free(Zathura.PDF.password);
1433 Zathura.PDF.file = NULL;
1434 Zathura.PDF.password = NULL;
1435 Zathura.PDF.page_number = 0;
1436 Zathura.PDF.scale = 0;
1437 Zathura.PDF.rotate = 0;
1439 Zathura.PDF.number_of_pages = 0;
1440 Zathura.PDF.page_yskip_t = 0;
1441 Zathura.PDF.page_yskip_pix = 0;
1442 Zathura.PDF.page_gap = 8;
1443 MTUNLOCK_PDFOBJ();
1445 surfCacheDestroy();
1447 /* destroy index */
1448 if (Zathura.UI.index) {
1449 gtk_widget_destroy(Zathura.UI.index);
1450 Zathura.UI.index = NULL;
1453 /* destroy information */
1454 if (Zathura.UI.information) {
1455 gtk_widget_destroy(Zathura.UI.information);
1456 Zathura.UI.information = NULL;
1459 /* free markers */
1460 if (Zathura.Marker.markers) free(Zathura.Marker.markers);
1461 Zathura.Marker.number_of_markers = 0;
1462 Zathura.Marker.last = -1;
1464 update_status();
1468 //==========================================================================
1470 // enter_password
1472 //==========================================================================
1473 void enter_password (void) {
1474 /* replace default inputbar handler */
1475 g_signal_handler_disconnect((gpointer) Zathura.UI.inputbar, Zathura.Handler.inputbar_activate);
1476 Zathura.Handler.inputbar_activate = g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "activate", G_CALLBACK(cb_inputbar_password_activate), NULL);
1478 Argument argument;
1479 argument.data = "Enter password: ";
1480 sc_focus_inputbar(&argument);
1484 //==========================================================================
1486 // eval_marker
1488 //==========================================================================
1489 void eval_marker (int id) {
1490 /* go to last marker */
1491 if (id == 0x27) {
1492 int current_page = Zathura.PDF.page_number;
1493 set_page(Zathura.Marker.last);
1494 Zathura.Marker.last = current_page;
1495 return;
1497 /* search markers */
1498 for (int i = 0; i < Zathura.Marker.number_of_markers; ++i) {
1499 if (Zathura.Marker.markers[i].id == id) {
1500 set_page(Zathura.Marker.markers[i].page);
1501 return;
1507 //==========================================================================
1509 // highlight_result
1511 //==========================================================================
1512 void highlight_result (int page_id, PopplerRectangle *rectangle) {
1513 PopplerRectangle *trect = poppler_rectangle_copy(rectangle);
1514 cairo_t *cairo = cairo_create(create_page_surface(page_id));
1515 cairo_set_source_rgba(cairo, Zathura.Style.search_highlight.red, Zathura.Style.search_highlight.green, Zathura.Style.search_highlight.blue, transparency);
1517 recalc_rectangle(page_id, trect);
1518 cairo_rectangle(cairo, trect->x1, trect->y1, trect->x2-trect->x1, trect->y2-trect->y1);
1519 poppler_rectangle_free(trect);
1520 cairo_fill(cairo);
1521 cairo_destroy(cairo);
1525 //==========================================================================
1527 // notify
1529 //==========================================================================
1530 void notify (int level, const char *message) {
1531 switch(level) {
1532 case ERROR:
1533 gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_e_bg));
1534 gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_e_fg));
1535 break;
1536 case WARNING:
1537 gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_w_bg));
1538 gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_w_fg));
1539 break;
1540 default:
1541 gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_bg));
1542 gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_fg));
1543 break;
1545 if (message) gtk_entry_set_text(Zathura.UI.inputbar, message);
1549 //==========================================================================
1551 // open_file
1553 //==========================================================================
1554 gboolean open_file (char *path, char *password) {
1555 MTLOCK_PDFOBJ();
1557 /* specify path max */
1558 size_t pm;
1559 #ifdef PATH_MAX
1560 pm = PATH_MAX;
1561 #else
1562 pm = pathconf(path, _PC_PATH_MAX);
1563 if (pm <= 0) pm = 4096;
1564 #endif
1566 char *rpath = NULL;
1567 if (path[0] == '~') {
1568 gchar *home_path = get_home_dir();
1569 rpath = g_build_filename(home_path, path+1, NULL);
1570 g_free(home_path);
1571 } else {
1572 rpath = g_strdup(path);
1575 /* get filename */
1576 char *file = (char *)g_malloc0(sizeof(char)*pm);
1577 if (!file) out_of_memory();
1579 if (!realpath(rpath, file)) {
1580 notify(ERROR, "File does not exist");
1581 g_free(file);
1582 g_free(rpath);
1583 MTUNLOCK_PDFOBJ();
1584 return FALSE;
1586 g_free(rpath);
1588 /* check if file exists */
1589 if (!g_file_test(file, G_FILE_TEST_IS_REGULAR)) {
1590 notify(ERROR, "File does not exist");
1591 g_free(file);
1592 MTUNLOCK_PDFOBJ();
1593 return FALSE;
1596 /* close old file */
1597 MTUNLOCK_PDFOBJ();
1598 close_file(FALSE);
1599 MTLOCK_PDFOBJ();
1601 /* format path */
1602 GError *error = NULL;
1603 char *file_uri = g_filename_to_uri(file, NULL, &error);
1604 if (!file_uri) {
1605 if (file) g_free(file);
1606 char *message = g_strdup_printf("Can not open file: %s", error->message);
1607 notify(ERROR, message);
1608 g_free(message);
1609 g_error_free(error);
1610 MTUNLOCK_PDFOBJ();
1611 return FALSE;
1614 /* open file */
1615 MTLOCK_PDFLIB();
1616 Zathura.PDF.document = poppler_document_new_from_file(file_uri, password, &error);
1617 MTUNLOCK_PDFLIB();
1619 if (!Zathura.PDF.document) {
1620 if (error->code == 1) {
1621 g_free(file_uri);
1622 g_error_free(error);
1623 Zathura.PDF.file = file;
1624 MTUNLOCK_PDFOBJ();
1625 enter_password();
1626 return FALSE;
1627 } else {
1628 char *message = g_strdup_printf("Can not open file: %s", error->message);
1629 notify(ERROR, message);
1630 g_free(file_uri);
1631 g_free(message);
1632 g_error_free(error);
1633 MTUNLOCK_PDFOBJ();
1634 return FALSE;
1638 /* save password */
1639 g_free(Zathura.PDF.password);
1640 Zathura.PDF.password = (password ? g_strdup(password) : NULL);
1642 /* inotify */
1643 if (!Zathura.FileMonitor.monitor) {
1644 GFile *file = g_file_new_for_uri(file_uri);
1645 if (file) {
1646 Zathura.FileMonitor.monitor = g_file_monitor_file(file, G_FILE_MONITOR_NONE, NULL, NULL);
1647 if (Zathura.FileMonitor.monitor) g_signal_connect(G_OBJECT(Zathura.FileMonitor.monitor), "changed", G_CALLBACK(cb_watch_file), NULL);
1648 Zathura.FileMonitor.file = file;
1652 g_free(file_uri);
1654 MTLOCK_PDFLIB();
1655 Zathura.PDF.number_of_pages = poppler_document_get_n_pages(Zathura.PDF.document);
1656 MTUNLOCK_PDFLIB();
1657 g_free(Zathura.PDF.file);
1658 Zathura.PDF.file = file;
1659 Zathura.PDF.scale = 100;
1660 Zathura.PDF.rotate = 0;
1661 Zathura.PDF.page_gap = 8;
1662 if (Zathura.State.filename) g_free(Zathura.State.filename);
1663 Zathura.State.filename = g_markup_escape_text(file, -1);
1664 Zathura.PDF.pages = g_malloc(Zathura.PDF.number_of_pages*sizeof(Page *));
1665 surfCacheDestroy();
1667 if (!Zathura.PDF.pages) out_of_memory();
1669 /* get pages and check label mode */
1670 MTLOCK_PDFLIB();
1671 Zathura.Global.enable_labelmode = FALSE;
1673 for (int i = 0; i < Zathura.PDF.number_of_pages; ++i) {
1674 Zathura.PDF.pages[i] = malloc(sizeof(Page));
1675 if (!Zathura.PDF.pages[i]) out_of_memory();
1677 Zathura.PDF.pages[i]->id = i+1;
1678 Zathura.PDF.pages[i]->page = poppler_document_get_page(Zathura.PDF.document, i);
1679 g_object_get(G_OBJECT(Zathura.PDF.pages[i]->page), "label", &(Zathura.PDF.pages[i]->label), NULL);
1681 /* check if it is necessary to use the label mode */
1682 int label_int = atoi(Zathura.PDF.pages[i]->label);
1683 if (label_int == 0 || label_int != i+1) Zathura.Global.enable_labelmode = TRUE;
1685 poppler_page_get_size(Zathura.PDF.pages[i]->page, &Zathura.PDF.pages[i]->width, &Zathura.PDF.pages[i]->height);
1686 if (Zathura.PDF.pages[i]->width < 1) Zathura.PDF.pages[i]->width = 1;
1687 if (Zathura.PDF.pages[i]->height < 1) Zathura.PDF.pages[i]->height = 1;
1688 //fprintf(stderr, "PAGE #%d: size=(%gx%g)\n", i, Zathura.PDF.pages[i]->width, Zathura.PDF.pages[i]->height);
1690 MTUNLOCK_PDFLIB();
1692 /* set correct goto mode */
1693 if (!Zathura.Global.enable_labelmode && GOTO_MODE == GOTO_LABELS) Zathura.Global.goto_mode = GOTO_DEFAULT;
1695 /* start page */
1696 int start_page = 0;
1697 Zathura.PDF.page_yskip_t = 0;
1698 Zathura.PDF.page_yskip_pix = 0;
1700 /* bookmarks */
1701 if (Zathura.Bookmarks.data && g_key_file_has_group(Zathura.Bookmarks.data, file)) {
1702 /* get last opened page */
1703 if (save_position && g_key_file_has_key(Zathura.Bookmarks.data, file, bm_reserved_names[BM_PAGE_ENTRY], NULL)) {
1704 start_page = g_key_file_get_integer(Zathura.Bookmarks.data, file, bm_reserved_names[BM_PAGE_ENTRY], NULL);
1706 /* get page offset */
1707 if (save_position && g_key_file_has_key(Zathura.Bookmarks.data, file, bm_reserved_names[BM_PAGE_OFFSET], NULL)) {
1708 double ofs = g_key_file_get_double(Zathura.Bookmarks.data, file, bm_reserved_names[BM_PAGE_OFFSET], NULL);
1709 if (ofs < 0) ofs = 0;
1710 int px = trunc(ofs);
1711 ofs -= px;
1712 if (px > Zathura.PDF.page_gap) px = Zathura.PDF.page_gap;
1713 Zathura.PDF.page_yskip_t = ofs;
1714 Zathura.PDF.page_yskip_pix = px;
1716 /* get zoom level */
1717 if (save_zoom_level && g_key_file_has_key(Zathura.Bookmarks.data, file, bm_reserved_names[BM_PAGE_SCALE], NULL)) {
1718 Zathura.PDF.scale = g_key_file_get_integer(Zathura.Bookmarks.data, file, bm_reserved_names[BM_PAGE_SCALE], NULL);
1719 Zathura.Global.adjust_mode = ADJUST_NONE;
1721 if (Zathura.PDF.scale > zoom_max) Zathura.PDF.scale = zoom_max;
1722 if (Zathura.PDF.scale < zoom_min) Zathura.PDF.scale = zoom_min;
1724 /* open and read bookmark file */
1725 gsize number_of_keys = 0;
1726 char **keys = g_key_file_get_keys(Zathura.Bookmarks.data, file, &number_of_keys, NULL);
1728 for (gsize i = 0; i < number_of_keys; ++i) {
1729 if (!is_reserved_bm_name(keys[i])) {
1730 Zathura.Bookmarks.bookmarks = safe_realloc((void **)&Zathura.Bookmarks.bookmarks, Zathura.Bookmarks.number_of_bookmarks+1, sizeof(Bookmark));
1731 if (!Zathura.Bookmarks.bookmarks) out_of_memory();
1733 Zathura.Bookmarks.bookmarks[Zathura.Bookmarks.number_of_bookmarks].id = g_strdup(keys[i]);
1734 Zathura.Bookmarks.bookmarks[Zathura.Bookmarks.number_of_bookmarks].page = g_key_file_get_integer(Zathura.Bookmarks.data, file, keys[i], NULL);
1736 ++Zathura.Bookmarks.number_of_bookmarks;
1740 g_strfreev(keys);
1743 /* set window title */
1744 gtk_window_set_title(GTK_WINDOW(Zathura.UI.window), basename(file));
1746 /* show document */
1747 set_page_keep_ofs(start_page);
1748 update_status();
1750 MTUNLOCK_PDFOBJ();
1751 isc_abort(NULL);
1752 return TRUE;
1756 //==========================================================================
1758 // open_stdin
1760 //==========================================================================
1761 gboolean open_stdin (gchar *password) {
1762 GError *error = NULL;
1763 gchar *file = NULL;
1764 gint handle = g_file_open_tmp("zathura.stdin.XXXXXX.pdf", &file, &error);
1765 if (handle == -1) {
1766 gchar *message = g_strdup_printf("Can not create temporary file: %s", error->message);
1767 notify(ERROR, message);
1768 g_free(message);
1769 g_error_free(error);
1770 return FALSE;
1773 // read from stdin and dump to temporary file
1774 int stdinfno = fileno(stdin);
1775 if (stdinfno == -1) {
1776 gchar *message = g_strdup_printf("Can not read from stdin.");
1777 notify(ERROR, message);
1778 g_free(message);
1779 close(handle);
1780 g_unlink(file);
1781 g_free(file);
1782 return FALSE;
1786 char buffer[BUFSIZ];
1787 ssize_t count = 0;
1788 while ((count = read(stdinfno, buffer, BUFSIZ)) > 0) {
1789 if (write(handle, buffer, count) != count) {
1790 gchar *message = g_strdup_printf("Can not write to temporary file: %s", file);
1791 notify(ERROR, message);
1792 g_free(message);
1793 close(handle);
1794 g_unlink(file);
1795 g_free(file);
1796 return FALSE;
1799 close(handle);
1801 if (count != 0) {
1802 gchar *message = g_strdup_printf("Can not read from stdin.");
1803 notify(ERROR, message);
1804 g_free(message);
1805 g_unlink(file);
1806 g_free(file);
1807 return FALSE;
1810 /* update data */
1811 if (Zathura.StdinSupport.file) g_unlink(Zathura.StdinSupport.file);
1812 g_free(Zathura.StdinSupport.file);
1813 Zathura.StdinSupport.file = file;
1815 return open_file(Zathura.StdinSupport.file, password);
1819 //==========================================================================
1821 // open_uri
1823 //==========================================================================
1824 void open_uri (char *uri) {
1825 char *escaped_uri = g_shell_quote(uri);
1826 char *uri_cmd = g_strdup_printf(uri_command, escaped_uri);
1827 system(uri_cmd);
1828 g_free(uri_cmd);
1829 g_free(escaped_uri);
1833 //==========================================================================
1835 // out_of_memory
1837 //==========================================================================
1838 void out_of_memory (void) {
1839 printf("error: out of memory\n");
1840 *(char*)0 = 0;
1841 exit(-1);
1845 //==========================================================================
1847 // update_status
1849 //==========================================================================
1850 void update_status (void) {
1851 /* update text */
1852 gtk_label_set_markup((GtkLabel *)Zathura.Global.status_text, Zathura.State.filename);
1853 /* update pages */
1854 if (Zathura.PDF.document && Zathura.PDF.pages) {
1855 int page = Zathura.PDF.page_number;
1856 g_free(Zathura.State.pages);
1857 Zathura.State.pages = g_strdup_printf("[%i/%i]", page+1, Zathura.PDF.number_of_pages);
1859 /* update state */
1860 char *zoom_level = (Zathura.PDF.scale != 0 ? g_strdup_printf("%d%%", Zathura.PDF.scale) : g_strdup(""));
1861 char *goto_mode = (Zathura.Global.goto_mode == GOTO_LABELS ? "L" : (Zathura.Global.goto_mode == GOTO_OFFSET ? "O" : "D"));
1862 char *status_text = g_strdup_printf("%s [%s] %s (%d%%)", zoom_level, goto_mode, Zathura.State.pages, Zathura.State.scroll_percentage);
1863 gtk_label_set_markup((GtkLabel *)Zathura.Global.status_state, status_text);
1864 g_free(status_text);
1865 g_free(zoom_level);
1869 //==========================================================================
1871 // read_bookmarks_file
1873 //==========================================================================
1874 void read_bookmarks_file (void) {
1875 /* free it at first */
1876 if (Zathura.Bookmarks.data) g_key_file_free(Zathura.Bookmarks.data);
1877 /* create or open existing bookmark file */
1878 Zathura.Bookmarks.data = g_key_file_new();
1879 if (!g_file_test(Zathura.Bookmarks.file, G_FILE_TEST_IS_REGULAR)) {
1880 /* file does not exist */
1881 g_file_set_contents(Zathura.Bookmarks.file, "# Zathura bookmarks\n", -1, NULL);
1883 GError *error = NULL;
1884 if (!g_key_file_load_from_file(Zathura.Bookmarks.data, Zathura.Bookmarks.file, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
1885 gchar *message = g_strdup_printf("Could not load bookmark file: %s", error->message);
1886 notify(ERROR, message);
1887 g_free(message);
1892 //==========================================================================
1894 // write_bookmarks_file
1896 //==========================================================================
1897 void write_bookmarks_file (void) {
1898 if (!Zathura.Bookmarks.data) return; /* nothing to do */
1899 /* save bookmarks */
1900 for (int i = 0; i < Zathura.Bookmarks.number_of_bookmarks; ++i) {
1901 g_key_file_set_integer(Zathura.Bookmarks.data, Zathura.PDF.file, Zathura.Bookmarks.bookmarks[i].id, Zathura.Bookmarks.bookmarks[i].page);
1903 /* convert file and save it */
1904 gchar *bookmarks = g_key_file_to_data(Zathura.Bookmarks.data, NULL, NULL);
1905 g_file_set_contents(Zathura.Bookmarks.file, bookmarks, -1, NULL);
1906 g_free(bookmarks);
1910 //==========================================================================
1912 // free_bookmarks
1914 //==========================================================================
1915 void free_bookmarks (void) {
1916 for (int i = 0; i < Zathura.Bookmarks.number_of_bookmarks; ++i) g_free(Zathura.Bookmarks.bookmarks[i].id);
1917 free(Zathura.Bookmarks.bookmarks);
1918 Zathura.Bookmarks.bookmarks = NULL;
1919 Zathura.Bookmarks.number_of_bookmarks = 0;
1923 //==========================================================================
1925 // read_configuration_file
1927 //==========================================================================
1928 void read_configuration_file (const char *rcfile) {
1929 if (!rcfile) return;
1930 if (!g_file_test(rcfile, G_FILE_TEST_IS_REGULAR)) return;
1931 char *content = NULL;
1932 if (g_file_get_contents(rcfile, &content, NULL, NULL)) {
1933 gchar **lines = g_strsplit(content, "\n", -1);
1934 int n = g_strv_length(lines)-1;
1935 for (int i = 0; i <= n; ++i) {
1936 if (!strlen(lines[i])) continue;
1937 gchar **pre_tokens = g_strsplit_set(lines[i], "\t ", -1);
1938 int pre_length = g_strv_length(pre_tokens);
1939 gchar** tokens = g_malloc0(sizeof(gchar*)*(pre_length+1));
1940 gchar** tokp = tokens;
1941 int length = 0;
1942 for (int f = 0; f != pre_length; ++f) {
1943 if (strlen(pre_tokens[f])) {
1944 *tokp++ = pre_tokens[f];
1945 ++length;
1948 if (!strcmp(tokens[0], "set")) cmd_set(length-1, tokens+1);
1949 else if (!strcmp(tokens[0], "map")) cmd_map(length-1, tokens+1);
1950 g_free(tokens);
1952 g_strfreev(lines);
1953 g_free(content);
1958 //==========================================================================
1960 // read_configuration
1962 //==========================================================================
1963 void read_configuration (void) {
1964 char *zathurarc = g_build_filename(Zathura.Config.config_dir, ZATHURA_RC, NULL);
1965 read_configuration_file(GLOBAL_RC);
1966 read_configuration_file(zathurarc);
1967 g_free(zathurarc);
1971 //==========================================================================
1973 // recalc_rectangle
1975 //==========================================================================
1976 void recalc_rectangle (int page_id, PopplerRectangle* rectangle) {
1977 double x1 = rectangle->x1;
1978 double x2 = rectangle->x2;
1979 double y1 = rectangle->y1;
1980 double y2 = rectangle->y2;
1981 Page *current_page = Zathura.PDF.pages[page_id];
1982 const double page_width = current_page->width;
1983 const double page_height = current_page->height;
1985 double scale = ((double) Zathura.PDF.scale/100.0);
1986 int rotate = Zathura.PDF.rotate;
1987 switch (rotate) {
1988 case 90:
1989 rectangle->x1 = y2*scale;
1990 rectangle->y1 = x1*scale;
1991 rectangle->x2 = y1*scale;
1992 rectangle->y2 = x2*scale;
1993 break;
1994 case 180:
1995 rectangle->x1 = (page_width-x2)*scale;
1996 rectangle->y1 = y2*scale;
1997 rectangle->x2 = (page_width-x1)*scale;
1998 rectangle->y2 = y1*scale;
1999 break;
2000 case 270:
2001 rectangle->x1 = (page_height-y1)*scale;
2002 rectangle->y1 = (page_width-x2)*scale;
2003 rectangle->x2 = (page_height-y2)*scale;
2004 rectangle->y2 = (page_width-x1)*scale;
2005 break;
2006 default:
2007 rectangle->x1 = x1*scale;
2008 rectangle->y1 = (page_height-y1)*scale;
2009 rectangle->x2 = x2*scale;
2010 rectangle->y2 = (page_height-y2)*scale;
2011 break;
2016 //==========================================================================
2018 // create_completion_row
2020 //==========================================================================
2021 GtkEventBox *create_completion_row (GtkBox *results, char *command, char *description, gboolean group) {
2022 GtkBox *col = GTK_BOX(gtk_hbox_new(FALSE, 0));
2023 GtkEventBox *row = GTK_EVENT_BOX(gtk_event_box_new());
2024 GtkLabel *show_command = GTK_LABEL(gtk_label_new(NULL));
2025 GtkLabel *show_description = GTK_LABEL(gtk_label_new(NULL));
2026 gtk_misc_set_alignment(GTK_MISC(show_command), 0.0, 0.0);
2027 gtk_misc_set_alignment(GTK_MISC(show_description), 0.0, 0.0);
2028 if (group) {
2029 gtk_misc_set_padding(GTK_MISC(show_command), 2.0, 4.0);
2030 gtk_misc_set_padding(GTK_MISC(show_description), 2.0, 4.0);
2031 } else {
2032 gtk_misc_set_padding(GTK_MISC(show_command), 1.0, 1.0);
2033 gtk_misc_set_padding(GTK_MISC(show_description), 1.0, 1.0);
2035 gtk_label_set_use_markup(show_command, TRUE);
2036 gtk_label_set_use_markup(show_description, TRUE);
2037 gchar *c = g_markup_printf_escaped(FORMAT_COMMAND, (command ? command : ""));
2038 gchar *d = g_markup_printf_escaped(FORMAT_DESCRIPTION, (description ? description : ""));
2039 gtk_label_set_markup(show_command, c);
2040 gtk_label_set_markup(show_description, d);
2041 g_free(c);
2042 g_free(d);
2043 if (group) {
2044 gtk_widget_modify_fg(GTK_WIDGET(show_command), GTK_STATE_NORMAL, &(Zathura.Style.completion_g_fg));
2045 gtk_widget_modify_fg(GTK_WIDGET(show_description), GTK_STATE_NORMAL, &(Zathura.Style.completion_g_fg));
2046 gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_g_bg));
2047 } else {
2048 gtk_widget_modify_fg(GTK_WIDGET(show_command), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
2049 gtk_widget_modify_fg(GTK_WIDGET(show_description), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
2050 gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_bg));
2052 gtk_widget_modify_font(GTK_WIDGET(show_command), Zathura.Style.font);
2053 gtk_widget_modify_font(GTK_WIDGET(show_description), Zathura.Style.font);
2054 gtk_box_pack_start(GTK_BOX(col), GTK_WIDGET(show_command), TRUE, TRUE, 2);
2055 gtk_box_pack_start(GTK_BOX(col), GTK_WIDGET(show_description), FALSE, FALSE, 2);
2056 gtk_container_add(GTK_CONTAINER(row), GTK_WIDGET(col));
2057 gtk_box_pack_start(results, GTK_WIDGET(row), FALSE, FALSE, 0);
2058 return row;
2062 //==========================================================================
2064 // set_completion_row_color
2066 //==========================================================================
2067 void set_completion_row_color (GtkBox *results, int mode, int id) {
2068 GtkEventBox *row = (GtkEventBox *)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(results)), id);
2069 if (row) {
2070 GtkBox *col = (GtkBox *)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(row)), 0);
2071 GtkLabel *cmd = (GtkLabel *)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(col)), 0);
2072 GtkLabel *cdesc = (GtkLabel *)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(col)), 1);
2073 if (mode == NORMAL) {
2074 gtk_widget_modify_fg(GTK_WIDGET(cmd), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
2075 gtk_widget_modify_fg(GTK_WIDGET(cdesc), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
2076 gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_bg));
2077 } else {
2078 gtk_widget_modify_fg(GTK_WIDGET(cmd), GTK_STATE_NORMAL, &(Zathura.Style.completion_hl_fg));
2079 gtk_widget_modify_fg(GTK_WIDGET(cdesc), GTK_STATE_NORMAL, &(Zathura.Style.completion_hl_fg));
2080 gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_hl_bg));
2086 //==========================================================================
2088 // set_page_keep_ofs
2090 // used in file open function to set the page, but don't reset the offsets
2092 //==========================================================================
2093 void set_page_keep_ofs (int page) {
2094 if (page >= Zathura.PDF.number_of_pages || page < 0) {
2095 notify(WARNING, "Could not open page");
2096 return;
2098 Zathura.PDF.page_number = page;
2099 Zathura.Search.draw = FALSE;
2100 switch_view(Zathura.UI.document);
2101 draw(page);
2105 //==========================================================================
2107 // set_page
2109 //==========================================================================
2110 void set_page (int page) {
2111 if (page >= Zathura.PDF.number_of_pages || page < 0) {
2112 notify(WARNING, "Could not open page");
2113 return;
2115 Zathura.PDF.page_number = page;
2116 Zathura.PDF.page_yskip_t = 0;
2117 Zathura.PDF.page_yskip_pix = 0;
2118 Zathura.Search.draw = FALSE;
2119 Argument argument;
2120 argument.n = TOP;
2121 switch_view(Zathura.UI.document);
2122 draw(page);
2123 sc_scroll(&argument);
2127 //==========================================================================
2129 // switch_view
2131 //==========================================================================
2132 void switch_view (GtkWidget *widget) {
2133 GtkWidget *child = gtk_bin_get_child(GTK_BIN(Zathura.UI.viewport));
2134 if (child == widget) return;
2135 if (child) {
2136 g_object_ref(child);
2137 gtk_container_remove(GTK_CONTAINER(Zathura.UI.viewport), child);
2139 gtk_container_add(GTK_CONTAINER(Zathura.UI.viewport), GTK_WIDGET(widget));
2143 //==========================================================================
2145 // completion_init
2147 //==========================================================================
2148 Completion *completion_init (void) {
2149 Completion *completion = malloc(sizeof(Completion));
2150 if (!completion) out_of_memory();
2151 completion->groups = NULL;
2152 return completion;
2156 //==========================================================================
2158 // completion_group_create
2160 //==========================================================================
2161 CompletionGroup *completion_group_create (char *name) {
2162 CompletionGroup *group = malloc(sizeof(CompletionGroup));
2163 if (!group) out_of_memory();
2164 group->value = (name ? g_strdup(name) : NULL);
2165 group->elements = NULL;
2166 group->next = NULL;
2167 return group;
2171 //==========================================================================
2173 // completion_add_group
2175 //==========================================================================
2176 void completion_add_group (Completion *completion, CompletionGroup *group) {
2177 CompletionGroup *cg = completion->groups;
2178 while (cg && cg->next) cg = cg->next;
2179 if (cg) cg->next = group; else completion->groups = group;
2183 //==========================================================================
2185 // completion_free
2187 //==========================================================================
2188 void completion_free (Completion *completion) {
2189 CompletionGroup *group = completion->groups;
2190 while (group) {
2191 CompletionGroup *ng;
2192 CompletionElement *element = group->elements;
2193 while (element) {
2194 CompletionElement *ne = element->next;
2195 g_free(element->value);
2196 g_free(element->description);
2197 free(element);
2198 element = ne;
2200 ng = group->next;
2201 g_free(group->value);
2202 free(group);
2203 group = ng;
2205 free(completion);
2209 //==========================================================================
2211 // completion_group_add_element
2213 //==========================================================================
2214 void completion_group_add_element (CompletionGroup *group, char *name, char *description) {
2215 CompletionElement *el = group->elements;
2216 while (el && el->next) el = el->next;
2217 CompletionElement *new_element = malloc(sizeof(CompletionElement));
2218 if (!new_element) out_of_memory();
2219 new_element->value = (name ? g_strdup(name) : NULL);
2220 new_element->description = (description ? g_strdup(description) : NULL);
2221 new_element->next = NULL;
2222 if (el) el->next = new_element; else group->elements = new_element;
2226 //==========================================================================
2228 // search
2230 // thread implementation
2232 //==========================================================================
2233 void *search (void *parameter) {
2234 Argument *argument = (Argument *)parameter;
2235 static char *search_item;
2236 static int direction;
2237 static int next_page = 0;
2238 gchar *old_query = NULL;
2239 GList *results = NULL;
2240 if (argument->n != NO_SEARCH) {
2241 /* search document */
2242 if (argument->n) direction = (argument->n == BACKWARD ? -1 : 1);
2243 if (argument->data) {
2244 if (search_item) g_free(search_item);
2245 search_item = g_strdup((char *)argument->data);
2247 g_free(argument->data);
2248 g_free(argument);
2250 MTLOCK_PDFOBJ();
2251 if (!Zathura.PDF.document || !search_item || !strlen(search_item)) {
2252 MTUNLOCK_PDFOBJ();
2253 MTLOCK_SEARCH();
2254 Zathura.Thread.search_thread_running = FALSE;
2255 MTUNLOCK_SEARCH();
2256 g_thread_exit(NULL);
2259 old_query = Zathura.Search.query;
2261 /* delete old results */
2262 if (Zathura.Search.results) {
2263 g_list_free(Zathura.Search.results);
2264 Zathura.Search.results = NULL;
2267 Zathura.Search.query = g_strdup(search_item);
2269 int number_of_pages = Zathura.PDF.number_of_pages;
2270 int page_number = Zathura.PDF.page_number;
2272 MTUNLOCK_PDFOBJ();
2274 int page_counter = (g_strcmp0(old_query,search_item) == 0 ? 1 : 0);
2275 for( ; page_counter <= number_of_pages; ++page_counter) {
2276 MTLOCK_SEARCH();
2277 if (Zathura.Thread.search_thread_running == FALSE) {
2278 MTUNLOCK_SEARCH();
2279 g_free(old_query);
2280 g_thread_exit(NULL);
2282 MTUNLOCK_SEARCH();
2284 next_page = (number_of_pages+page_number+page_counter*direction)%number_of_pages;
2286 MTLOCK_PDFLIB();
2287 PopplerPage *page = poppler_document_get_page(Zathura.PDF.document, next_page);
2288 MTUNLOCK_PDFLIB();
2290 if (!page) {
2291 g_free(old_query);
2292 g_thread_exit(NULL);
2295 MTLOCK_PDFLIB();
2296 results = poppler_page_find_text(page, search_item);
2297 MTUNLOCK_PDFLIB();
2299 g_object_unref(page);
2301 if (results) break;
2303 } else {
2304 Zathura.Search.draw = TRUE;
2305 g_free(argument->data);
2306 g_free(argument);
2309 /* draw results */
2310 if (results) {
2311 gdk_threads_enter();
2313 set_page(next_page);
2315 if (Zathura.Search.results) g_list_free(Zathura.Search.results);
2317 Zathura.Search.results = results;
2318 Zathura.Search.page = next_page;
2319 Zathura.Search.draw = TRUE;
2320 Zathura.Search.query = g_strdup(search_item);
2322 gdk_threads_leave();
2325 MTLOCK_SEARCH();
2326 Zathura.Thread.search_thread_running = FALSE;
2327 MTUNLOCK_SEARCH();
2329 g_free(old_query);
2330 g_thread_exit(NULL);
2331 return NULL;
2335 //==========================================================================
2337 // sc_abort
2339 // shortcut implementation
2341 //==========================================================================
2342 void sc_abort (Argument *argument) {
2343 /* Clear buffer */
2344 if (Zathura.Global.buffer) {
2345 g_string_free(Zathura.Global.buffer, TRUE);
2346 Zathura.Global.buffer = NULL;
2347 gtk_label_set_markup((GtkLabel *)Zathura.Global.status_buffer, "");
2350 if (!Zathura.Global.show_inputbar) gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
2352 /* Set back to normal mode */
2353 change_mode(NORMAL);
2354 switch_view(Zathura.UI.document);
2358 //==========================================================================
2360 // sc_adjust_window
2362 //==========================================================================
2363 void sc_adjust_window (Argument *argument) {
2364 //fprintf(stderr, "RESIZED!\n");
2365 surfCacheDestroy(); // why not?
2367 if (!Zathura.PDF.document) return;
2368 Zathura.Global.adjust_mode = argument->n;
2369 GtkAdjustment *adjustment;
2370 double view_size;
2371 if (argument->n == ADJUST_BESTFIT) {
2372 adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
2373 } else if (argument->n == ADJUST_WIDTH) {
2374 adjustment = gtk_scrolled_window_get_hadjustment(Zathura.UI.view);
2375 } else {
2376 return;
2378 view_size = gtk_adjustment_get_page_size(adjustment);
2380 Page *current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
2381 double page_width = current_page->width;
2382 double page_height = current_page->height;
2384 if (Zathura.PDF.rotate == 90 || Zathura.PDF.rotate == 270) {
2385 double swap = page_width;
2386 page_width = page_height;
2387 page_height = swap;
2390 if (argument->n == ADJUST_BESTFIT) {
2391 Zathura.PDF.scale = (view_size/page_height)*100;
2392 } else {
2393 Zathura.PDF.scale = (view_size/page_width)*100;
2396 draw(Zathura.PDF.page_number);
2397 update_status();
2401 //==========================================================================
2403 // sc_xcenter_window
2405 //==========================================================================
2406 void sc_xcenter_window (Argument *argument) {
2407 if (!Zathura.PDF.document) return;
2408 Zathura.Global.xcenter_mode = argument->n;
2409 draw(Zathura.PDF.page_number);
2410 update_status();
2414 //==========================================================================
2416 // sc_change_buffer
2418 //==========================================================================
2419 void sc_change_buffer (Argument *argument) {
2420 if (!Zathura.Global.buffer) return;
2421 int buffer_length = Zathura.Global.buffer->len;
2422 if (argument->n == DELETE_LAST) {
2423 if (buffer_length-1 == 0) {
2424 g_string_free(Zathura.Global.buffer, TRUE);
2425 Zathura.Global.buffer = NULL;
2426 gtk_label_set_markup((GtkLabel*) Zathura.Global.status_buffer, "");
2427 } else {
2428 GString *temp = g_string_new_len(Zathura.Global.buffer->str, buffer_length-1);
2429 g_string_free(Zathura.Global.buffer, TRUE);
2430 Zathura.Global.buffer = temp;
2431 gtk_label_set_markup((GtkLabel *)Zathura.Global.status_buffer, Zathura.Global.buffer->str);
2437 //==========================================================================
2439 // sc_change_mode
2441 //==========================================================================
2442 void sc_change_mode (Argument *argument) {
2443 if (argument) change_mode(argument->n);
2447 //==========================================================================
2449 // sc_focus_inputbar
2451 //==========================================================================
2452 void sc_focus_inputbar (Argument *argument) {
2453 if (!(GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura.UI.inputbar)))) gtk_widget_show(GTK_WIDGET(Zathura.UI.inputbar));
2454 if (argument->data) {
2455 char *data;
2456 if (argument->n == APPEND_FILEPATH) {
2457 data = g_strdup_printf("%s%s", (const char *)argument->data, Zathura.PDF.file);
2458 } else {
2459 data = g_strdup((const char *)argument->data);
2461 notify(DEFAULT, data);
2462 g_free(data);
2463 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.inputbar));
2464 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
2469 //==========================================================================
2471 // sc_follow
2473 //==========================================================================
2474 void sc_follow (Argument *argument) {
2475 if (!Zathura.PDF.document) return;
2476 Page *current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
2477 int link_id = 1;
2479 MTLOCK_PDFLIB();
2480 GList *link_list = poppler_page_get_link_mapping(current_page->page);
2481 MTUNLOCK_PDFLIB();
2482 link_list = g_list_reverse(link_list);
2484 if (g_list_length(link_list) <= 0) return;
2486 for (GList *links = link_list; links; links = g_list_next(links)) {
2487 PopplerLinkMapping *link_mapping = (PopplerLinkMapping *)links->data;
2488 PopplerRectangle *link_rectangle = &link_mapping->area;
2489 PopplerAction *action = link_mapping->action;
2491 /* only handle URI and internal links */
2492 if (action->type == POPPLER_ACTION_URI || action->type == POPPLER_ACTION_GOTO_DEST) {
2493 highlight_result(Zathura.PDF.page_number, link_rectangle);
2494 /* draw text */
2495 recalc_rectangle(Zathura.PDF.page_number, link_rectangle);
2496 cairo_t *cairo = cairo_create(create_page_surface(Zathura.PDF.page_number));
2497 cairo_select_font_face(cairo, font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
2498 cairo_set_font_size(cairo, 10);
2499 cairo_move_to(cairo, link_rectangle->x1+1, link_rectangle->y1-1);
2500 char *link_number = g_strdup_printf("%i", link_id++);
2501 cairo_show_text(cairo, link_number);
2502 cairo_destroy(cairo);
2503 g_free(link_number);
2507 gtk_widget_queue_draw(Zathura.UI.drawing_area);
2508 poppler_page_free_link_mapping(link_list);
2510 /* replace default inputbar handler */
2511 g_signal_handler_disconnect((gpointer) Zathura.UI.inputbar, Zathura.Handler.inputbar_activate);
2512 Zathura.Handler.inputbar_activate = g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "activate", G_CALLBACK(cb_inputbar_form_activate), NULL);
2514 argument->data = "Follow hint: ";
2515 sc_focus_inputbar(argument);
2519 //==========================================================================
2521 // sc_navigate
2523 //==========================================================================
2524 void sc_navigate (Argument *argument) {
2525 if (!Zathura.PDF.document) return;
2527 int number_of_pages = Zathura.PDF.number_of_pages;
2528 int new_page = Zathura.PDF.page_number;
2530 if (argument->n == NEXT) {
2531 new_page = (scroll_wrap ? (new_page+1)%number_of_pages : new_page+1);
2532 } else if (argument->n == PREVIOUS) {
2533 new_page = (scroll_wrap ? (new_page+number_of_pages-1)%number_of_pages : new_page-1);
2535 if (!scroll_wrap && (new_page < 0 || new_page >= number_of_pages)) return;
2537 set_page(new_page);
2538 update_status();
2542 //==========================================================================
2544 // sc_recolor
2546 //==========================================================================
2547 void sc_recolor (Argument *argument) {
2548 Zathura.Global.recolor = !Zathura.Global.recolor;
2549 draw(Zathura.PDF.page_number);
2553 //==========================================================================
2555 // sc_reload
2557 //==========================================================================
2558 void sc_reload (Argument *argument) {
2559 draw(Zathura.PDF.page_number);
2561 GtkAdjustment *vadjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
2562 GtkAdjustment *hadjustment = gtk_scrolled_window_get_hadjustment(Zathura.UI.view);
2564 /* save old information */
2565 MTLOCK_PDFOBJ();
2566 char *path = (Zathura.PDF.file ? strdup(Zathura.PDF.file) : NULL);
2567 char *password = (Zathura.PDF.password ? strdup(Zathura.PDF.password) : NULL);
2568 int scale = Zathura.PDF.scale;
2569 int page = Zathura.PDF.page_number;
2570 int rotate = Zathura.PDF.rotate;
2571 gdouble va = gtk_adjustment_get_value(vadjustment);
2572 gdouble ha = gtk_adjustment_get_value(hadjustment);
2573 MTUNLOCK_PDFOBJ();
2575 /* reopen and restore settings */
2576 close_file(TRUE);
2577 open_file(path, password);
2579 MTLOCK_PDFOBJ();
2580 Zathura.PDF.scale = scale;
2581 Zathura.PDF.rotate = rotate;
2582 va = ha = 0;
2583 gtk_adjustment_set_value(vadjustment, va);
2584 gtk_adjustment_set_value(hadjustment, ha);
2585 MTUNLOCK_PDFOBJ();
2587 if (Zathura.PDF.number_of_pages != 0) {
2588 if (page >= Zathura.PDF.number_of_pages-1) page = Zathura.PDF.number_of_pages-1;
2589 Zathura.PDF.page_number = page;
2590 draw(Zathura.PDF.page_number);
2593 if (path) free(path);
2594 if (password) free(password);
2598 //==========================================================================
2600 // sc_rotate
2602 //==========================================================================
2603 void sc_rotate (Argument *argument) {
2604 Zathura.PDF.rotate = (Zathura.PDF.rotate+90)%360;
2605 Zathura.Search.draw = TRUE;
2606 draw(Zathura.PDF.page_number);
2610 //==========================================================================
2612 // sc_scroll
2614 //==========================================================================
2615 void sc_scroll (Argument *argument) {
2616 GtkAdjustment *adjustment;
2618 if (argument->n == LEFT || argument->n == RIGHT) {
2619 adjustment = gtk_scrolled_window_get_hadjustment(Zathura.UI.view);
2620 } else {
2621 adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
2624 if (argument->n == WINDOW_UP || argument->n == WINDOW_DOWN ||
2625 argument->n == UP || argument->n == DOWN)
2627 adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
2628 int window_y = gtk_adjustment_get_page_size(adjustment);
2629 int pgdelta = window_y/6;
2630 if (pgdelta < 2) return; // are you nuts?
2631 int delta = (argument->n == WINDOW_UP || argument->n == WINDOW_DOWN ? window_y-pgdelta : 64);
2632 if (argument->n == WINDOW_UP || argument->n == UP) {
2633 scroll_up_pixels_no_draw(delta);
2634 } else {
2635 scroll_down_pixels_no_draw(delta);
2637 int top_page = Zathura.PDF.page_number;
2638 // setup new page
2639 if (top_page >= 0 && top_page < Zathura.PDF.number_of_pages) {
2640 Zathura.Search.draw = TRUE;
2641 draw(top_page);
2642 gtk_adjustment_set_value(adjustment, 0);
2643 update_status();
2645 return;
2649 //gdouble view_size = gtk_adjustment_get_page_size(adjustment);
2650 gdouble value = gtk_adjustment_get_value(adjustment);
2651 //gdouble max = gtk_adjustment_get_upper(adjustment)-view_size;
2652 //static gboolean ss = FALSE;
2653 if (value != 0) {
2654 value = 0;
2655 gtk_adjustment_set_value(adjustment, 0);
2658 if ((argument->n == UP || argument->n == HALF_UP || argument->n == FULL_UP) && value == 0) {
2659 int old_page = Zathura.PDF.page_number;
2660 Argument arg;
2661 arg.n = PREVIOUS;
2662 sc_navigate(&arg);
2663 if (scroll_wrap || Zathura.PDF.page_number < old_page) {
2664 arg.n = BOTTOM;
2665 //ss = TRUE;
2666 sc_scroll(&arg);
2668 return;
2669 } else if ((argument->n == DOWN || argument->n == HALF_DOWN || argument->n == FULL_DOWN) /*&& value == max*/) {
2670 Argument arg;
2671 arg.n = NEXT;
2672 //ss = TRUE;
2673 sc_navigate(&arg);
2674 return;
2678 gdouble new_value;
2679 switch (argument->n) {
2680 case FULL_UP: new_value = (value-view_size < 0 ? 0 : value-view_size); break;
2681 case FULL_DOWN: new_value = (value+view_size > max ? max : value+view_size); break;
2682 case HALF_UP: new_value = (value-view_size/2 < 0 ? 0 : value-view_size/2); break;
2683 case HALF_DOWN: new_value = (value+view_size/2 > max ? max : value+view_size/2); break;
2684 case LEFT: case UP: new_value = (value-scroll_step < 0 ? 0 : value-scroll_step); break;
2685 case TOP: new_value = 0; break;
2686 case BOTTOM: new_value = max; break;
2687 default: new_value = (value+scroll_step > max ? max : value+scroll_step); break;
2690 if (!(argument->n == LEFT || argument->n == RIGHT)) Zathura.State.scroll_percentage = (max == 0 ? 0 : new_value*100/max);
2692 if (smooth_scrolling && !ss) {
2693 gdouble i;
2694 if (new_value > value) {
2695 for (i = value; i+smooth_scrolling < new_value; i += smooth_scrolling) gtk_adjustment_set_value(adjustment, i);
2696 } else {
2697 for (i = value; i+smooth_scrolling > new_value; i -= smooth_scrolling) gtk_adjustment_set_value(adjustment, i);
2701 gtk_adjustment_set_value(adjustment, new_value);
2702 ss = FALSE;
2705 update_status();
2709 //==========================================================================
2711 // sc_search
2713 //==========================================================================
2714 void sc_search (Argument *argument) {
2715 MTLOCK_SEARCH();
2716 if (Zathura.Thread.search_thread_running) {
2717 Zathura.Thread.search_thread_running = FALSE;
2718 MTUNLOCK_SEARCH();
2719 gdk_threads_leave();
2720 g_thread_join(Zathura.Thread.search_thread);
2721 gdk_threads_enter();
2722 MTLOCK_SEARCH();
2725 Argument *newarg = g_malloc0(sizeof(Argument));
2726 newarg->n = argument->n;
2727 newarg->data = (argument->data ? g_strdup(argument->data) : NULL);
2728 Zathura.Thread.search_thread_running = TRUE;
2729 Zathura.Thread.search_thread = g_thread_create(search, (gpointer) newarg, TRUE, NULL);
2730 MTUNLOCK_SEARCH();
2734 //==========================================================================
2736 // sc_switch_goto_mode
2738 //==========================================================================
2739 void sc_switch_goto_mode (Argument *argument) {
2740 switch (Zathura.Global.goto_mode) {
2741 case GOTO_LABELS: Zathura.Global.goto_mode = GOTO_OFFSET; break;
2742 case GOTO_OFFSET: Zathura.Global.goto_mode = GOTO_DEFAULT; break;
2743 default:
2744 if (Zathura.Global.enable_labelmode) Zathura.Global.goto_mode = GOTO_LABELS; else Zathura.Global.goto_mode = GOTO_OFFSET;
2745 break;
2747 update_status();
2751 //==========================================================================
2753 // cb_index_row_activated
2755 //==========================================================================
2756 gboolean cb_index_row_activated (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) {
2757 GtkTreeModel *model;
2758 GtkTreeIter iter;
2760 g_object_get(treeview, "model", &model, NULL);
2762 if (gtk_tree_model_get_iter(model, &iter, path)) {
2763 PopplerAction *action;
2764 PopplerDest *destination;
2766 gtk_tree_model_get(model, &iter, 1, &action, -1);
2767 if (!action) return TRUE;
2769 if (action->type == POPPLER_ACTION_GOTO_DEST) {
2770 destination = action->goto_dest.dest;
2771 int page_number = destination->page_num;
2772 if (action->goto_dest.dest->type == POPPLER_DEST_NAMED) {
2773 PopplerDest *d = poppler_document_find_dest(Zathura.PDF.document, action->goto_dest.dest->named_dest);
2774 if (d) {
2775 page_number = d->page_num;
2776 poppler_dest_free(d);
2779 set_page(page_number-1);
2780 update_status();
2781 Zathura.Global.show_index = FALSE;
2782 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.document));
2786 Zathura.Global.mode = NORMAL;
2787 g_object_unref(model);
2789 return TRUE;
2793 //==========================================================================
2795 // sc_navigate_index
2797 //==========================================================================
2798 void sc_navigate_index (Argument *argument) {
2799 if (!Zathura.UI.index) return;
2801 GtkTreeView *treeview = gtk_container_get_children(GTK_CONTAINER(Zathura.UI.index))->data;
2802 GtkTreePath *path;
2804 gtk_tree_view_get_cursor(treeview, &path, NULL);
2805 if (!path) return;
2807 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
2808 GtkTreeIter iter;
2809 GtkTreeIter child_iter;
2811 gboolean is_valid_path = TRUE;
2813 switch (argument->n) {
2814 case UP:
2815 if (!gtk_tree_path_prev(path)) {
2816 is_valid_path = (gtk_tree_path_get_depth(path) > 1 && gtk_tree_path_up(path));
2817 } else {
2818 /* row above */
2819 while (gtk_tree_view_row_expanded(treeview, path)) {
2820 gtk_tree_model_get_iter(model, &iter, path);
2821 /* select last child */
2822 gtk_tree_model_iter_nth_child(model, &child_iter, &iter, gtk_tree_model_iter_n_children(model, &iter)-1);
2823 gtk_tree_path_free(path);
2824 path = gtk_tree_model_get_path(model, &child_iter);
2827 break;
2828 case COLLAPSE:
2829 if (!gtk_tree_view_collapse_row(treeview, path) && gtk_tree_path_get_depth(path) > 1) {
2830 gtk_tree_path_up(path);
2831 gtk_tree_view_collapse_row(treeview, path);
2833 break;
2834 case DOWN:
2835 if (gtk_tree_view_row_expanded(treeview, path)) {
2836 gtk_tree_path_down(path);
2837 } else {
2838 do {
2839 gtk_tree_model_get_iter(model, &iter, path);
2840 if (gtk_tree_model_iter_next(model, &iter)) {
2841 path = gtk_tree_model_get_path(model, &iter);
2842 break;
2844 } while ((is_valid_path = (gtk_tree_path_get_depth(path) > 1)) && gtk_tree_path_up(path));
2846 break;
2847 case EXPAND:
2848 if (gtk_tree_view_expand_row(treeview, path, FALSE)) gtk_tree_path_down(path);
2849 break;
2850 case SELECT:
2851 cb_index_row_activated(treeview, path, NULL, NULL);
2852 return;
2855 if (is_valid_path) gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
2857 gtk_tree_path_free(path);
2861 //==========================================================================
2863 // sc_toggle_index
2865 //==========================================================================
2866 void sc_toggle_index (Argument *argument) {
2867 if (!Zathura.PDF.document) return;
2869 GtkWidget *treeview;
2870 GtkTreeModel *model;
2871 GtkCellRenderer *renderer;
2872 PopplerIndexIter *iter;
2874 if (!Zathura.UI.index) {
2875 Zathura.UI.index = gtk_scrolled_window_new (NULL, NULL);
2876 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.UI.index), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2878 if ((iter = poppler_index_iter_new(Zathura.PDF.document))) {
2879 model = GTK_TREE_MODEL(gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER));
2880 MTLOCK_PDFLIB();
2881 build_index(model, NULL, iter);
2882 MTUNLOCK_PDFLIB();
2883 poppler_index_iter_free(iter);
2884 } else {
2885 notify(WARNING, "This document does not contain any index");
2886 Zathura.UI.index = NULL;
2887 return;
2890 treeview = gtk_tree_view_new_with_model (model);
2891 g_object_unref(model);
2892 renderer = gtk_cell_renderer_text_new();
2893 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (treeview), 0, "Title", renderer, "markup", 0, NULL);
2894 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
2895 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2896 g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 0)), "expand", TRUE, NULL);
2898 gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview), gtk_tree_path_new_first(), NULL, FALSE);
2899 g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(cb_index_row_activated), NULL);
2901 gtk_container_add (GTK_CONTAINER (Zathura.UI.index), treeview);
2902 gtk_widget_show (treeview);
2903 gtk_widget_show(Zathura.UI.index);
2906 if (!Zathura.Global.show_index) {
2907 switch_view(Zathura.UI.index);
2908 Zathura.Global.mode = INDEX;
2909 } else {
2910 switch_view(Zathura.UI.document);
2911 Zathura.Global.mode = NORMAL;
2914 Zathura.Global.show_index = !Zathura.Global.show_index;
2918 //==========================================================================
2920 // sc_toggle_inputbar
2922 //==========================================================================
2923 void sc_toggle_inputbar (Argument *argument) {
2924 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura.UI.inputbar))) {
2925 gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
2926 } else {
2927 gtk_widget_show(GTK_WIDGET(Zathura.UI.inputbar));
2932 //==========================================================================
2934 // sc_toggle_fullscreen
2936 //==========================================================================
2937 void sc_toggle_fullscreen (Argument *argument) {
2938 static gboolean fs = TRUE;
2940 if (fs) {
2941 gtk_window_fullscreen(GTK_WINDOW(Zathura.UI.window));
2942 gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
2943 gtk_widget_hide(GTK_WIDGET(Zathura.UI.statusbar));
2945 Argument arg;
2946 arg.n = ADJUST_BESTFIT;
2947 sc_adjust_window(&arg);
2949 Zathura.Global.mode = FULLSCREEN;
2950 fs = FALSE;
2951 } else {
2952 gtk_window_unfullscreen(GTK_WINDOW(Zathura.UI.window));
2953 gtk_widget_show(GTK_WIDGET(Zathura.UI.inputbar));
2954 gtk_widget_show(GTK_WIDGET(Zathura.UI.statusbar));
2956 Zathura.Global.mode = NORMAL;
2957 fs = TRUE;
2959 isc_abort(NULL);
2963 //==========================================================================
2965 // sc_toggle_statusbar
2967 //==========================================================================
2968 void sc_toggle_statusbar (Argument *argument) {
2969 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura.UI.statusbar))) {
2970 gtk_widget_hide(GTK_WIDGET(Zathura.UI.statusbar));
2971 } else {
2972 gtk_widget_show(GTK_WIDGET(Zathura.UI.statusbar));
2977 //==========================================================================
2979 // sc_quit
2981 //==========================================================================
2982 void sc_quit (Argument *argument) {
2983 cb_destroy(NULL, NULL);
2987 //==========================================================================
2989 // sc_zoom
2991 //==========================================================================
2992 void sc_zoom (Argument *argument) {
2993 bcmd_zoom(NULL, argument);
2997 //**************************************************************************
2999 // inputbar shortcut declarations
3001 //**************************************************************************
3003 //==========================================================================
3005 // isc_abort
3007 //==========================================================================
3008 void isc_abort (Argument *argument) {
3009 Argument arg = { HIDE };
3010 isc_completion(&arg);
3012 notify(DEFAULT, "");
3013 change_mode(NORMAL);
3014 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
3016 if (!Zathura.Global.show_inputbar) gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
3018 /* replace default inputbar handler */
3019 g_signal_handler_disconnect((gpointer) Zathura.UI.inputbar, Zathura.Handler.inputbar_activate);
3020 Zathura.Handler.inputbar_activate = g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "activate", G_CALLBACK(cb_inputbar_activate), NULL);
3021 sc_abort(NULL);
3025 //==========================================================================
3027 // isc_command_history
3029 //==========================================================================
3030 void isc_command_history (Argument *argument) {
3031 static int current = 0;
3032 int length = g_list_length(Zathura.Global.history);
3033 if (length > 0) {
3034 current = (length+current+(argument->n == NEXT ? 1 : -1))%length;
3035 gchar *command = (gchar *)g_list_nth_data(Zathura.Global.history, current);
3036 notify(DEFAULT, command);
3037 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.inputbar));
3038 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
3043 //==========================================================================
3045 // isc_completion
3047 //==========================================================================
3048 void isc_completion (Argument *argument) {
3049 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 1, -1);
3050 gchar *tmp_string = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 0, 1);
3051 gchar identifier = tmp_string[0];
3052 int length = strlen(input);
3054 if (!input || !tmp_string) {
3055 if (input) g_free(input);
3056 if (tmp_string) g_free(tmp_string);
3057 return;
3060 /* get current information*/
3061 char *first_space = strstr(input, " ");
3062 char *current_command;
3063 char *current_parameter;
3064 int current_command_length;
3066 if (!first_space) {
3067 current_command = g_strdup(input);
3068 current_command_length = length;
3069 current_parameter = NULL;
3070 } else {
3071 int offset = abs(input-first_space);
3072 current_command = g_strndup(input, offset);
3073 current_command_length = strlen(current_command);
3074 current_parameter = input+offset+1;
3077 /* if the identifier does not match the command sign and
3078 * the completion should not be hidden, leave this function */
3079 if (identifier != ':' && argument->n != HIDE) {
3080 if (current_command) g_free(current_command);
3081 if (input) g_free(input);
3082 if (tmp_string) g_free(tmp_string);
3083 return;
3086 /* static elements */
3087 static GtkBox *results = NULL;
3088 static CompletionRow *rows = NULL;
3090 static int current_item = 0;
3091 static int n_items = 0;
3093 static char *previous_command = NULL;
3094 static char *previous_parameter = NULL;
3095 static int previous_id = 0;
3096 static int previous_length = 0;
3098 static gboolean command_mode = TRUE;
3100 /* delete old list iff
3101 * the completion should be hidden
3102 * the current command differs from the previous one
3103 * the current parameter differs from the previous one
3105 if (argument->n == HIDE || previous_length != length ||
3106 (current_parameter && previous_parameter && strcmp(current_parameter, previous_parameter) != 0) ||
3107 (current_command && previous_command && strcmp(current_command, previous_command) != 0)) {
3108 if (results) gtk_widget_destroy(GTK_WIDGET(results));
3109 results = NULL;
3110 if (rows) {
3111 for (int i = 0; i != n_items; ++i) {
3112 g_free(rows[i].command);
3113 g_free(rows[i].description);
3115 free(rows);
3117 rows = NULL;
3118 current_item = 0;
3119 n_items = 0;
3120 command_mode = TRUE;
3121 if (argument->n == HIDE) {
3122 if (current_command) g_free(current_command);
3123 if (input) g_free(input);
3124 if (tmp_string) g_free(tmp_string);
3125 return;
3129 /* create new list iff
3130 * there is no current list
3131 * the current command differs from the previous one
3132 * the current parameter differs from the previous one
3134 if (!results) {
3135 results = GTK_BOX(gtk_vbox_new(FALSE, 0));
3136 /* create list based on parameters iff
3137 * there is a current parameter given
3138 * there is an old list with commands
3139 * the current command does not differ from the previous one
3140 * the current command has an completion function
3142 if (strchr(input, ' ')) {
3143 gboolean search_matching_command = FALSE;
3144 for (int i = 0; i < LENGTH(commands); ++i) {
3145 int abbr_length = (commands[i].abbr ? strlen(commands[i].abbr) : 0);
3146 int cmd_length = (commands[i].command ? strlen(commands[i].command) : 0);
3147 if ((current_command_length <= cmd_length && !strncmp(current_command, commands[i].command, current_command_length)) ||
3148 (current_command_length <= abbr_length && !strncmp(current_command, commands[i].abbr, current_command_length))) {
3149 if (commands[i].completion) {
3150 previous_command = current_command;
3151 previous_id = i;
3152 search_matching_command = TRUE;
3153 } else {
3154 if (current_command) g_free(current_command);
3155 if (input) g_free(input);
3156 if (tmp_string) g_free(tmp_string);
3157 return;
3162 if (!search_matching_command) {
3163 if (current_command) g_free(current_command);
3164 if (input) g_free(input);
3165 if (tmp_string) g_free(tmp_string);
3166 return;
3169 Completion *result = commands[previous_id].completion(current_parameter);
3171 if (!result || !result->groups) {
3172 if (current_command) g_free(current_command);
3173 if (input) g_free(input);
3174 if (tmp_string) g_free(tmp_string);
3175 return;
3178 command_mode = FALSE;
3179 CompletionGroup *group = NULL;
3180 CompletionElement *element = NULL;
3182 rows = malloc(sizeof(CompletionRow));
3183 if (!rows) out_of_memory();
3185 for (group = result->groups; group != NULL; group = group->next) {
3186 int group_elements = 0;
3188 for(element = group->elements; element != NULL; element = element->next)
3190 if(element->value)
3192 if(group->value && !group_elements)
3194 rows = safe_realloc((void**)&rows, n_items+1, sizeof(CompletionRow));
3195 if(!rows)
3196 out_of_memory();
3197 rows[n_items].command = g_strdup(group->value);
3198 rows[n_items].description = NULL;
3199 rows[n_items].command_id = -1;
3200 rows[n_items].is_group = TRUE;
3201 rows[n_items++].row = GTK_WIDGET(create_completion_row(results, group->value, NULL, TRUE));
3204 rows = safe_realloc((void**)&rows, n_items+1, sizeof(CompletionRow));
3205 if(!rows)
3206 out_of_memory();
3207 rows[n_items].command = g_strdup(element->value);
3208 rows[n_items].description = element->description ? g_strdup(element->description) : NULL;
3209 rows[n_items].command_id = previous_id;
3210 rows[n_items].is_group = FALSE;
3211 rows[n_items++].row = GTK_WIDGET(create_completion_row(results, element->value, element->description, FALSE));
3212 group_elements++;
3217 /* clean up */
3218 completion_free(result);
3220 /* create list based on commands */
3221 else
3223 int i = 0;
3224 command_mode = TRUE;
3226 rows = malloc(LENGTH(commands)*sizeof(CompletionRow));
3227 if(!rows)
3228 out_of_memory();
3230 //printf("clen=%u\n", LENGTH(commands));
3231 for(i = 0; i < LENGTH(commands); i++)
3233 int abbr_length = commands[i].abbr ? strlen(commands[i].abbr) : 0;
3234 int cmd_length = commands[i].command ? strlen(commands[i].command) : 0;
3236 /* add command to list iff
3237 * the current command would match the command
3238 * the current command would match the abbreviation
3240 if( ((current_command_length <= cmd_length) && !strncmp(current_command, commands[i].command, current_command_length)) ||
3241 ((current_command_length <= abbr_length) && !strncmp(current_command, commands[i].abbr, current_command_length))
3244 rows[n_items].command = g_strdup(commands[i].command);
3245 rows[n_items].description = g_strdup(commands[i].description);
3246 rows[n_items].command_id = i;
3247 rows[n_items].is_group = FALSE;
3248 rows[n_items++].row = GTK_WIDGET(create_completion_row(results, commands[i].command, commands[i].description, FALSE));
3252 //printf("n_items=%u\n", n_items);
3253 rows = safe_realloc((void**)&rows, (n_items+1), sizeof(CompletionRow));
3254 if(!rows)
3255 out_of_memory();
3258 gtk_box_pack_start(Zathura.UI.box, GTK_WIDGET(results), FALSE, FALSE, 0);
3259 gtk_widget_show_all(GTK_WIDGET(Zathura.UI.window));
3261 current_item = (argument->n == NEXT) ? -1 : 0;
3264 /* update coloring iff
3265 * there is a list with items
3267 if( (results) && (n_items > 0) )
3269 set_completion_row_color(results, NORMAL, current_item);
3270 char* temp;
3271 int i = 0, next_group = 0;
3273 for(i = 0; i < n_items; i++)
3275 if(argument->n == NEXT || argument->n == NEXT_GROUP)
3276 current_item = (current_item+n_items+1)%n_items;
3277 else if(argument->n == PREVIOUS || argument->n == PREVIOUS_GROUP)
3278 current_item = (current_item+n_items-1)%n_items;
3280 if(rows[current_item].is_group)
3282 if(!command_mode && (argument->n == NEXT_GROUP || argument->n == PREVIOUS_GROUP))
3283 next_group = 1;
3284 continue;
3286 else
3288 if(!command_mode && (next_group == 0) && (argument->n == NEXT_GROUP || argument->n == PREVIOUS_GROUP))
3289 continue;
3290 break;
3294 set_completion_row_color(results, HIGHLIGHT, current_item);
3296 /* hide other items */
3297 int uh = ceil(n_completion_items/2);
3298 int lh = floor(n_completion_items/2);
3300 for(i = 0; i < n_items; i++)
3302 if((n_items > 1) && (
3303 (i >= (current_item-lh) && (i <= current_item+uh)) ||
3304 (i < n_completion_items && current_item < lh) ||
3305 (i >= (n_items-n_completion_items) && (current_item >= (n_items-uh))))
3307 gtk_widget_show(rows[i].row);
3308 else
3309 gtk_widget_hide(rows[i].row);
3312 if(command_mode)
3313 temp = g_strconcat(":", rows[current_item].command, (n_items == 1) ? " " : NULL, NULL);
3314 else
3315 temp = g_strconcat(":", previous_command, " ", rows[current_item].command, NULL);
3317 gtk_entry_set_text(Zathura.UI.inputbar, temp);
3318 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
3319 g_free(temp);
3321 previous_command = g_strdup((command_mode) ? rows[current_item].command : current_command);
3322 previous_parameter = g_strdup((command_mode) ? current_parameter : rows[current_item].command);
3323 previous_length = strlen(previous_command)+((command_mode) ? (length-current_command_length) : (strlen(previous_parameter)+1));
3324 previous_id = rows[current_item].command_id;
3327 if(current_command)
3328 g_free(current_command);
3329 if(input)
3330 g_free(input);
3331 if(tmp_string)
3332 g_free(tmp_string);
3336 //==========================================================================
3338 // isc_string_manipulation
3340 //==========================================================================
3341 void isc_string_manipulation (Argument *argument)
3343 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 0, -1);
3344 int length = strlen(input);
3345 int pos = gtk_editable_get_position(GTK_EDITABLE(Zathura.UI.inputbar));
3346 int i;
3348 switch (argument->n) {
3349 case DELETE_LAST_WORD:
3350 i = pos-1;
3352 if(!pos)
3353 return;
3355 /* remove trailing spaces */
3356 for(; i >= 0 && input[i] == ' '; i--);
3358 /* find the beginning of the word */
3359 while((i > 0) && (input[i] != ' ') && (input[i] != '/'))
3360 i--;
3362 gtk_editable_delete_text(GTK_EDITABLE(Zathura.UI.inputbar), i, pos);
3363 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), i);
3364 break;
3365 case DELETE_LAST_CHAR:
3366 if((length-1) <= 0)
3367 isc_abort(NULL);
3369 gtk_editable_delete_text(GTK_EDITABLE(Zathura.UI.inputbar), pos-1, pos);
3370 break;
3371 case DELETE_TO_LINE_START:
3372 gtk_editable_delete_text(GTK_EDITABLE(Zathura.UI.inputbar), 1, pos);
3373 break;
3374 case NEXT_CHAR:
3375 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), pos+1);
3376 break;
3377 case PREVIOUS_CHAR:
3378 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), (pos == 0) ? 0 : pos-1);
3379 break;
3380 default: /* unreachable */
3381 break;
3386 //**************************************************************************
3388 // command implementation
3390 //**************************************************************************
3392 //==========================================================================
3394 // cmd_bookmark
3396 //==========================================================================
3397 gboolean cmd_bookmark (int argc, char **argv) {
3398 if(!Zathura.PDF.document || argc < 1)
3399 return TRUE;
3401 /* get id */
3402 int i;
3403 GString *id = g_string_new("");
3405 for(i = 0; i < argc; i++)
3407 if(i != 0)
3408 id = g_string_append_c(id, ' ');
3410 id = g_string_append(id, argv[i]);
3413 if(strlen(id->str) == 0)
3415 notify(WARNING, "Can't set bookmark: bookmark name is empty");
3416 g_string_free(id, TRUE);
3417 return FALSE;
3420 if(is_reserved_bm_name(id->str))
3422 notify(WARNING, "Can't set bookmark: reserved bookmark name");
3423 g_string_free(id, TRUE);
3424 return FALSE;
3427 /* reload the bookmark file */
3428 read_bookmarks_file();
3430 /* check for existing bookmark to overwrite */
3431 for(i = 0; i < Zathura.Bookmarks.number_of_bookmarks; i++)
3433 if(!strcmp(id->str, Zathura.Bookmarks.bookmarks[i].id))
3435 Zathura.Bookmarks.bookmarks[i].page = Zathura.PDF.page_number;
3436 g_string_free(id, TRUE);
3437 return TRUE;
3441 /* add new bookmark */
3442 Zathura.Bookmarks.bookmarks = safe_realloc((void**)&Zathura.Bookmarks.bookmarks,
3443 Zathura.Bookmarks.number_of_bookmarks+1, sizeof(Bookmark));
3444 if(!Zathura.Bookmarks.bookmarks)
3445 out_of_memory();
3447 Zathura.Bookmarks.bookmarks[Zathura.Bookmarks.number_of_bookmarks].id = g_strdup(id->str);
3448 Zathura.Bookmarks.bookmarks[Zathura.Bookmarks.number_of_bookmarks].page = Zathura.PDF.page_number;
3449 Zathura.Bookmarks.number_of_bookmarks++;
3451 /* write the bookmark file */
3452 write_bookmarks_file();
3454 g_string_free(id, TRUE);
3455 return TRUE;
3459 //==========================================================================
3461 // cmd_open_bookmark
3463 //==========================================================================
3464 gboolean cmd_open_bookmark (int argc, char **argv)
3466 if(!Zathura.PDF.document || argc < 1)
3467 return TRUE;
3469 /* get id */
3470 int i;
3471 GString *id = g_string_new("");
3473 for(i = 0; i < argc; i++)
3475 if(i != 0)
3476 id = g_string_append_c(id, ' ');
3478 id = g_string_append(id, argv[i]);
3481 /* find bookmark */
3482 for(i = 0; i < Zathura.Bookmarks.number_of_bookmarks; i++)
3484 if(!strcmp(id->str, Zathura.Bookmarks.bookmarks[i].id))
3486 set_page(Zathura.Bookmarks.bookmarks[i].page);
3487 g_string_free(id, TRUE);
3488 return TRUE;
3492 notify(WARNING, "No matching bookmark found");
3493 g_string_free(id, TRUE);
3494 return FALSE;
3498 //==========================================================================
3500 // cmd_close
3502 //==========================================================================
3503 gboolean cmd_close (int argc, char **argv)
3505 close_file(FALSE);
3507 return TRUE;
3511 //==========================================================================
3513 // cmd_delete_bookmark
3515 //==========================================================================
3516 gboolean cmd_delete_bookmark (int argc, char **argv)
3518 if(!Zathura.PDF.document || argc < 1)
3519 return TRUE;
3521 /* get id */
3522 int i;
3523 GString *id = g_string_new("");
3525 for(i = 0; i < argc; i++)
3527 if(i != 0)
3528 id = g_string_append_c(id, ' ');
3530 id = g_string_append(id, argv[i]);
3533 /* reload bookmark file */
3534 read_bookmarks_file();
3536 /* check for bookmark to delete */
3537 for(i = 0; i < Zathura.Bookmarks.number_of_bookmarks; i++)
3539 if(!strcmp(id->str, Zathura.Bookmarks.bookmarks[i].id))
3541 /* update key file */
3542 g_key_file_remove_key(Zathura.Bookmarks.data, Zathura.PDF.file, Zathura.Bookmarks.bookmarks[i].id, NULL);
3544 g_free(Zathura.Bookmarks.bookmarks[i].id);
3545 /* update bookmarks */
3546 Zathura.Bookmarks.bookmarks[i].id = Zathura.Bookmarks.bookmarks[Zathura.Bookmarks.number_of_bookmarks-1].id;
3547 Zathura.Bookmarks.bookmarks[i].page = Zathura.Bookmarks.bookmarks[Zathura.Bookmarks.number_of_bookmarks-1].page;
3548 Zathura.Bookmarks.bookmarks = safe_realloc((void**)&Zathura.Bookmarks.bookmarks,
3549 Zathura.Bookmarks.number_of_bookmarks, sizeof(Bookmark));
3550 if(!Zathura.Bookmarks.bookmarks)
3551 out_of_memory();
3553 Zathura.Bookmarks.number_of_bookmarks--;
3554 g_string_free(id, TRUE);
3556 /* write bookmark file */
3557 write_bookmarks_file();
3559 return TRUE;
3563 g_string_free(id, TRUE);
3564 return TRUE;
3568 //==========================================================================
3570 // cmd_export
3572 //==========================================================================
3573 gboolean cmd_export (int argc, char **argv)
3575 if(argc == 0 || !Zathura.PDF.document)
3576 return TRUE;
3578 if(argc < 2)
3580 notify(WARNING, "No export path specified");
3581 return FALSE;
3584 /* export images */
3585 if(!strcmp(argv[0], "images"))
3587 int page_number;
3588 for(page_number = 0; page_number < Zathura.PDF.number_of_pages; page_number++)
3590 GList *image_list;
3591 GList *images;
3592 cairo_surface_t *image;
3594 MTLOCK_PDFLIB();
3595 image_list = poppler_page_get_image_mapping(Zathura.PDF.pages[page_number]->page);
3596 MTUNLOCK_PDFLIB();
3598 if(!g_list_length(image_list))
3600 notify(WARNING, "This document does not contain any images");
3601 return FALSE;
3604 for(images = image_list; images; images = g_list_next(images))
3606 PopplerImageMapping *image_mapping;
3607 gint image_id;
3608 char* file;
3609 char* filename;
3611 image_mapping = (PopplerImageMapping*) images->data;
3612 image_id = image_mapping->image_id;
3614 MTLOCK_PDFLIB();
3615 image = poppler_page_get_image(Zathura.PDF.pages[page_number]->page, image_id);
3616 MTUNLOCK_PDFLIB();
3618 if(!image)
3619 continue;
3621 filename = g_strdup_printf("%s_p%i_i%i.png", Zathura.PDF.file, page_number+1, image_id);
3623 if(argv[1][0] == '~')
3625 gchar* home_path = get_home_dir();
3626 file = g_strdup_printf("%s%s%s", home_path, argv[1]+1, filename);
3627 g_free(home_path);
3629 else
3630 file = g_strdup_printf("%s%s", argv[1], filename);
3632 cairo_surface_write_to_png(image, file);
3634 g_free(filename);
3635 g_free(file);
3639 else if(!strcmp(argv[0], "attachments"))
3641 MTLOCK_PDFLIB();
3642 if(!poppler_document_has_attachments(Zathura.PDF.document))
3644 notify(WARNING, "PDF file has no attachments");
3645 MTUNLOCK_PDFLIB();
3646 return FALSE;
3649 GList *attachment_list = poppler_document_get_attachments(Zathura.PDF.document);
3650 MTUNLOCK_PDFLIB();
3652 GList *attachments;
3653 char *file;
3655 for(attachments = attachment_list; attachments; attachments = g_list_next(attachments))
3657 PopplerAttachment *attachment = (PopplerAttachment*) attachments->data;
3659 if(argv[1][0] == '~')
3661 gchar* home_path = get_home_dir();
3662 file = g_strdup_printf("%s%s%s", home_path, argv[1]+1, attachment->name);
3663 g_free(home_path);
3665 else
3666 file = g_strdup_printf("%s%s", argv[1], attachment->name);
3668 MTLOCK_PDFLIB();
3669 poppler_attachment_save(attachment, file, NULL);
3670 MTUNLOCK_PDFLIB();
3672 g_free(file);
3676 return TRUE;
3680 //==========================================================================
3682 // cmd_info
3684 //==========================================================================
3685 gboolean cmd_info (int argc, char **argv)
3687 if(!Zathura.PDF.document)
3688 return TRUE;
3690 static gboolean visible = FALSE;
3692 if(!Zathura.UI.information)
3694 GtkListStore *list;
3695 GtkTreeIter iter;
3696 GtkCellRenderer *renderer;
3697 GtkTreeSelection *selection;
3699 list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
3701 /* read document information */
3702 gchar *title, *author;
3703 gchar *subject, *keywords;
3704 gchar *creator, *producer;
3705 GTime creation_date, modification_date;
3707 g_object_get(Zathura.PDF.document,
3708 "title", &title,
3709 "author", &author,
3710 "subject", &subject,
3711 "keywords", &keywords,
3712 "creator", &creator,
3713 "producer", &producer,
3714 "creation-date", &creation_date,
3715 "mod-date", &modification_date,
3716 NULL);
3718 /* append information to list */
3719 gtk_list_store_append(list, &iter);
3720 gtk_list_store_set(list, &iter, 0, "Author", 1, author ? author : "", -1);
3721 gtk_list_store_append(list, &iter);
3722 gtk_list_store_set(list, &iter, 0, "Title", 1, title ? title : "", -1);
3723 gtk_list_store_append(list, &iter);
3724 gtk_list_store_set(list, &iter, 0, "Subject", 1, subject ? subject : "", -1);
3725 gtk_list_store_append(list, &iter);
3726 gtk_list_store_set(list, &iter, 0, "Keywords", 1, keywords ? keywords : "", -1);
3727 gtk_list_store_append(list, &iter);
3728 gtk_list_store_set(list, &iter, 0, "Creator", 1, creator ? creator : "", -1);
3729 gtk_list_store_append(list, &iter);
3730 gtk_list_store_set(list, &iter, 0, "Producer", 1, producer ? producer : "", -1);
3732 Zathura.UI.information = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
3733 renderer = gtk_cell_renderer_text_new();
3735 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(Zathura.UI.information), -1,
3736 "Name", renderer, "text", 0, NULL);
3737 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(Zathura.UI.information), -1,
3738 "Value", renderer, "text", 1, NULL);
3740 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(Zathura.UI.information));
3741 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3743 gtk_widget_show_all(Zathura.UI.information);
3746 if(!visible)
3747 switch_view(Zathura.UI.information);
3748 else
3749 switch_view(Zathura.UI.document);
3751 visible = !visible;
3753 return FALSE;
3757 //==========================================================================
3759 // cmd_map
3761 //==========================================================================
3762 gboolean cmd_map (int argc, char **argv)
3764 if(argc < 2)
3765 return TRUE;
3767 char* ks = argv[0];
3769 /* search for the right shortcut function */
3770 int sc_id = -1;
3772 int sc_c;
3773 for(sc_c = 0; sc_c < LENGTH(shortcut_names); sc_c++)
3775 if(!strcmp(argv[1], shortcut_names[sc_c].name))
3777 sc_id = sc_c;
3778 break;
3782 if(sc_id == -1)
3784 notify(WARNING, "No such shortcut function exists");
3785 return FALSE;
3788 /* parse modifier and key */
3789 int mask = 0;
3790 int key = 0;
3791 int keyl = strlen(ks);
3792 int mode = NORMAL;
3794 // single key (e.g.: g)
3795 if(keyl == 1)
3796 key = ks[0];
3798 // modifier and key (e.g.: <S-g>
3799 // special key or modifier and key/special key (e.g.: <S-g>, <Space>)
3801 else if(keyl >= 3 && ks[0] == '<' && ks[keyl-1] == '>')
3803 char* specialkey = NULL;
3805 /* check for modifier */
3806 if(keyl >= 5 && ks[2] == '-')
3808 /* evaluate modifier */
3809 switch(ks[1])
3811 case 'S':
3812 mask = GDK_SHIFT_MASK;
3813 break;
3814 case 'C':
3815 mask = GDK_CONTROL_MASK;
3816 break;
3819 /* no valid modifier */
3820 if(!mask)
3822 notify(WARNING, "No valid modifier given.");
3823 return FALSE;
3826 /* modifier and special key */
3827 if(keyl > 5)
3828 specialkey = g_strndup(ks+3, keyl-4);
3829 else
3830 key = ks[3];
3832 else
3833 specialkey = ks;
3835 /* search special key */
3836 int g_c;
3837 for(g_c = 0; specialkey && g_c < LENGTH(gdk_keys); g_c++)
3839 if(!strcmp(specialkey, gdk_keys[g_c].identifier))
3841 key = gdk_keys[g_c].key;
3842 break;
3846 if(specialkey)
3847 g_free(specialkey);
3850 if(!key)
3852 notify(WARNING, "No valid key binding given.");
3853 return FALSE;
3856 /* parse argument */
3857 Argument arg = {0, 0};
3859 if(argc >= 3)
3861 int arg_id = -1;
3863 /* compare argument with given argument names... */
3864 int arg_c;
3865 for(arg_c = 0; arg_c < LENGTH(argument_names); arg_c++)
3867 if(!strcmp(argv[2], argument_names[arg_c].name))
3869 arg_id = argument_names[arg_c].argument;
3870 break;
3874 /* if not, save it do .data */
3875 if(arg_id == -1)
3876 arg.data = argv[2];
3877 else
3878 arg.n = arg_id;
3881 /* parse mode */
3882 if(argc >= 4)
3884 int mode_c;
3885 for(mode_c = 0; mode_c < LENGTH(mode_names); mode_c++)
3887 if(!strcmp(argv[3], mode_names[mode_c].name))
3889 mode = mode_names[mode_c].mode;
3890 break;
3895 /* search for existing binding to overwrite it */
3896 ShortcutList* sc = Zathura.Bindings.sclist;
3897 while(sc && sc->next != NULL)
3899 if(sc->element.key == key && sc->element.mask == mask
3900 && sc->element.mode == mode)
3902 sc->element.function = shortcut_names[sc_id].function;
3903 sc->element.argument = arg;
3904 return TRUE;
3907 sc = sc->next;
3910 /* create new entry */
3911 ShortcutList* entry = malloc(sizeof(ShortcutList));
3912 if(!entry)
3913 out_of_memory();
3915 entry->element.mask = mask;
3916 entry->element.key = key;
3917 entry->element.function = shortcut_names[sc_id].function;
3918 entry->element.mode = mode;
3919 entry->element.argument = arg;
3920 entry->next = NULL;
3922 /* append to list */
3923 if(!Zathura.Bindings.sclist)
3924 Zathura.Bindings.sclist = entry;
3926 if(sc)
3927 sc->next = entry;
3929 return TRUE;
3933 //==========================================================================
3935 // cmd_open
3937 //==========================================================================
3938 gboolean cmd_open (int argc, char **argv)
3940 if(argc == 0 || strlen(argv[0]) == 0)
3941 return TRUE;
3943 /* assembly the arguments back to one string */
3944 int i = 0;
3945 GString *filepath = g_string_new("");
3946 for(i = 0; i < argc; i++)
3948 if(i != 0)
3949 filepath = g_string_append_c(filepath, ' ');
3951 filepath = g_string_append(filepath, argv[i]);
3954 gboolean res = open_file(filepath->str, NULL);
3955 g_string_free(filepath, TRUE);
3956 return res;
3960 //==========================================================================
3962 // cmd_print
3964 //==========================================================================
3965 gboolean cmd_print (int argc, char **argv)
3967 if(!Zathura.PDF.document)
3968 return TRUE;
3970 if(argc == 0)
3972 notify(WARNING, "No printer specified");
3973 return FALSE;
3976 char* printer = argv[0];
3977 char* sites = (argc >= 2) ? g_strdup(argv[1]) : g_strdup_printf("1-%i", Zathura.PDF.number_of_pages);
3978 GString *addit = g_string_new("");
3980 int i;
3981 for(i = 2; i < argc; i++)
3983 if(i != 0)
3984 addit = g_string_append_c(addit, ' ');
3986 addit = g_string_append(addit, argv[i]);
3989 char* escaped_filename = g_shell_quote(Zathura.PDF.file);
3990 char* command = g_strdup_printf(print_command, printer, sites, addit->str, escaped_filename);
3991 system(command);
3993 g_free(sites);
3994 g_free(escaped_filename);
3995 g_free(command);
3996 g_string_free(addit, TRUE);
3998 return TRUE;
4002 //==========================================================================
4004 // cmd_rotate
4006 //==========================================================================
4007 gboolean cmd_rotate (int argc, char **argv)
4009 return TRUE;
4013 //==========================================================================
4015 // cmd_set
4017 //==========================================================================
4018 gboolean cmd_set (int argc, char **argv)
4020 if(argc <= 0)
4021 return FALSE;
4023 int i;
4024 for(i = 0; i < LENGTH(settings); i++)
4026 if(!strcmp(argv[0], settings[i].name))
4028 /* check var type */
4029 if(settings[i].type == 'b')
4031 gboolean *x = (gboolean*) (settings[i].variable);
4032 *x = !(*x);
4034 if(argv[1])
4036 if(!strcmp(argv[1], "false") || !strcmp(argv[1], "0"))
4037 *x = FALSE;
4038 else
4039 *x = TRUE;
4042 else if(settings[i].type == 'i')
4044 if(argc != 2)
4045 return FALSE;
4047 int *x = (int*) (settings[i].variable);
4049 int id = -1;
4050 int arg_c;
4051 for(arg_c = 0; arg_c < LENGTH(argument_names); arg_c++)
4053 if(!strcmp(argv[1], argument_names[arg_c].name))
4055 id = argument_names[arg_c].argument;
4056 break;
4060 if(id == -1)
4061 id = atoi(argv[1]);
4063 *x = id;
4065 else if(settings[i].type == 'f')
4067 if(argc != 2)
4068 return FALSE;
4070 float *x = (float*) (settings[i].variable);
4071 if(argv[1])
4072 *x = atof(argv[1]);
4074 else if(settings[i].type == 's')
4076 if(argc < 2)
4077 return FALSE;
4079 /* assembly the arguments back to one string */
4080 int j;
4081 GString *s = g_string_new("");
4082 for(j = 1; j < argc; j++)
4084 if(j != 1)
4085 s = g_string_append_c(s, ' ');
4087 s = g_string_append(s, argv[j]);
4090 char **x = (char**) settings[i].variable;
4091 *x = s->str;
4093 else if(settings[i].type == 'c')
4095 if(argc != 2)
4096 return FALSE;
4098 char *x = (char*) (settings[i].variable);
4099 if(argv[1])
4100 *x = argv[1][0];
4103 /* re-init */
4104 if(settings[i].reinit)
4105 init_look();
4107 /* render */
4108 if(settings[i].render)
4110 if(!Zathura.PDF.document)
4111 return FALSE;
4113 draw(Zathura.PDF.page_number);
4118 update_status();
4119 return TRUE;
4123 //==========================================================================
4125 // cmd_quit
4127 //==========================================================================
4128 gboolean cmd_quit (int argc, char **argv)
4130 cb_destroy(NULL, NULL);
4131 return TRUE;
4135 //==========================================================================
4137 // save_file
4139 //==========================================================================
4140 gboolean save_file (int argc, char **argv, gboolean overwrite)
4142 if(argc == 0 || !Zathura.PDF.document)
4143 return TRUE;
4145 gchar* file_path = NULL;
4147 if(argv[0][0] == '~')
4149 gchar* home_path = get_home_dir();
4150 file_path = g_build_filename(home_path, argv[0]+1, NULL);
4151 g_free(home_path);
4153 else
4154 file_path = g_strdup(argv[0]);
4156 if (!overwrite && g_file_test(file_path, G_FILE_TEST_EXISTS))
4158 char* message = g_strdup_printf("File already exists: %s. Use :write! to overwrite it.", file_path);
4159 notify(ERROR, message);
4160 g_free(message);
4161 g_free(file_path);
4162 return FALSE;
4165 char* path = NULL;
4166 if (file_path[0] == '/')
4167 path = g_strdup_printf("file://%s", file_path);
4168 else
4170 char* cur = g_get_current_dir();
4171 path = g_strdup_printf("file://%s/%s", cur, file_path);
4172 g_free(cur);
4174 g_free(file_path);
4176 MTLOCK_PDFLIB();
4177 /* format path */
4178 GError* error = NULL;
4179 if (!poppler_document_save(Zathura.PDF.document, path, &error))
4181 g_free(path);
4182 char* message = g_strdup_printf("Can not write file: %s", error->message);
4183 notify(ERROR, message);
4184 g_free(message);
4185 g_error_free(error);
4186 MTUNLOCK_PDFLIB();
4187 return FALSE;
4190 MTUNLOCK_PDFLIB();
4191 g_free(path);
4193 return TRUE;
4197 //==========================================================================
4199 // cmd_save
4201 //==========================================================================
4202 gboolean cmd_save (int argc, char **argv)
4204 return save_file(argc, argv, FALSE);
4208 //==========================================================================
4210 // cmd_savef
4212 //==========================================================================
4213 gboolean cmd_savef (int argc, char **argv)
4215 return save_file(argc, argv, TRUE);
4219 //**************************************************************************
4221 // completion command implementation
4223 //**************************************************************************
4225 //==========================================================================
4227 // cc_bookmark
4229 //==========================================================================
4230 Completion *cc_bookmark (char *input) {
4231 Completion* completion = completion_init();
4232 CompletionGroup* group = completion_group_create(NULL);
4234 completion_add_group(completion, group);
4236 int i = 0;
4237 int input_length = input ? strlen(input) : 0;
4239 for(i = 0; i < Zathura.Bookmarks.number_of_bookmarks; i++)
4241 if( (input_length <= strlen(Zathura.Bookmarks.bookmarks[i].id)) &&
4242 !strncmp(input, Zathura.Bookmarks.bookmarks[i].id, input_length) )
4244 completion_group_add_element(group, Zathura.Bookmarks.bookmarks[i].id, g_strdup_printf("Page %d", Zathura.Bookmarks.bookmarks[i].page));
4248 return completion;
4252 //==========================================================================
4254 // cc_export
4256 //==========================================================================
4257 Completion *cc_export (char *input)
4259 Completion* completion = completion_init();
4260 CompletionGroup* group = completion_group_create(NULL);
4262 completion_add_group(completion, group);
4264 completion_group_add_element(group, "images", "Export images");
4265 completion_group_add_element(group, "attachments", "Export attachments");
4267 return completion;
4271 //==========================================================================
4273 // cc_open
4275 //==========================================================================
4276 Completion *cc_open (char *input)
4278 Completion* completion = completion_init();
4279 CompletionGroup* group = completion_group_create(NULL);
4281 completion_add_group(completion, group);
4283 /* ~ */
4284 if(input && input[0] == '~')
4286 gchar* home_path = get_home_dir();
4287 char *file = g_strdup_printf(":open %s/%s", home_path, input+1);
4288 g_free(home_path);
4289 gtk_entry_set_text(Zathura.UI.inputbar, file);
4290 gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
4291 g_free(file);
4292 completion_free(completion);
4293 return NULL;
4296 /* read dir */
4297 char* path = g_strdup("/");
4298 char* file = g_strdup("");
4299 int file_length = 0;
4301 /* parse input string */
4302 if(input && strlen(input) > 0)
4304 char* dinput = g_strdup(input);
4305 char* binput = g_strdup(input);
4306 char* path_temp = dirname(dinput);
4307 char* file_temp = basename(binput);
4308 char last_char = input[strlen(input)-1];
4310 if( !strcmp(path_temp, "/") && !strcmp(file_temp, "/") )
4312 g_free(file);
4313 file = g_strdup("");
4315 else if( !strcmp(path_temp, "/") && strcmp(file_temp, "/") && last_char != '/')
4317 g_free(file);
4318 file = g_strdup(file_temp);
4320 else if( !strcmp(path_temp, "/") && strcmp(file_temp, "/") && last_char == '/')
4322 g_free(path);
4323 path = g_strdup_printf("/%s/", file_temp);
4325 else if(last_char == '/')
4327 g_free(path);
4328 path = g_strdup(input);
4330 else
4332 g_free(path);
4333 g_free(file);
4334 path = g_strdup_printf("%s/", path_temp);
4335 file = g_strdup(file_temp);
4338 g_free(dinput);
4339 g_free(binput);
4342 file_length = strlen(file);
4344 /* open directory */
4345 GDir* dir = g_dir_open(path, 0, NULL);
4346 if(!dir)
4348 g_free(path);
4349 g_free(file);
4350 completion_free(completion);
4351 return NULL;
4354 /* create element list */
4355 char* name = NULL;
4357 while((name = (char*) g_dir_read_name(dir)) != NULL)
4359 char* d_name = g_filename_display_name(name);
4360 int d_length = strlen(d_name);
4362 if( ((file_length <= d_length) && !strncmp(file, d_name, file_length)) ||
4363 (file_length == 0) )
4365 char* d = g_strdup_printf("%s%s", path, d_name);
4366 if(g_file_test(d, G_FILE_TEST_IS_DIR))
4368 gchar *subdir = d;
4369 d = g_strdup_printf("%s/", subdir);
4370 g_free(subdir);
4372 completion_group_add_element(group, d, NULL);
4373 g_free(d);
4375 g_free(d_name);
4378 g_dir_close(dir);
4379 g_free(file);
4380 g_free(path);
4382 return completion;
4386 //==========================================================================
4388 // cc_print
4390 //==========================================================================
4391 Completion *cc_print (char *input)
4393 Completion* completion = completion_init();
4394 CompletionGroup* group = completion_group_create(NULL);
4396 completion_add_group(completion, group);
4398 int input_length = input ? strlen(input) : 0;
4400 /* read printers */
4401 char *current_line = NULL, current_char;
4402 int count = 0;
4403 FILE *fp;
4405 fp = popen(list_printer_command, "r");
4407 if(!fp)
4409 completion_free(completion);
4410 return NULL;
4413 while((current_char = fgetc(fp)) != EOF)
4415 if(!current_line)
4416 current_line = malloc(sizeof(char));
4417 if(!current_line)
4418 out_of_memory();
4420 current_line = safe_realloc((void**)&current_line, count+1, sizeof(char));
4421 if(!current_line)
4422 out_of_memory();
4424 if(current_char != '\n')
4425 current_line[count++] = current_char;
4426 else
4428 current_line[count] = '\0';
4429 int line_length = strlen(current_line);
4431 if( (input_length <= line_length) ||
4432 (!strncmp(input, current_line, input_length)) )
4434 completion_group_add_element(group, current_line, NULL);
4437 free(current_line);
4438 current_line = NULL;
4439 count = 0;
4443 pclose(fp);
4445 return completion;
4449 //==========================================================================
4451 // cc_set
4453 //==========================================================================
4454 Completion *cc_set (char *input)
4456 Completion* completion = completion_init();
4457 CompletionGroup* group = completion_group_create(NULL);
4459 completion_add_group(completion, group);
4461 int i = 0;
4462 int input_length = input ? strlen(input) : 0;
4464 for(i = 0; i < LENGTH(settings); i++)
4466 if( (input_length <= strlen(settings[i].name)) &&
4467 !strncmp(input, settings[i].name, input_length) )
4469 completion_group_add_element(group, settings[i].name, settings[i].description);
4473 return completion;
4477 //**************************************************************************
4479 // buffer command implementation
4481 //**************************************************************************
4483 //==========================================================================
4485 // bcmd_goto
4487 //==========================================================================
4488 void bcmd_goto (char* buffer, Argument* argument)
4490 if(!Zathura.PDF.document)
4491 return;
4493 int b_length = strlen(buffer);
4494 if(b_length < 1)
4495 return;
4497 if(!strcmp(buffer, "gg"))
4498 set_page(0);
4499 else if(!strcmp(buffer, "G"))
4500 set_page(Zathura.PDF.number_of_pages-1);
4501 else
4503 char* id = g_strndup(buffer, b_length-1);
4504 int pid = atoi(id);
4506 if(Zathura.Global.goto_mode == GOTO_LABELS)
4508 int i;
4509 for(i = 0; i < Zathura.PDF.number_of_pages; i++)
4510 if(!strcmp(id, Zathura.PDF.pages[i]->label))
4511 pid = Zathura.PDF.pages[i]->id;
4513 set_page(pid-1);
4514 g_free(id);
4517 update_status();
4521 //==========================================================================
4523 // try_goto
4525 //==========================================================================
4526 gboolean try_goto (const char *buffer)
4528 char* endptr = NULL;
4529 long page_number = strtol(buffer, &endptr, 10)-1;
4530 if(*endptr)
4531 /* conversion error */
4532 return FALSE;
4533 else
4535 /* behave like vim: <= 1 => first line, >= #lines => last line */
4536 page_number = MAX(0, MIN(Zathura.PDF.number_of_pages-1, page_number));
4537 set_page(page_number);
4538 update_status();
4539 return TRUE;
4544 //==========================================================================
4546 // bcmd_scroll
4548 //==========================================================================
4549 void bcmd_scroll (char *buffer, Argument *argument)
4551 int b_length = strlen(buffer);
4552 if(b_length < 1)
4553 return;
4555 int percentage = atoi(g_strndup(buffer, b_length-1));
4556 percentage = (percentage < 0) ? 0 : ((percentage > 100) ? 100 : percentage);
4558 GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
4560 gdouble view_size = gtk_adjustment_get_page_size(adjustment);
4561 gdouble max = gtk_adjustment_get_upper(adjustment)-view_size;
4562 gdouble nvalue = (percentage*max)/100;
4564 percentage = 0;
4565 nvalue = 0;
4567 Zathura.State.scroll_percentage = percentage;
4568 gtk_adjustment_set_value(adjustment, nvalue);
4569 update_status();
4573 //==========================================================================
4575 // bcmd_zoom
4577 //==========================================================================
4578 void bcmd_zoom (char *buffer, Argument *argument)
4580 Zathura.Global.adjust_mode = ADJUST_NONE;
4582 if(argument->n == ZOOM_IN)
4584 if((Zathura.PDF.scale+zoom_step) <= zoom_max)
4585 Zathura.PDF.scale += zoom_step;
4586 else
4587 Zathura.PDF.scale = zoom_max;
4589 else if(argument->n == ZOOM_OUT)
4591 if((Zathura.PDF.scale-zoom_step) >= zoom_min)
4592 Zathura.PDF.scale -= zoom_step;
4593 else
4594 Zathura.PDF.scale = zoom_min;
4596 else if(argument->n == ZOOM_SPECIFIC)
4598 int b_length = strlen(buffer);
4599 if(b_length < 1)
4600 return;
4602 int value = atoi(g_strndup(buffer, b_length-1));
4603 if(value <= zoom_min)
4604 Zathura.PDF.scale = zoom_min;
4605 else if(value >= zoom_max)
4606 Zathura.PDF.scale = zoom_max;
4607 else
4608 Zathura.PDF.scale = value;
4610 else
4611 Zathura.PDF.scale = 100;
4613 Zathura.Search.draw = TRUE;
4614 draw(Zathura.PDF.page_number);
4615 update_status();
4619 //**************************************************************************
4621 // special command implementation
4623 //**************************************************************************
4625 //==========================================================================
4627 // scmd_search
4629 //==========================================================================
4630 gboolean scmd_search (gchar *input, Argument *argument)
4632 if(!input || !strlen(input))
4633 return TRUE;
4635 argument->data = input;
4636 sc_search(argument);
4638 return TRUE;
4642 //**************************************************************************
4644 // callback implementation
4646 //**************************************************************************
4648 //==========================================================================
4650 // cb_destroy
4652 //==========================================================================
4653 gboolean cb_destroy (GtkWidget *widget, gpointer data) {
4654 pango_font_description_free(Zathura.Style.font);
4656 if(Zathura.PDF.document)
4657 close_file(FALSE);
4659 /* clean up bookmarks */
4660 g_free(Zathura.Bookmarks.file);
4661 if (Zathura.Bookmarks.data)
4662 g_key_file_free(Zathura.Bookmarks.data);
4664 /* destroy mutexes */
4665 g_static_mutex_free(&(Zathura.Lock.pdflib_lock));
4666 g_static_mutex_free(&(Zathura.Lock.search_lock));
4667 g_static_mutex_free(&(Zathura.Lock.pdf_obj_lock));
4668 g_static_mutex_free(&(Zathura.Lock.select_lock));
4670 /* inotify */
4671 if(Zathura.FileMonitor.monitor)
4672 g_object_unref(Zathura.FileMonitor.monitor);
4673 if(Zathura.FileMonitor.file)
4674 g_object_unref(Zathura.FileMonitor.file);
4676 g_list_free(Zathura.Global.history);
4678 /* clean shortcut list */
4679 ShortcutList* sc = Zathura.Bindings.sclist;
4681 while(sc)
4683 ShortcutList* ne = sc->next;
4684 free(sc);
4685 sc = ne;
4688 g_free(Zathura.State.filename);
4689 g_free(Zathura.State.pages);
4691 g_free(Zathura.Config.config_dir);
4692 g_free(Zathura.Config.data_dir);
4693 if (Zathura.StdinSupport.file)
4694 g_unlink(Zathura.StdinSupport.file);
4695 g_free(Zathura.StdinSupport.file);
4697 gtk_main_quit();
4699 return TRUE;
4703 //==========================================================================
4705 // cb_draw
4707 //==========================================================================
4708 gboolean cb_draw (GtkWidget *widget, GdkEventExpose *expose, gpointer data) {
4709 if (!Zathura.PDF.document) return FALSE;
4711 int page_id = Zathura.PDF.page_number;
4712 if (page_id < 0 || page_id > Zathura.PDF.number_of_pages) return FALSE;
4714 gdk_window_clear(widget->window);
4716 int window_x, window_y;
4717 gdk_drawable_get_size(widget->window, &window_x, &window_y);
4719 const double scale = ((double)Zathura.PDF.scale/100.0);
4721 //Page *current_page = Zathura.PDF.pages[page_id];
4722 //double page_height = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width);
4724 //int yofs = -(page_height*Zathura.PDF.page_yskip_t*scale+Zathura.PDF.page_yskip_pix);
4725 int yofs = get_current_page_screen_ytop();
4727 while (yofs < window_y && page_id < Zathura.PDF.number_of_pages) {
4728 Page *current_page = Zathura.PDF.pages[page_id];
4729 const double width = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->width : current_page->height)*scale;
4730 const double height = (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180 ? current_page->height : current_page->width)*scale;
4732 int offset_x;
4734 if (window_x > width) {
4735 switch (Zathura.Global.xcenter_mode) {
4736 case CENTER_LEFT_TOP: offset_x = 0; break;
4737 case CENTER_RIGHT_BOTTOM: offset_x = window_x-width; break;
4738 default: offset_x = (window_x-width)/2; break;
4740 } else {
4741 offset_x = 0;
4744 if (Zathura.Search.draw) {
4745 GList *list;
4746 for (list = Zathura.Search.results; list && list->data; list = g_list_next(list)) {
4747 highlight_result(Zathura.Search.page, (PopplerRectangle *)list->data);
4749 Zathura.Search.draw = FALSE;
4752 cairo_t *cairo = gdk_cairo_create(widget->window);
4753 cairo_surface_t *surf = create_page_surface(page_id);
4754 cairo_set_source_surface(cairo, surf, offset_x, yofs);
4755 cairo_paint(cairo);
4756 cairo_destroy(cairo);
4758 yofs += height+Zathura.PDF.page_gap;
4759 page_id += 1;
4762 return TRUE;
4766 //==========================================================================
4768 // cb_inputbar_kb_pressed
4770 //==========================================================================
4771 gboolean cb_inputbar_kb_pressed (GtkWidget *widget, GdkEventKey *event, gpointer data)
4773 int i;
4775 /* inputbar shortcuts */
4776 for(i = 0; i < LENGTH(inputbar_shortcuts); i++)
4778 if(event->keyval == inputbar_shortcuts[i].key &&
4779 (((event->state&inputbar_shortcuts[i].mask) == inputbar_shortcuts[i].mask)
4780 || inputbar_shortcuts[i].mask == 0))
4782 inputbar_shortcuts[i].function(&(inputbar_shortcuts[i].argument));
4783 return TRUE;
4787 /* special commands */
4788 char* identifier_string = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 0, 1);
4789 char identifier = identifier_string[0];
4791 for(i = 0; i < LENGTH(special_commands); i++)
4793 if((identifier == special_commands[i].identifier) &&
4794 (special_commands[i].always == 1))
4796 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 1, -1);
4797 guint new_utf_char = gdk_keyval_to_unicode(event->keyval);
4799 if(new_utf_char != 0)
4801 gchar* newchar = g_malloc0(6*sizeof(gchar));
4802 if(newchar == NULL)
4804 g_free(input);
4805 continue;
4808 gint len = g_unichar_to_utf8(new_utf_char, newchar);
4809 newchar[len] = 0;
4810 gchar* tmp = g_strconcat(input, newchar, NULL);
4812 g_free(input);
4813 g_free(newchar);
4815 input = tmp;
4818 // FIXME
4819 if((special_commands[i].function == scmd_search) && (event->keyval == GDK_Return))
4821 Argument argument = { NO_SEARCH, NULL };
4822 scmd_search(input, &argument);
4824 else
4826 special_commands[i].function(input, &(special_commands[i].argument));
4829 g_free(identifier_string);
4830 g_free(input);
4831 return FALSE;
4835 g_free(identifier_string);
4837 return FALSE;
4841 //==========================================================================
4843 // cb_inputbar_activate
4845 //==========================================================================
4846 gboolean cb_inputbar_activate (GtkEntry *entry, gpointer data)
4848 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
4849 gchar **tokens = g_strsplit(input, " ", -1);
4850 g_free(input);
4852 gchar *command = tokens[0];
4853 int length = g_strv_length(tokens);
4854 int i = 0;
4855 gboolean retv = FALSE;
4856 gboolean succ = FALSE;
4858 /* no input */
4859 if(length < 1)
4861 isc_abort(NULL);
4862 g_strfreev(tokens);
4863 return FALSE;
4866 /* append input to the command history */
4867 Zathura.Global.history = g_list_append(Zathura.Global.history, g_strdup(gtk_entry_get_text(entry)));
4869 /* special commands */
4870 char identifier = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, 1)[0];
4871 for(i = 0; i < LENGTH(special_commands); i++)
4873 if(identifier == special_commands[i].identifier)
4875 /* special commands that are evaluated every key change are not
4876 * called here */
4877 if(special_commands[i].always == 1)
4879 isc_abort(NULL);
4880 g_strfreev(tokens);
4881 return TRUE;
4884 retv = special_commands[i].function(input, &(special_commands[i].argument));
4885 if(retv) isc_abort(NULL);
4886 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
4887 g_strfreev(tokens);
4888 return TRUE;
4892 /* search commands */
4893 for(i = 0; i < LENGTH(commands); i++)
4895 if((g_strcmp0(command, commands[i].command) == 0) ||
4896 (g_strcmp0(command, commands[i].abbr) == 0))
4898 retv = commands[i].function(length-1, tokens+1);
4899 succ = TRUE;
4900 break;
4904 if(retv)
4905 isc_abort(NULL);
4907 if(!succ) {
4908 /* it maybe a goto command */
4909 if(!try_goto(command))
4910 notify(ERROR, "Unknown command.");
4913 Argument arg = { HIDE };
4914 isc_completion(&arg);
4915 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
4917 g_strfreev(tokens);
4918 return TRUE;
4922 //==========================================================================
4924 // cb_inputbar_form_activate
4926 //==========================================================================
4927 gboolean cb_inputbar_form_activate (GtkEntry *entry, gpointer data)
4929 if(!Zathura.PDF.document)
4930 return TRUE;
4932 Page* current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
4933 int number_of_links = 0, link_id = 1, new_page_id = Zathura.PDF.page_number;
4935 MTLOCK_PDFLIB();
4936 GList *link_list = poppler_page_get_link_mapping(current_page->page);
4937 MTUNLOCK_PDFLIB();
4938 link_list = g_list_reverse(link_list);
4940 if((number_of_links = g_list_length(link_list)) <= 0)
4941 return FALSE;
4943 /* parse entry */
4944 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
4945 gchar *token = input+strlen("Follow hint: ")-1;
4946 if(!token)
4947 return FALSE;
4949 int li = atoi(token);
4950 if(li <= 0 || li > number_of_links)
4952 set_page(Zathura.PDF.page_number);
4953 isc_abort(NULL);
4954 notify(WARNING, "Invalid hint");
4955 return TRUE;
4958 /* compare entry */
4959 GList *links;
4960 for(links = link_list; links; links = g_list_next(links))
4962 PopplerLinkMapping *link_mapping = (PopplerLinkMapping*) links->data;
4963 PopplerAction *action = link_mapping->action;
4965 /* only handle URI and internal links */
4966 if(action->type == POPPLER_ACTION_URI)
4968 if(li == link_id)
4969 open_uri(action->uri.uri);
4971 else if(action->type == POPPLER_ACTION_GOTO_DEST)
4973 if(li == link_id)
4975 if(action->goto_dest.dest->type == POPPLER_DEST_NAMED)
4977 PopplerDest* destination = poppler_document_find_dest(Zathura.PDF.document, action->goto_dest.dest->named_dest);
4978 if(destination)
4980 new_page_id = destination->page_num-1;
4981 poppler_dest_free(destination);
4984 else
4985 new_page_id = action->goto_dest.dest->page_num-1;
4988 else
4989 continue;
4991 link_id++;
4994 poppler_page_free_link_mapping(link_list);
4996 /* reset all */
4997 set_page(new_page_id);
4998 isc_abort(NULL);
5000 return TRUE;
5004 //==========================================================================
5006 // cb_inputbar_password_activate
5008 //==========================================================================
5009 gboolean cb_inputbar_password_activate (GtkEntry *entry, gpointer data)
5011 gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
5012 gchar *token = input+strlen("Enter password: ")-1;
5013 if(!token)
5014 return FALSE;
5016 if(!open_file(Zathura.PDF.file, token))
5018 enter_password();
5019 return TRUE;
5022 /* replace default inputbar handler */
5023 g_signal_handler_disconnect((gpointer) Zathura.UI.inputbar, Zathura.Handler.inputbar_activate);
5024 Zathura.Handler.inputbar_activate = g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "activate", G_CALLBACK(cb_inputbar_activate), NULL);
5026 isc_abort(NULL);
5028 return TRUE;
5032 //==========================================================================
5034 // cb_view_kb_pressed
5036 //==========================================================================
5037 gboolean cb_view_kb_pressed (GtkWidget *widget, GdkEventKey *event, gpointer data)
5039 //fprintf(stderr, "KEY=0x%04X\n", event->keyval);
5040 ShortcutList* sc = Zathura.Bindings.sclist;
5041 while(sc)
5044 event->keyval == sc->element.key
5045 && (CLEAN(event->state) == sc->element.mask || (sc->element.key >= 0x21
5046 && sc->element.key <= 0x7E && CLEAN(event->state) == GDK_SHIFT_MASK))
5047 && (Zathura.Global.mode&sc->element.mode || sc->element.mode == ALL)
5048 && sc->element.function
5051 if(!(Zathura.Global.buffer && strlen(Zathura.Global.buffer->str)) || (sc->element.mask == GDK_CONTROL_MASK) ||
5052 (sc->element.key <= 0x21 || sc->element.key >= 0x7E)
5055 sc->element.function(&(sc->element.argument));
5056 return TRUE;
5060 sc = sc->next;
5063 if(Zathura.Global.mode == ADD_MARKER)
5065 add_marker(event->keyval);
5066 change_mode(NORMAL);
5067 return TRUE;
5069 else if(Zathura.Global.mode == EVAL_MARKER)
5071 eval_marker(event->keyval);
5072 change_mode(NORMAL);
5073 return TRUE;
5076 /* append only numbers and characters to buffer */
5077 if( (event->keyval >= 0x21) && (event->keyval <= 0x7E))
5079 if(!Zathura.Global.buffer)
5080 Zathura.Global.buffer = g_string_new("");
5082 Zathura.Global.buffer = g_string_append_c(Zathura.Global.buffer, event->keyval);
5083 gtk_label_set_markup((GtkLabel*) Zathura.Global.status_buffer, Zathura.Global.buffer->str);
5086 /* search buffer commands */
5087 if(Zathura.Global.buffer)
5089 int i;
5090 for(i = 0; i < LENGTH(buffer_commands); i++)
5092 regex_t regex;
5093 int status;
5095 regcomp(&regex, buffer_commands[i].regex, REG_EXTENDED);
5096 status = regexec(&regex, Zathura.Global.buffer->str, (size_t) 0, NULL, 0);
5097 regfree(&regex);
5099 if(status == 0)
5101 buffer_commands[i].function(Zathura.Global.buffer->str, &(buffer_commands[i].argument));
5102 g_string_free(Zathura.Global.buffer, TRUE);
5103 Zathura.Global.buffer = NULL;
5104 gtk_label_set_markup((GtkLabel*) Zathura.Global.status_buffer, "");
5106 return TRUE;
5111 return FALSE;
5115 //==========================================================================
5117 // cb_view_resized
5119 //==========================================================================
5120 gboolean cb_view_resized (GtkWidget *widget, GtkAllocation *allocation, gpointer data)
5122 Argument arg;
5123 arg.n = Zathura.Global.adjust_mode;
5124 sc_adjust_window(&arg);
5126 return TRUE;
5130 //==========================================================================
5132 // cb_view_button_pressed
5134 //==========================================================================
5135 gboolean cb_view_button_pressed (GtkWidget *widget, GdkEventButton *event, gpointer data)
5137 if(!Zathura.PDF.document)
5138 return FALSE;
5140 /* clean page */
5141 draw(Zathura.PDF.page_number);
5142 MTLOCK_SELECT();
5143 Zathura.SelectPoint.x = event->x;
5144 Zathura.SelectPoint.y = event->y;
5145 MTUNLOCK_SELECT();
5147 return TRUE;
5151 //==========================================================================
5153 // cb_view_button_release
5155 //==========================================================================
5156 gboolean cb_view_button_release (GtkWidget *widget, GdkEventButton *event, gpointer data) {
5157 if(!Zathura.PDF.document)
5158 return FALSE;
5160 double scale, offset_x, offset_y;
5161 PopplerRectangle rectangle;
5162 cairo_t* cairo;
5164 /* build selection rectangle */
5165 rectangle.x1 = event->x;
5166 rectangle.y1 = event->y;
5168 MTLOCK_SELECT();
5169 rectangle.x2 = Zathura.SelectPoint.x;
5170 rectangle.y2 = Zathura.SelectPoint.y;
5171 MTUNLOCK_SELECT();
5173 /* calculate offset */
5174 calculate_offset(widget, &offset_x, &offset_y);
5176 /* draw selection rectangle */
5177 cairo = cairo_create(create_page_surface(Zathura.PDF.page_number));
5178 cairo_set_source_rgba(cairo, Zathura.Style.select_text.red, Zathura.Style.select_text.green,
5179 Zathura.Style.select_text.blue, transparency);
5180 cairo_rectangle(cairo, rectangle.x1-offset_x, rectangle.y1-offset_y,
5181 (rectangle.x2-rectangle.x1), (rectangle.y2-rectangle.y1));
5182 cairo_fill(cairo);
5183 cairo_destroy(cairo);
5184 gtk_widget_queue_draw(Zathura.UI.drawing_area);
5186 /* resize selection rectangle to document page */
5187 Page *current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
5188 const double page_width = current_page->width;
5189 const double page_height = current_page->height;
5191 scale = ((double) Zathura.PDF.scale/100.0);
5192 rectangle.x1 = (rectangle.x1-offset_x)/scale;
5193 rectangle.y1 = (rectangle.y1-offset_y)/scale;
5194 rectangle.x2 = (rectangle.x2-offset_x)/scale;
5195 rectangle.y2 = (rectangle.y2-offset_y)/scale;
5197 /* rotation */
5198 int rotate = Zathura.PDF.rotate;
5199 double x1 = rectangle.x1;
5200 double x2 = rectangle.x2;
5201 double y1 = rectangle.y1;
5202 double y2 = rectangle.y2;
5204 switch(rotate)
5206 case 90:
5207 rectangle.x1 = y1;
5208 rectangle.y1 = page_height-x2;
5209 rectangle.x2 = y2;
5210 rectangle.y2 = page_height-x1;
5211 break;
5212 case 180:
5213 rectangle.x1 = (page_height-y1);
5214 rectangle.y1 = (page_width-x2);
5215 rectangle.x2 = (page_height-y2);
5216 rectangle.y2 = (page_width-x1);
5217 break;
5218 case 270:
5219 rectangle.x1 = page_width-y2;
5220 rectangle.y1 = x1;
5221 rectangle.x2 = page_width-y1;
5222 rectangle.y2 = x2;
5223 break;
5226 /* reset points of the rectangle so that p1 is in the top-left corner
5227 * and p2 is in the bottom right corner */
5228 if(rectangle.x1 > rectangle.x2)
5230 double d = rectangle.x1;
5231 rectangle.x1 = rectangle.x2;
5232 rectangle.x2 = d;
5234 if(rectangle.y2 > rectangle.y1)
5236 double d = rectangle.y1;
5237 rectangle.y1 = rectangle.y2;
5238 rectangle.y2 = d;
5241 #if !POPPLER_CHECK_VERSION(0,15,0)
5242 /* adapt y coordinates */
5243 rectangle.y1 = page_height-rectangle.y1;
5244 rectangle.y2 = page_height-rectangle.y2;
5245 #endif
5247 /* get selected text */
5248 MTLOCK_PDFLIB();
5249 char* selected_text = poppler_page_get_selected_text(
5250 Zathura.PDF.pages[Zathura.PDF.page_number]->page,SELECTION_STYLE,
5251 &rectangle);
5253 if(selected_text)
5255 gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), selected_text, -1);
5256 g_free(selected_text);
5258 MTUNLOCK_PDFLIB();
5261 return TRUE;
5265 //==========================================================================
5267 // cb_view_motion_notify
5269 //==========================================================================
5270 gboolean cb_view_motion_notify (GtkWidget *widget, GdkEventMotion *event, gpointer data)
5272 return TRUE;
5276 //==========================================================================
5278 // cb_view_scrolled
5280 //==========================================================================
5281 gboolean cb_view_scrolled (GtkWidget *widget, GdkEventScroll *event, gpointer data)
5283 int i;
5284 for(i = 0; i < LENGTH(mouse_scroll_events); i++)
5286 if(event->direction == mouse_scroll_events[i].direction)
5288 mouse_scroll_events[i].function(&(mouse_scroll_events[i].argument));
5289 return TRUE;
5293 return FALSE;
5297 //==========================================================================
5299 // cb_watch_file
5301 //==========================================================================
5302 gboolean cb_watch_file (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event, gpointer data) {
5303 if(event != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
5304 return FALSE;
5306 sc_reload(NULL);
5308 return TRUE;
5313 //**************************************************************************
5315 // main function
5317 //**************************************************************************
5319 int main (int argc, char *argv[]) {
5320 /* embed */
5321 Zathura.UI.embed = 0;
5323 Zathura.Config.config_dir = 0;
5324 Zathura.Config.data_dir = 0;
5326 char* config_dir = 0;
5327 char* data_dir = 0;
5328 GOptionEntry entries[] =
5330 { "reparent", 'e', 0, G_OPTION_ARG_INT, &Zathura.UI.embed, "Reparents to window specified by xid", "xid" },
5331 { "config-dir", 'c', 0, G_OPTION_ARG_FILENAME, &config_dir, "Path to the config directory", "path" },
5332 { "data-dir", 'd', 0, G_OPTION_ARG_FILENAME, &data_dir, "Path to the data directory", "path" },
5333 { NULL }
5336 GOptionContext* context = g_option_context_new(" [file] [password]");
5337 g_option_context_add_main_entries(context, entries, NULL);
5339 GError* error = NULL;
5340 if(!g_option_context_parse(context, &argc, &argv, &error))
5342 printf("Error parsing command line arguments: %s\n", error->message);
5343 g_option_context_free(context);
5344 g_error_free(error);
5345 return 1;
5347 g_option_context_free(context);
5349 if (config_dir)
5350 Zathura.Config.config_dir = g_strdup(config_dir);
5351 if (data_dir)
5352 Zathura.Config.data_dir = g_strdup(data_dir);
5354 g_thread_init(NULL);
5355 gdk_threads_init();
5357 gtk_init(&argc, &argv);
5359 init_zathura();
5360 init_directories();
5361 init_keylist();
5362 read_configuration();
5363 init_settings();
5364 init_bookmarks();
5365 init_look();
5367 if(argc > 1)
5369 char* password = (argc == 3) ? argv[2] : NULL;
5370 if (strcmp(argv[1], "-") == 0)
5371 open_stdin(password);
5372 else
5373 open_file(argv[1], password);
5376 switch_view(Zathura.UI.document);
5377 update_status();
5379 gtk_widget_show_all(GTK_WIDGET(Zathura.UI.window));
5380 gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
5382 if(!Zathura.Global.show_inputbar)
5383 gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
5385 if(!Zathura.Global.show_statusbar)
5386 gtk_widget_hide(GTK_WIDGET(Zathura.UI.statusbar));
5388 gdk_threads_enter();
5389 gtk_main();
5390 gdk_threads_leave();
5392 return 0;