1 /* See LICENSE file for license and copyright information */
5 #if defined(_XOPEN_SOURCE) && _XOPEN_SOURCE < 500
9 # define _XOPEN_SOURCE 500
12 #ifndef _DEFAULT_SOURCE
13 # define _DEFAULT_SOURCE
24 #include <poppler/glib/poppler.h>
27 #include <glib/gstdio.h>
29 #include <gdk/gdkkeysyms.h>
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
57 //**************************************************************************
61 //**************************************************************************
63 // maximum surfaces in page surface cache
64 #define SURF_CACHE_SIZE (16)
66 typedef struct PageSurfCache_t
{
67 cairo_surface_t
*surf
;
70 struct PageSurfCache_t
*prev
;
71 struct PageSurfCache_t
*next
;
75 PageSurfCache
*surfCacheHead
= NULL
;
76 PageSurfCache
*surfCacheTail
= NULL
;
79 //==========================================================================
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
));
102 surfCacheTail
= last
;
106 //==========================================================================
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
);
118 surfCacheHead
= surfCacheTail
= NULL
;
122 //==========================================================================
126 //==========================================================================
127 static void surfCacheRemove (PageSurfCache
*c
) {
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 //==========================================================================
139 //==========================================================================
140 static void surfCacheToFront (PageSurfCache
*c
) {
141 if (!c
|| c
== surfCacheHead
) return; // nothing to do
144 c
->next
= surfCacheHead
;
145 surfCacheHead
->prev
= c
;
150 //==========================================================================
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
) {
163 for (PageSurfCache
*c
= surfCacheHead
; c
; c
= c
->next
) {
164 if (c
->page_id
== pgid
) {
166 if (c
->scale
!= scale
) {
167 if (c
->surf
) cairo_surface_destroy(c
->surf
);
174 // not found, use tail
175 PageSurfCache
*res
= surfCacheTail
;
176 surfCacheToFront(res
);
177 if (res
->surf
) cairo_surface_destroy(res
->surf
);
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
,
203 #define FULLSCREEN (1<<1)
205 #define NORMAL (1<<3)
209 typedef struct CElement
{
212 struct CElement
*next
;
216 typedef struct CGroup
{
218 CompletionElement
*elements
;
225 CompletionGroup
*groups
;
253 void (*function
) (Argument
*);
268 void (*function
) (Argument
*);
275 void (*function
) (Argument
*);
282 void (*function
) (Argument
*);
290 gboolean (*function
) (int, char **);
291 Completion
* (*completion
) (char *);
298 void (*function
) (char *, Argument
*);
305 gboolean (*function
) (char *, Argument
*);
311 typedef struct SCList
{
360 GtkScrolledWindow
*view
;
361 GtkViewport
*viewport
;
362 GtkWidget
*statusbar
;
363 GtkBox
*statusbar_entries
;
366 GtkWidget
*information
;
367 GtkWidget
*drawing_area
;
369 GdkNativeWindow embed
;
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
;
397 GtkLabel
*status_text
;
398 GtkLabel
*status_buffer
;
399 GtkLabel
*status_state
;
405 gboolean enable_labelmode
;
409 gboolean show_statusbar
;
410 gboolean show_inputbar
;
415 ShortcutList
*sclist
;
426 int scroll_percentage
;
431 int number_of_markers
;
436 GFileMonitor
*monitor
;
444 int number_of_bookmarks
;
448 PopplerDocument
*document
;
453 double page_yskip_t
; // [0..1]
454 int page_yskip_pix
; // in screen pixels; for a gap
458 int page_gap
; // usually 8
459 //TODO: cache total height and other things?
463 GStaticMutex pdflib_lock
;
464 GStaticMutex pdf_obj_lock
;
465 GStaticMutex search_lock
;
466 GStaticMutex select_lock
;
477 GThread
*search_thread
;
478 gboolean search_thread_running
;
479 GThread
*inotify_thread
;
483 guint inputbar_activate
;
484 guint inputbar_key_press_event
;
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);
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
);
629 //==========================================================================
633 //==========================================================================
634 static void *safe_realloc (void **ptr
, size_t nmemb
, size_t size
) {
635 static const size_t limit
= ~((size_t)0u);
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
;
644 /* Error handling. */
652 //==========================================================================
656 //==========================================================================
657 void init_look (void) {
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
));
688 gtk_widget_modify_bg(GTK_WIDGET(Zathura
.UI
.drawing_area
), GTK_STATE_NORMAL
, &(Zathura
.Style
.default_bg
));
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
);
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
);
709 if (show_scrollbars
) {
710 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura
.UI
.view
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
712 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura
.UI
.view
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
716 if (Zathura
.Global
.show_inputbar
) {
717 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.inputbar
));
719 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
723 if (Zathura
.Global
.show_statusbar
) {
724 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.statusbar
));
726 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.statusbar
));
731 //==========================================================================
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
);
744 return g_strdup(path
);
748 //==========================================================================
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
);
762 //==========================================================================
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 //==========================================================================
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
;
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
;
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 //==========================================================================
807 //==========================================================================
808 void init_bookmarks (void) {
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
;
830 //==========================================================================
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
];
842 if (!Zathura
.Bindings
.sclist
) Zathura
.Bindings
.sclist
= e
;
849 //==========================================================================
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 //==========================================================================
868 //==========================================================================
869 void init_zathura (void) {
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
));
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
;
903 if (Zathura
.UI
.embed
) {
904 Zathura
.UI
.window
= gtk_plug_new(Zathura
.UI
.embed
);
906 Zathura
.UI
.window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
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();
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
);
927 gtk_box_set_spacing(Zathura
.UI
.box
, 0);
928 gtk_container_add(GTK_CONTAINER(Zathura
.UI
.window
), GTK_WIDGET(Zathura
.UI
.box
));
931 gtk_box_set_spacing(Zathura
.UI
.continuous
, 5);
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
);
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
);
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
);
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
));
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
);
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 //==========================================================================
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
;
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 //==========================================================================
1022 //==========================================================================
1023 void build_index (GtkTreeModel
*model
, GtkTreeIter
*parent
, PopplerIndexIter
*index_iter
) {
1025 GtkTreeIter tree_iter
;
1026 PopplerIndexIter
*child
;
1027 PopplerAction
*action
;
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
);
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);
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);
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);
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;
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) {
1119 *out_page_yskip_t
= 0;
1120 *out_page_yskip_pix
= 0;
1124 if (docofs
< 0) docofs
= 0;
1126 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1128 //fprintf(stderr, "docofs=%d\n", docofs);
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;
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);
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
) {
1182 int currypos
= calc_current_document_screen_offset();
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
) {
1195 int currypos
= calc_current_document_screen_offset();
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
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
;
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
);
1235 cairo_set_source_rgb(cairo
, 1, 1, 1);
1236 cairo_rectangle(cairo
, 0, 0, width
, height
);
1238 cairo_restore(cairo
);
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);
1252 poppler_page_render(current_page
->page
, cairo
);
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;
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;
1296 //fprintf(stderr, "reusing new page surface for page #%d\n", page_id);
1303 //==========================================================================
1307 //==========================================================================
1308 void draw (int page_id
) {
1309 cairo_surface_t
*surf
= create_page_surface(page_id
);
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 //==========================================================================
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
;
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 //==========================================================================
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
;
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 //==========================================================================
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
);
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();
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
;
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
);
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;
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
;
1460 if (Zathura
.Marker
.markers
) free(Zathura
.Marker
.markers
);
1461 Zathura
.Marker
.number_of_markers
= 0;
1462 Zathura
.Marker
.last
= -1;
1468 //==========================================================================
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
);
1479 argument
.data
= "Enter password: ";
1480 sc_focus_inputbar(&argument
);
1484 //==========================================================================
1488 //==========================================================================
1489 void eval_marker (int id
) {
1490 /* go to last marker */
1492 int current_page
= Zathura
.PDF
.page_number
;
1493 set_page(Zathura
.Marker
.last
);
1494 Zathura
.Marker
.last
= current_page
;
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
);
1507 //==========================================================================
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
);
1521 cairo_destroy(cairo
);
1525 //==========================================================================
1529 //==========================================================================
1530 void notify (int level
, const char *message
) {
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
));
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
));
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
));
1545 if (message
) gtk_entry_set_text(Zathura
.UI
.inputbar
, message
);
1549 //==========================================================================
1553 //==========================================================================
1554 gboolean
open_file (char *path
, char *password
) {
1557 /* specify path max */
1562 pm
= pathconf(path
, _PC_PATH_MAX
);
1563 if (pm
<= 0) pm
= 4096;
1567 if (path
[0] == '~') {
1568 gchar
*home_path
= get_home_dir();
1569 rpath
= g_build_filename(home_path
, path
+1, NULL
);
1572 rpath
= g_strdup(path
);
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");
1588 /* check if file exists */
1589 if (!g_file_test(file
, G_FILE_TEST_IS_REGULAR
)) {
1590 notify(ERROR
, "File does not exist");
1596 /* close old file */
1602 GError
*error
= NULL
;
1603 char *file_uri
= g_filename_to_uri(file
, NULL
, &error
);
1605 if (file
) g_free(file
);
1606 char *message
= g_strdup_printf("Can not open file: %s", error
->message
);
1607 notify(ERROR
, message
);
1609 g_error_free(error
);
1616 Zathura
.PDF
.document
= poppler_document_new_from_file(file_uri
, password
, &error
);
1619 if (!Zathura
.PDF
.document
) {
1620 if (error
->code
== 1) {
1622 g_error_free(error
);
1623 Zathura
.PDF
.file
= file
;
1628 char *message
= g_strdup_printf("Can not open file: %s", error
->message
);
1629 notify(ERROR
, message
);
1632 g_error_free(error
);
1639 g_free(Zathura
.PDF
.password
);
1640 Zathura
.PDF
.password
= (password
? g_strdup(password
) : NULL
);
1643 if (!Zathura
.FileMonitor
.monitor
) {
1644 GFile
*file
= g_file_new_for_uri(file_uri
);
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
;
1655 Zathura
.PDF
.number_of_pages
= poppler_document_get_n_pages(Zathura
.PDF
.document
);
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
*));
1667 if (!Zathura
.PDF
.pages
) out_of_memory();
1669 /* get pages and check label mode */
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);
1692 /* set correct goto mode */
1693 if (!Zathura
.Global
.enable_labelmode
&& GOTO_MODE
== GOTO_LABELS
) Zathura
.Global
.goto_mode
= GOTO_DEFAULT
;
1697 Zathura
.PDF
.page_yskip_t
= 0;
1698 Zathura
.PDF
.page_yskip_pix
= 0;
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
);
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
;
1743 /* set window title */
1744 gtk_window_set_title(GTK_WINDOW(Zathura
.UI
.window
), basename(file
));
1747 set_page_keep_ofs(start_page
);
1756 //==========================================================================
1760 //==========================================================================
1761 gboolean
open_stdin (gchar
*password
) {
1762 GError
*error
= NULL
;
1764 gint handle
= g_file_open_tmp("zathura.stdin.XXXXXX.pdf", &file
, &error
);
1766 gchar
*message
= g_strdup_printf("Can not create temporary file: %s", error
->message
);
1767 notify(ERROR
, message
);
1769 g_error_free(error
);
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
);
1786 char buffer
[BUFSIZ
];
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
);
1802 gchar
*message
= g_strdup_printf("Can not read from stdin.");
1803 notify(ERROR
, message
);
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 //==========================================================================
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
);
1829 g_free(escaped_uri
);
1833 //==========================================================================
1837 //==========================================================================
1838 void out_of_memory (void) {
1839 printf("error: out of memory\n");
1845 //==========================================================================
1849 //==========================================================================
1850 void update_status (void) {
1852 gtk_label_set_markup((GtkLabel
*)Zathura
.Global
.status_text
, Zathura
.State
.filename
);
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
);
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
);
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
);
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
);
1910 //==========================================================================
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
;
1942 for (int f
= 0; f
!= pre_length
; ++f
) {
1943 if (strlen(pre_tokens
[f
])) {
1944 *tokp
++ = pre_tokens
[f
];
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);
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
);
1971 //==========================================================================
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
;
1989 rectangle
->x1
= y2
*scale
;
1990 rectangle
->y1
= x1
*scale
;
1991 rectangle
->x2
= y1
*scale
;
1992 rectangle
->y2
= x2
*scale
;
1995 rectangle
->x1
= (page_width
-x2
)*scale
;
1996 rectangle
->y1
= y2
*scale
;
1997 rectangle
->x2
= (page_width
-x1
)*scale
;
1998 rectangle
->y2
= y1
*scale
;
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
;
2007 rectangle
->x1
= x1
*scale
;
2008 rectangle
->y1
= (page_height
-y1
)*scale
;
2009 rectangle
->x2
= x2
*scale
;
2010 rectangle
->y2
= (page_height
-y2
)*scale
;
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);
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);
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
);
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
));
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);
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
);
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
));
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");
2098 Zathura
.PDF
.page_number
= page
;
2099 Zathura
.Search
.draw
= FALSE
;
2100 switch_view(Zathura
.UI
.document
);
2105 //==========================================================================
2109 //==========================================================================
2110 void set_page (int page
) {
2111 if (page
>= Zathura
.PDF
.number_of_pages
|| page
< 0) {
2112 notify(WARNING
, "Could not open page");
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
;
2121 switch_view(Zathura
.UI
.document
);
2123 sc_scroll(&argument
);
2127 //==========================================================================
2131 //==========================================================================
2132 void switch_view (GtkWidget
*widget
) {
2133 GtkWidget
*child
= gtk_bin_get_child(GTK_BIN(Zathura
.UI
.viewport
));
2134 if (child
== widget
) return;
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 //==========================================================================
2147 //==========================================================================
2148 Completion
*completion_init (void) {
2149 Completion
*completion
= malloc(sizeof(Completion
));
2150 if (!completion
) out_of_memory();
2151 completion
->groups
= NULL
;
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
;
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 //==========================================================================
2187 //==========================================================================
2188 void completion_free (Completion
*completion
) {
2189 CompletionGroup
*group
= completion
->groups
;
2191 CompletionGroup
*ng
;
2192 CompletionElement
*element
= group
->elements
;
2194 CompletionElement
*ne
= element
->next
;
2195 g_free(element
->value
);
2196 g_free(element
->description
);
2201 g_free(group
->value
);
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 //==========================================================================
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
);
2251 if (!Zathura
.PDF
.document
|| !search_item
|| !strlen(search_item
)) {
2254 Zathura
.Thread
.search_thread_running
= FALSE
;
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
;
2274 int page_counter
= (g_strcmp0(old_query
,search_item
) == 0 ? 1 : 0);
2275 for( ; page_counter
<= number_of_pages
; ++page_counter
) {
2277 if (Zathura
.Thread
.search_thread_running
== FALSE
) {
2280 g_thread_exit(NULL
);
2284 next_page
= (number_of_pages
+page_number
+page_counter
*direction
)%number_of_pages
;
2287 PopplerPage
*page
= poppler_document_get_page(Zathura
.PDF
.document
, next_page
);
2292 g_thread_exit(NULL
);
2296 results
= poppler_page_find_text(page
, search_item
);
2299 g_object_unref(page
);
2304 Zathura
.Search
.draw
= TRUE
;
2305 g_free(argument
->data
);
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();
2326 Zathura
.Thread
.search_thread_running
= FALSE
;
2330 g_thread_exit(NULL
);
2335 //==========================================================================
2339 // shortcut implementation
2341 //==========================================================================
2342 void sc_abort (Argument
*argument
) {
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 //==========================================================================
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
;
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
);
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
;
2390 if (argument
->n
== ADJUST_BESTFIT
) {
2391 Zathura
.PDF
.scale
= (view_size
/page_height
)*100;
2393 Zathura
.PDF
.scale
= (view_size
/page_width
)*100;
2396 draw(Zathura
.PDF
.page_number
);
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
);
2414 //==========================================================================
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
, "");
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 //==========================================================================
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
) {
2456 if (argument
->n
== APPEND_FILEPATH
) {
2457 data
= g_strdup_printf("%s%s", (const char *)argument
->data
, Zathura
.PDF
.file
);
2459 data
= g_strdup((const char *)argument
->data
);
2461 notify(DEFAULT
, data
);
2463 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.inputbar
));
2464 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), -1);
2469 //==========================================================================
2473 //==========================================================================
2474 void sc_follow (Argument
*argument
) {
2475 if (!Zathura
.PDF
.document
) return;
2476 Page
*current_page
= Zathura
.PDF
.pages
[Zathura
.PDF
.page_number
];
2480 GList
*link_list
= poppler_page_get_link_mapping(current_page
->page
);
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
);
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 //==========================================================================
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;
2542 //==========================================================================
2546 //==========================================================================
2547 void sc_recolor (Argument
*argument
) {
2548 Zathura
.Global
.recolor
= !Zathura
.Global
.recolor
;
2549 draw(Zathura
.PDF
.page_number
);
2553 //==========================================================================
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 */
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
);
2575 /* reopen and restore settings */
2577 open_file(path
, password
);
2580 Zathura
.PDF
.scale
= scale
;
2581 Zathura
.PDF
.rotate
= rotate
;
2583 gtk_adjustment_set_value(vadjustment
, va
);
2584 gtk_adjustment_set_value(hadjustment
, ha
);
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 //==========================================================================
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 //==========================================================================
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
);
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
);
2635 scroll_down_pixels_no_draw(delta
);
2637 int top_page
= Zathura
.PDF
.page_number
;
2639 if (top_page
>= 0 && top_page
< Zathura
.PDF
.number_of_pages
) {
2640 Zathura
.Search
.draw
= TRUE
;
2642 gtk_adjustment_set_value(adjustment
, 0);
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;
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
;
2663 if (scroll_wrap
|| Zathura
.PDF
.page_number
< old_page
) {
2669 } else if ((argument
->n
== DOWN
|| argument
->n
== HALF_DOWN
|| argument
->n
== FULL_DOWN
) /*&& value == max*/) {
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) {
2694 if (new_value > value) {
2695 for (i = value; i+smooth_scrolling < new_value; i += smooth_scrolling) gtk_adjustment_set_value(adjustment, i);
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);
2709 //==========================================================================
2713 //==========================================================================
2714 void sc_search (Argument
*argument
) {
2716 if (Zathura
.Thread
.search_thread_running
) {
2717 Zathura
.Thread
.search_thread_running
= FALSE
;
2719 gdk_threads_leave();
2720 g_thread_join(Zathura
.Thread
.search_thread
);
2721 gdk_threads_enter();
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
);
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;
2744 if (Zathura
.Global
.enable_labelmode
) Zathura
.Global
.goto_mode
= GOTO_LABELS
; else Zathura
.Global
.goto_mode
= GOTO_OFFSET
;
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
;
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
);
2775 page_number
= d
->page_num
;
2776 poppler_dest_free(d
);
2779 set_page(page_number
-1);
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
);
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
;
2804 gtk_tree_view_get_cursor(treeview
, &path
, NULL
);
2807 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
2809 GtkTreeIter child_iter
;
2811 gboolean is_valid_path
= TRUE
;
2813 switch (argument
->n
) {
2815 if (!gtk_tree_path_prev(path
)) {
2816 is_valid_path
= (gtk_tree_path_get_depth(path
) > 1 && gtk_tree_path_up(path
));
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
);
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
);
2835 if (gtk_tree_view_row_expanded(treeview
, path
)) {
2836 gtk_tree_path_down(path
);
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
);
2844 } while ((is_valid_path
= (gtk_tree_path_get_depth(path
) > 1)) && gtk_tree_path_up(path
));
2848 if (gtk_tree_view_expand_row(treeview
, path
, FALSE
)) gtk_tree_path_down(path
);
2851 cb_index_row_activated(treeview
, path
, NULL
, NULL
);
2855 if (is_valid_path
) gtk_tree_view_set_cursor(treeview
, path
, NULL
, FALSE
);
2857 gtk_tree_path_free(path
);
2861 //==========================================================================
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
));
2881 build_index(model
, NULL
, iter
);
2883 poppler_index_iter_free(iter
);
2885 notify(WARNING
, "This document does not contain any index");
2886 Zathura
.UI
.index
= NULL
;
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
;
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
));
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
;
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
));
2946 arg
.n
= ADJUST_BESTFIT
;
2947 sc_adjust_window(&arg
);
2949 Zathura
.Global
.mode
= FULLSCREEN
;
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
;
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
));
2972 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.statusbar
));
2977 //==========================================================================
2981 //==========================================================================
2982 void sc_quit (Argument
*argument
) {
2983 cb_destroy(NULL
, NULL
);
2987 //==========================================================================
2991 //==========================================================================
2992 void sc_zoom (Argument
*argument
) {
2993 bcmd_zoom(NULL
, argument
);
2997 //**************************************************************************
2999 // inputbar shortcut declarations
3001 //**************************************************************************
3003 //==========================================================================
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
);
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
);
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 //==========================================================================
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
);
3060 /* get current information*/
3061 char *first_space
= strstr(input
, " ");
3062 char *current_command
;
3063 char *current_parameter
;
3064 int current_command_length
;
3067 current_command
= g_strdup(input
);
3068 current_command_length
= length
;
3069 current_parameter
= NULL
;
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
);
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
));
3111 for (int i
= 0; i
!= n_items
; ++i
) {
3112 g_free(rows
[i
].command
);
3113 g_free(rows
[i
].description
);
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
);
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
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
;
3152 search_matching_command
= TRUE
;
3154 if (current_command
) g_free(current_command
);
3155 if (input
) g_free(input
);
3156 if (tmp_string
) g_free(tmp_string
);
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
);
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
);
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
)
3192 if(group
->value
&& !group_elements
)
3194 rows
= safe_realloc((void**)&rows
, n_items
+1, sizeof(CompletionRow
));
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
));
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
));
3218 completion_free(result
);
3220 /* create list based on commands */
3224 command_mode
= TRUE
;
3226 rows
= malloc(LENGTH(commands
)*sizeof(CompletionRow
));
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
));
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
);
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
))
3288 if(!command_mode
&& (next_group
== 0) && (argument
->n
== NEXT_GROUP
|| argument
->n
== PREVIOUS_GROUP
))
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
);
3309 gtk_widget_hide(rows
[i
].row
);
3313 temp
= g_strconcat(":", rows
[current_item
].command
, (n_items
== 1) ? " " : NULL
, NULL
);
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);
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
;
3328 g_free(current_command
);
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
));
3348 switch (argument
->n
) {
3349 case DELETE_LAST_WORD
:
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
] != '/'))
3362 gtk_editable_delete_text(GTK_EDITABLE(Zathura
.UI
.inputbar
), i
, pos
);
3363 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), i
);
3365 case DELETE_LAST_CHAR
:
3369 gtk_editable_delete_text(GTK_EDITABLE(Zathura
.UI
.inputbar
), pos
-1, pos
);
3371 case DELETE_TO_LINE_START
:
3372 gtk_editable_delete_text(GTK_EDITABLE(Zathura
.UI
.inputbar
), 1, pos
);
3375 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), pos
+1);
3378 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), (pos
== 0) ? 0 : pos
-1);
3380 default: /* unreachable */
3386 //**************************************************************************
3388 // command implementation
3390 //**************************************************************************
3392 //==========================================================================
3396 //==========================================================================
3397 gboolean
cmd_bookmark (int argc
, char **argv
) {
3398 if(!Zathura
.PDF
.document
|| argc
< 1)
3403 GString
*id
= g_string_new("");
3405 for(i
= 0; i
< argc
; i
++)
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
);
3420 if(is_reserved_bm_name(id
->str
))
3422 notify(WARNING
, "Can't set bookmark: reserved bookmark name");
3423 g_string_free(id
, TRUE
);
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
);
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
)
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
);
3459 //==========================================================================
3461 // cmd_open_bookmark
3463 //==========================================================================
3464 gboolean
cmd_open_bookmark (int argc
, char **argv
)
3466 if(!Zathura
.PDF
.document
|| argc
< 1)
3471 GString
*id
= g_string_new("");
3473 for(i
= 0; i
< argc
; i
++)
3476 id
= g_string_append_c(id
, ' ');
3478 id
= g_string_append(id
, argv
[i
]);
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
);
3492 notify(WARNING
, "No matching bookmark found");
3493 g_string_free(id
, TRUE
);
3498 //==========================================================================
3502 //==========================================================================
3503 gboolean
cmd_close (int argc
, char **argv
)
3511 //==========================================================================
3513 // cmd_delete_bookmark
3515 //==========================================================================
3516 gboolean
cmd_delete_bookmark (int argc
, char **argv
)
3518 if(!Zathura
.PDF
.document
|| argc
< 1)
3523 GString
*id
= g_string_new("");
3525 for(i
= 0; i
< argc
; i
++)
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
)
3553 Zathura
.Bookmarks
.number_of_bookmarks
--;
3554 g_string_free(id
, TRUE
);
3556 /* write bookmark file */
3557 write_bookmarks_file();
3563 g_string_free(id
, TRUE
);
3568 //==========================================================================
3572 //==========================================================================
3573 gboolean
cmd_export (int argc
, char **argv
)
3575 if(argc
== 0 || !Zathura
.PDF
.document
)
3580 notify(WARNING
, "No export path specified");
3585 if(!strcmp(argv
[0], "images"))
3588 for(page_number
= 0; page_number
< Zathura
.PDF
.number_of_pages
; page_number
++)
3592 cairo_surface_t
*image
;
3595 image_list
= poppler_page_get_image_mapping(Zathura
.PDF
.pages
[page_number
]->page
);
3598 if(!g_list_length(image_list
))
3600 notify(WARNING
, "This document does not contain any images");
3604 for(images
= image_list
; images
; images
= g_list_next(images
))
3606 PopplerImageMapping
*image_mapping
;
3611 image_mapping
= (PopplerImageMapping
*) images
->data
;
3612 image_id
= image_mapping
->image_id
;
3615 image
= poppler_page_get_image(Zathura
.PDF
.pages
[page_number
]->page
, image_id
);
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
);
3630 file
= g_strdup_printf("%s%s", argv
[1], filename
);
3632 cairo_surface_write_to_png(image
, file
);
3639 else if(!strcmp(argv
[0], "attachments"))
3642 if(!poppler_document_has_attachments(Zathura
.PDF
.document
))
3644 notify(WARNING
, "PDF file has no attachments");
3649 GList
*attachment_list
= poppler_document_get_attachments(Zathura
.PDF
.document
);
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
);
3666 file
= g_strdup_printf("%s%s", argv
[1], attachment
->name
);
3669 poppler_attachment_save(attachment
, file
, NULL
);
3680 //==========================================================================
3684 //==========================================================================
3685 gboolean
cmd_info (int argc
, char **argv
)
3687 if(!Zathura
.PDF
.document
)
3690 static gboolean visible
= FALSE
;
3692 if(!Zathura
.UI
.information
)
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
,
3710 "subject", &subject
,
3711 "keywords", &keywords
,
3712 "creator", &creator
,
3713 "producer", &producer
,
3714 "creation-date", &creation_date
,
3715 "mod-date", &modification_date
,
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
);
3747 switch_view(Zathura
.UI
.information
);
3749 switch_view(Zathura
.UI
.document
);
3757 //==========================================================================
3761 //==========================================================================
3762 gboolean
cmd_map (int argc
, char **argv
)
3769 /* search for the right shortcut function */
3773 for(sc_c
= 0; sc_c
< LENGTH(shortcut_names
); sc_c
++)
3775 if(!strcmp(argv
[1], shortcut_names
[sc_c
].name
))
3784 notify(WARNING
, "No such shortcut function exists");
3788 /* parse modifier and key */
3791 int keyl
= strlen(ks
);
3794 // single key (e.g.: g)
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 */
3812 mask
= GDK_SHIFT_MASK
;
3815 mask
= GDK_CONTROL_MASK
;
3819 /* no valid modifier */
3822 notify(WARNING
, "No valid modifier given.");
3826 /* modifier and special key */
3828 specialkey
= g_strndup(ks
+3, keyl
-4);
3835 /* search special key */
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
;
3852 notify(WARNING
, "No valid key binding given.");
3856 /* parse argument */
3857 Argument arg
= {0, 0};
3863 /* compare argument with given argument names... */
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
;
3874 /* if not, save it do .data */
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
;
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
;
3910 /* create new entry */
3911 ShortcutList
* entry
= malloc(sizeof(ShortcutList
));
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
;
3922 /* append to list */
3923 if(!Zathura
.Bindings
.sclist
)
3924 Zathura
.Bindings
.sclist
= entry
;
3933 //==========================================================================
3937 //==========================================================================
3938 gboolean
cmd_open (int argc
, char **argv
)
3940 if(argc
== 0 || strlen(argv
[0]) == 0)
3943 /* assembly the arguments back to one string */
3945 GString
*filepath
= g_string_new("");
3946 for(i
= 0; i
< argc
; i
++)
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
);
3960 //==========================================================================
3964 //==========================================================================
3965 gboolean
cmd_print (int argc
, char **argv
)
3967 if(!Zathura
.PDF
.document
)
3972 notify(WARNING
, "No printer specified");
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("");
3981 for(i
= 2; i
< argc
; i
++)
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
);
3994 g_free(escaped_filename
);
3996 g_string_free(addit
, TRUE
);
4002 //==========================================================================
4006 //==========================================================================
4007 gboolean
cmd_rotate (int argc
, char **argv
)
4013 //==========================================================================
4017 //==========================================================================
4018 gboolean
cmd_set (int argc
, char **argv
)
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
);
4036 if(!strcmp(argv
[1], "false") || !strcmp(argv
[1], "0"))
4042 else if(settings
[i
].type
== 'i')
4047 int *x
= (int*) (settings
[i
].variable
);
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
;
4065 else if(settings
[i
].type
== 'f')
4070 float *x
= (float*) (settings
[i
].variable
);
4074 else if(settings
[i
].type
== 's')
4079 /* assembly the arguments back to one string */
4081 GString
*s
= g_string_new("");
4082 for(j
= 1; j
< argc
; j
++)
4085 s
= g_string_append_c(s
, ' ');
4087 s
= g_string_append(s
, argv
[j
]);
4090 char **x
= (char**) settings
[i
].variable
;
4093 else if(settings
[i
].type
== 'c')
4098 char *x
= (char*) (settings
[i
].variable
);
4104 if(settings
[i
].reinit
)
4108 if(settings
[i
].render
)
4110 if(!Zathura
.PDF
.document
)
4113 draw(Zathura
.PDF
.page_number
);
4123 //==========================================================================
4127 //==========================================================================
4128 gboolean
cmd_quit (int argc
, char **argv
)
4130 cb_destroy(NULL
, NULL
);
4135 //==========================================================================
4139 //==========================================================================
4140 gboolean
save_file (int argc
, char **argv
, gboolean overwrite
)
4142 if(argc
== 0 || !Zathura
.PDF
.document
)
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
);
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
);
4166 if (file_path
[0] == '/')
4167 path
= g_strdup_printf("file://%s", file_path
);
4170 char* cur
= g_get_current_dir();
4171 path
= g_strdup_printf("file://%s/%s", cur
, file_path
);
4178 GError
* error
= NULL
;
4179 if (!poppler_document_save(Zathura
.PDF
.document
, path
, &error
))
4182 char* message
= g_strdup_printf("Can not write file: %s", error
->message
);
4183 notify(ERROR
, message
);
4185 g_error_free(error
);
4197 //==========================================================================
4201 //==========================================================================
4202 gboolean
cmd_save (int argc
, char **argv
)
4204 return save_file(argc
, argv
, FALSE
);
4208 //==========================================================================
4212 //==========================================================================
4213 gboolean
cmd_savef (int argc
, char **argv
)
4215 return save_file(argc
, argv
, TRUE
);
4219 //**************************************************************************
4221 // completion command implementation
4223 //**************************************************************************
4225 //==========================================================================
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
);
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
));
4252 //==========================================================================
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");
4271 //==========================================================================
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
);
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);
4289 gtk_entry_set_text(Zathura
.UI
.inputbar
, file
);
4290 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), -1);
4292 completion_free(completion
);
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
, "/") )
4313 file
= g_strdup("");
4315 else if( !strcmp(path_temp
, "/") && strcmp(file_temp
, "/") && last_char
!= '/')
4318 file
= g_strdup(file_temp
);
4320 else if( !strcmp(path_temp
, "/") && strcmp(file_temp
, "/") && last_char
== '/')
4323 path
= g_strdup_printf("/%s/", file_temp
);
4325 else if(last_char
== '/')
4328 path
= g_strdup(input
);
4334 path
= g_strdup_printf("%s/", path_temp
);
4335 file
= g_strdup(file_temp
);
4342 file_length
= strlen(file
);
4344 /* open directory */
4345 GDir
* dir
= g_dir_open(path
, 0, NULL
);
4350 completion_free(completion
);
4354 /* create element list */
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
))
4369 d
= g_strdup_printf("%s/", subdir
);
4372 completion_group_add_element(group
, d
, NULL
);
4386 //==========================================================================
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;
4401 char *current_line
= NULL
, current_char
;
4405 fp
= popen(list_printer_command
, "r");
4409 completion_free(completion
);
4413 while((current_char
= fgetc(fp
)) != EOF
)
4416 current_line
= malloc(sizeof(char));
4420 current_line
= safe_realloc((void**)¤t_line
, count
+1, sizeof(char));
4424 if(current_char
!= '\n')
4425 current_line
[count
++] = current_char
;
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
);
4438 current_line
= NULL
;
4449 //==========================================================================
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
);
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
);
4477 //**************************************************************************
4479 // buffer command implementation
4481 //**************************************************************************
4483 //==========================================================================
4487 //==========================================================================
4488 void bcmd_goto (char* buffer
, Argument
* argument
)
4490 if(!Zathura
.PDF
.document
)
4493 int b_length
= strlen(buffer
);
4497 if(!strcmp(buffer
, "gg"))
4499 else if(!strcmp(buffer
, "G"))
4500 set_page(Zathura
.PDF
.number_of_pages
-1);
4503 char* id
= g_strndup(buffer
, b_length
-1);
4506 if(Zathura
.Global
.goto_mode
== GOTO_LABELS
)
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
;
4521 //==========================================================================
4525 //==========================================================================
4526 gboolean
try_goto (const char *buffer
)
4528 char* endptr
= NULL
;
4529 long page_number
= strtol(buffer
, &endptr
, 10)-1;
4531 /* conversion error */
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
);
4544 //==========================================================================
4548 //==========================================================================
4549 void bcmd_scroll (char *buffer
, Argument
*argument
)
4551 int b_length
= strlen(buffer
);
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;
4567 Zathura
.State
.scroll_percentage
= percentage
;
4568 gtk_adjustment_set_value(adjustment
, nvalue
);
4573 //==========================================================================
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
;
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
;
4594 Zathura
.PDF
.scale
= zoom_min
;
4596 else if(argument
->n
== ZOOM_SPECIFIC
)
4598 int b_length
= strlen(buffer
);
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
;
4608 Zathura
.PDF
.scale
= value
;
4611 Zathura
.PDF
.scale
= 100;
4613 Zathura
.Search
.draw
= TRUE
;
4614 draw(Zathura
.PDF
.page_number
);
4619 //**************************************************************************
4621 // special command implementation
4623 //**************************************************************************
4625 //==========================================================================
4629 //==========================================================================
4630 gboolean
scmd_search (gchar
*input
, Argument
*argument
)
4632 if(!input
|| !strlen(input
))
4635 argument
->data
= input
;
4636 sc_search(argument
);
4642 //**************************************************************************
4644 // callback implementation
4646 //**************************************************************************
4648 //==========================================================================
4652 //==========================================================================
4653 gboolean
cb_destroy (GtkWidget
*widget
, gpointer data
) {
4654 pango_font_description_free(Zathura
.Style
.font
);
4656 if(Zathura
.PDF
.document
)
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
));
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
;
4683 ShortcutList
* ne
= sc
->next
;
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
);
4703 //==========================================================================
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
;
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;
4744 if (Zathura
.Search
.draw
) {
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
);
4756 cairo_destroy(cairo
);
4758 yofs
+= height
+Zathura
.PDF
.page_gap
;
4766 //==========================================================================
4768 // cb_inputbar_kb_pressed
4770 //==========================================================================
4771 gboolean
cb_inputbar_kb_pressed (GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
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
));
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
));
4808 gint len
= g_unichar_to_utf8(new_utf_char
, newchar
);
4810 gchar
* tmp
= g_strconcat(input
, newchar
, NULL
);
4819 if((special_commands
[i
].function
== scmd_search
) && (event
->keyval
== GDK_Return
))
4821 Argument argument
= { NO_SEARCH
, NULL
};
4822 scmd_search(input
, &argument
);
4826 special_commands
[i
].function(input
, &(special_commands
[i
].argument
));
4829 g_free(identifier_string
);
4835 g_free(identifier_string
);
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);
4852 gchar
*command
= tokens
[0];
4853 int length
= g_strv_length(tokens
);
4855 gboolean retv
= FALSE
;
4856 gboolean succ
= 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
4877 if(special_commands
[i
].always
== 1)
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
));
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);
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
));
4922 //==========================================================================
4924 // cb_inputbar_form_activate
4926 //==========================================================================
4927 gboolean
cb_inputbar_form_activate (GtkEntry
*entry
, gpointer data
)
4929 if(!Zathura
.PDF
.document
)
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
;
4936 GList
*link_list
= poppler_page_get_link_mapping(current_page
->page
);
4938 link_list
= g_list_reverse(link_list
);
4940 if((number_of_links
= g_list_length(link_list
)) <= 0)
4944 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 1, -1);
4945 gchar
*token
= input
+strlen("Follow hint: ")-1;
4949 int li
= atoi(token
);
4950 if(li
<= 0 || li
> number_of_links
)
4952 set_page(Zathura
.PDF
.page_number
);
4954 notify(WARNING
, "Invalid hint");
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
)
4969 open_uri(action
->uri
.uri
);
4971 else if(action
->type
== POPPLER_ACTION_GOTO_DEST
)
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
);
4980 new_page_id
= destination
->page_num
-1;
4981 poppler_dest_free(destination
);
4985 new_page_id
= action
->goto_dest
.dest
->page_num
-1;
4994 poppler_page_free_link_mapping(link_list
);
4997 set_page(new_page_id
);
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;
5016 if(!open_file(Zathura
.PDF
.file
, token
))
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
);
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
;
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
));
5063 if(Zathura
.Global
.mode
== ADD_MARKER
)
5065 add_marker(event
->keyval
);
5066 change_mode(NORMAL
);
5069 else if(Zathura
.Global
.mode
== EVAL_MARKER
)
5071 eval_marker(event
->keyval
);
5072 change_mode(NORMAL
);
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
)
5090 for(i
= 0; i
< LENGTH(buffer_commands
); i
++)
5095 regcomp(®ex
, buffer_commands
[i
].regex
, REG_EXTENDED
);
5096 status
= regexec(®ex
, Zathura
.Global
.buffer
->str
, (size_t) 0, NULL
, 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
, "");
5115 //==========================================================================
5119 //==========================================================================
5120 gboolean
cb_view_resized (GtkWidget
*widget
, GtkAllocation
*allocation
, gpointer data
)
5123 arg
.n
= Zathura
.Global
.adjust_mode
;
5124 sc_adjust_window(&arg
);
5130 //==========================================================================
5132 // cb_view_button_pressed
5134 //==========================================================================
5135 gboolean
cb_view_button_pressed (GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
5137 if(!Zathura
.PDF
.document
)
5141 draw(Zathura
.PDF
.page_number
);
5143 Zathura
.SelectPoint
.x
= event
->x
;
5144 Zathura
.SelectPoint
.y
= event
->y
;
5151 //==========================================================================
5153 // cb_view_button_release
5155 //==========================================================================
5156 gboolean
cb_view_button_release (GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
) {
5157 if(!Zathura
.PDF
.document
)
5160 double scale
, offset_x
, offset_y
;
5161 PopplerRectangle rectangle
;
5164 /* build selection rectangle */
5165 rectangle
.x1
= event
->x
;
5166 rectangle
.y1
= event
->y
;
5169 rectangle
.x2
= Zathura
.SelectPoint
.x
;
5170 rectangle
.y2
= Zathura
.SelectPoint
.y
;
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
));
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
;
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
;
5208 rectangle
.y1
= page_height
-x2
;
5210 rectangle
.y2
= page_height
-x1
;
5213 rectangle
.x1
= (page_height
-y1
);
5214 rectangle
.y1
= (page_width
-x2
);
5215 rectangle
.x2
= (page_height
-y2
);
5216 rectangle
.y2
= (page_width
-x1
);
5219 rectangle
.x1
= page_width
-y2
;
5221 rectangle
.x2
= page_width
-y1
;
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
;
5234 if(rectangle
.y2
> rectangle
.y1
)
5236 double d
= rectangle
.y1
;
5237 rectangle
.y1
= rectangle
.y2
;
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
;
5247 /* get selected text */
5249 char* selected_text
= poppler_page_get_selected_text(
5250 Zathura
.PDF
.pages
[Zathura
.PDF
.page_number
]->page
,SELECTION_STYLE
,
5255 gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY
), selected_text
, -1);
5256 g_free(selected_text
);
5265 //==========================================================================
5267 // cb_view_motion_notify
5269 //==========================================================================
5270 gboolean
cb_view_motion_notify (GtkWidget
*widget
, GdkEventMotion
*event
, gpointer data
)
5276 //==========================================================================
5280 //==========================================================================
5281 gboolean
cb_view_scrolled (GtkWidget
*widget
, GdkEventScroll
*event
, gpointer data
)
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
));
5297 //==========================================================================
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
)
5313 //**************************************************************************
5317 //**************************************************************************
5319 int main (int argc
, char *argv
[]) {
5321 Zathura
.UI
.embed
= 0;
5323 Zathura
.Config
.config_dir
= 0;
5324 Zathura
.Config
.data_dir
= 0;
5326 char* config_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" },
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
);
5347 g_option_context_free(context
);
5350 Zathura
.Config
.config_dir
= g_strdup(config_dir
);
5352 Zathura
.Config
.data_dir
= g_strdup(data_dir
);
5354 g_thread_init(NULL
);
5357 gtk_init(&argc
, &argv
);
5362 read_configuration();
5369 char* password
= (argc
== 3) ? argv
[2] : NULL
;
5370 if (strcmp(argv
[1], "-") == 0)
5371 open_stdin(password
);
5373 open_file(argv
[1], password
);
5376 switch_view(Zathura
.UI
.document
);
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();
5390 gdk_threads_leave();