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
26 #include <poppler/glib/poppler.h>
29 #include <glib/gstdio.h>
31 #include <gdk/gdkkeysyms.h>
35 #define NORETURN __attribute__((noreturn))
36 #define LENGTH(x) (sizeof(x)/sizeof((x)[0]))
37 #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))
39 #define MTLOCK_PDFOBJ() do { g_static_mutex_lock(&(Zathura.Lock.pdf_obj_lock)); } while (0)
40 #define MTUNLOCK_PDFOBJ() do { g_static_mutex_unlock(&(Zathura.Lock.pdf_obj_lock)); } while (0)
42 #define MTLOCK_PDFLIB() do { g_static_mutex_lock(&(Zathura.Lock.pdflib_lock)); } while (0)
43 #define MTUNLOCK_PDFLIB() do { g_static_mutex_unlock(&(Zathura.Lock.pdflib_lock)); } while (0)
45 #define MTLOCK_SEARCH() do { g_static_mutex_lock(&(Zathura.Lock.search_lock)); } while (0)
46 #define MTUNLOCK_SEARCH() do { g_static_mutex_unlock(&(Zathura.Lock.search_lock)); } while (0)
48 #define MTLOCK_SELECT() do { g_static_mutex_lock(&(Zathura.Lock.select_lock)); } while (0)
49 #define MTUNLOCK_SELECT() do { g_static_mutex_unlock(&(Zathura.Lock.select_lock)); } while (0)
52 // just remove the #if-#define-#endif block if support for poppler versions
53 // before 0.15 is dropped
54 #if !POPPLER_CHECK_VERSION(0,15,0)
55 # define poppler_page_get_selected_text poppler_page_get_text
58 static const char *main_group_name
= "//main//";
59 static const char *lastcheck_key_name
= "lastcheck";
62 //**************************************************************************
66 //**************************************************************************
68 // maximum surfaces in page surface cache
69 #define SURF_CACHE_SIZE (16)
71 typedef struct PageSurfCache_t
{
72 cairo_surface_t
*surf
;
75 struct PageSurfCache_t
*prev
;
76 struct PageSurfCache_t
*next
;
80 PageSurfCache
*surfCacheHead
= NULL
;
81 PageSurfCache
*surfCacheTail
= NULL
;
84 //==========================================================================
88 // initialise page cache, if necessary
90 //==========================================================================
91 static void surfCacheInit (void) {
92 if (surfCacheHead
) return;
93 surfCacheHead
= (PageSurfCache
*)g_malloc0(sizeof(PageSurfCache
));
94 memset(surfCacheHead
, 0, sizeof(PageSurfCache
));
95 surfCacheHead
->page_id
= -1;
96 surfCacheHead
->scale
= 0;
97 PageSurfCache
*last
= surfCacheHead
;
98 for (int f
= 1; f
< SURF_CACHE_SIZE
; ++f
) {
99 PageSurfCache
*c
= (PageSurfCache
*)g_malloc0(sizeof(PageSurfCache
));
100 memset(c
, 0, sizeof(PageSurfCache
));
107 surfCacheTail
= last
;
111 //==========================================================================
115 //==========================================================================
116 static void surfCacheDestroy (void) {
117 while (surfCacheHead
) {
118 PageSurfCache
*c
= surfCacheHead
;
119 surfCacheHead
= c
->next
;
120 if (c
->surf
) cairo_surface_destroy(c
->surf
);
123 surfCacheHead
= surfCacheTail
= NULL
;
127 //==========================================================================
131 //==========================================================================
132 static void surfCacheRemove (PageSurfCache
*c
) {
134 if (c
->prev
) c
->prev
->next
= c
->next
; else surfCacheHead
= c
->next
;
135 if (c
->next
) c
->next
->prev
= c
->prev
; else surfCacheTail
= c
->prev
;
136 c
->prev
= c
->next
= NULL
;
140 //==========================================================================
144 //==========================================================================
145 static void surfCacheToFront (PageSurfCache
*c
) {
146 if (!c
|| c
== surfCacheHead
) return; // nothing to do
149 c
->next
= surfCacheHead
;
150 surfCacheHead
->prev
= c
;
155 //==========================================================================
159 // this either returns an existing cached item, or creates a new
160 // one, of evicts and clears the old one
161 // the item is also moved to the front of the list
162 // destroys the surface if it is invalid (i.e. check the `surf`
163 // field in the returned struct)
165 //==========================================================================
166 static PageSurfCache
*surfCacheGet (int pgid
, const double scale
) {
168 for (PageSurfCache
*c
= surfCacheHead
; c
; c
= c
->next
) {
169 if (c
->page_id
== pgid
) {
171 if (c
->scale
!= scale
) {
172 if (c
->surf
) cairo_surface_destroy(c
->surf
);
179 // not found, use tail
180 PageSurfCache
*res
= surfCacheTail
;
181 surfCacheToFront(res
);
182 if (res
->surf
) cairo_surface_destroy(res
->surf
);
192 NEXT
, PREVIOUS
, LEFT
, RIGHT
, UP
, DOWN
, BOTTOM
, TOP
, HIDE
, HIGHLIGHT
,
193 DELETE_LAST_WORD
, DELETE_LAST_CHAR
, DEFAULT
, ERROR
, WARNING
, NEXT_GROUP
,
194 PREVIOUS_GROUP
, ZOOM_IN
, ZOOM_OUT
, ZOOM_ORIGINAL
, ZOOM_SPECIFIC
, FORWARD
,
195 BACKWARD
, ADJUST_BESTFIT
, ADJUST_WIDTH
, ADJUST_NONE
, CONTINUOUS
, DELETE_LAST
,
196 ADD_MARKER
, EVAL_MARKER
, EXPAND
, COLLAPSE
, SELECT
, GOTO_DEFAULT
, GOTO_LABELS
,
197 GOTO_OFFSET
, HALF_UP
, HALF_DOWN
, FULL_UP
, FULL_DOWN
, FULL_WINDOW_DOWN
,
198 NEXT_CHAR
, PREVIOUS_CHAR
, DELETE_TO_LINE_START
, APPEND_FILEPATH
, NO_SEARCH
,
200 WINDOW_UP
, WINDOW_DOWN
,
202 CENTER_LEFT_TOP
, CENTER_CENTER
, CENTER_RIGHT_BOTTOM
,
208 #define FULLSCREEN (1<<1)
210 #define NORMAL (1<<3)
214 typedef struct CElement
{
217 struct CElement
*next
;
221 typedef struct CGroup
{
223 CompletionElement
*elements
;
230 CompletionGroup
*groups
;
258 void (*function
) (Argument
*);
273 void (*function
) (Argument
*);
280 void (*function
) (Argument
*);
287 void (*function
) (Argument
*);
295 gboolean (*function
) (int, char **);
296 Completion
* (*completion
) (char *);
303 void (*function
) (char *, Argument
*);
310 gboolean (*function
) (char *, Argument
*);
316 typedef struct SCList
{
365 GtkScrolledWindow
*view
;
366 GtkViewport
*viewport
;
367 GtkWidget
*statusbar
;
368 GtkBox
*statusbar_entries
;
371 GtkWidget
*information
;
372 GtkWidget
*drawing_area
;
374 GdkNativeWindow embed
;
380 GdkColor inputbar_fg
;
381 GdkColor inputbar_bg
;
382 GdkColor statusbar_fg
;
383 GdkColor statusbar_bg
;
384 GdkColor completion_fg
;
385 GdkColor completion_bg
;
386 GdkColor completion_g_bg
;
387 GdkColor completion_g_fg
;
388 GdkColor completion_hl_fg
;
389 GdkColor completion_hl_bg
;
390 GdkColor notification_e_fg
;
391 GdkColor notification_e_bg
;
392 GdkColor notification_w_fg
;
393 GdkColor notification_w_bg
;
394 GdkColor recolor_darkcolor
;
395 GdkColor recolor_lightcolor
;
396 GdkColor search_highlight
;
397 GdkColor select_text
;
398 PangoFontDescription
*font
;
402 GtkLabel
*status_text
;
403 GtkLabel
*status_buffer
;
404 GtkLabel
*status_state
;
410 gboolean enable_labelmode
;
414 gboolean show_statusbar
;
415 gboolean show_inputbar
;
420 ShortcutList
*sclist
;
431 int scroll_percentage
;
436 int number_of_markers
;
441 GFileMonitor
*monitor
;
449 int number_of_bookmarks
;
453 PopplerDocument
*document
;
458 double page_yskip_t
; // [0..1]
459 int page_yskip_pix
; // in screen pixels; for a gap
463 int page_gap
; // usually 8
464 //TODO: cache total height and other things?
468 GStaticMutex pdflib_lock
;
469 GStaticMutex pdf_obj_lock
;
470 GStaticMutex search_lock
;
471 GStaticMutex select_lock
;
482 GThread
*search_thread
;
483 gboolean search_thread_running
;
484 GThread
*inotify_thread
;
488 guint inputbar_activate
;
489 guint inputbar_key_press_event
;
503 /* function declarations */
504 void init_look (void);
505 void init_directories (void);
506 void init_bookmarks (void);
507 void init_keylist (void);
508 void init_settings (void);
509 void init_zathura (void);
510 void add_marker (int);
511 void build_index (GtkTreeModel
*, GtkTreeIter
*, PopplerIndexIter
*);
512 void change_mode (int);
513 void calculate_screen_offset (GtkWidget
*widget
, int scrx
, int scry
, double *offset_x
, double *offset_y
, int *out_page_id
);
514 void close_file (gboolean
);
515 void enter_password (void);
516 void highlight_result (int, PopplerRectangle
*);
517 cairo_surface_t
*create_page_surface (int page_id
);
518 void draw (int page_id
);
519 void render_view (GtkWidget
*widget
, gboolean fullclear
);
520 void eval_marker (int);
521 void notify (int, const char *);
522 gboolean
open_file (char *, char *);
523 gboolean
open_stdin (gchar
*);
524 void open_uri (char *);
525 void out_of_memory (void) NORETURN
;
526 void update_status (void);
527 void read_bookmarks_file (void);
528 void write_bookmarks_file (void);
529 void free_bookmarks (void);
530 void read_configuration_file (const char *);
531 void read_configuration (void);
532 void recalc_rectangle (int, PopplerRectangle
*);
533 void set_completion_row_color (GtkBox
*, int, int);
535 void set_page_keep_ofs (int page
);
536 void switch_view (GtkWidget
*);
537 GtkEventBox
*create_completion_row (GtkBox
*, char *, char *, gboolean
);
538 gchar
*fix_path (const gchar
*);
539 gchar
*path_from_env (const gchar
*);
540 gchar
*get_home_dir (void);
542 Completion
*completion_init (void);
543 CompletionGroup
*completion_group_create (char*);
544 void completion_add_group (Completion
*, CompletionGroup
*);
545 void completion_free (Completion
*);
546 void completion_group_add_element (CompletionGroup
*, char *, char *);
548 /* thread declaration */
549 void *search (void *);
551 /* shortcut declarations */
552 void sc_abort (Argument
*);
553 void sc_adjust_window (Argument
*);
554 void sc_xcenter_window (Argument
*);
555 //void sc_ycenter_window (Argument *);
556 void sc_change_buffer (Argument
*);
557 void sc_change_mode (Argument
*);
558 void sc_focus_inputbar (Argument
*);
559 void sc_follow (Argument
*);
560 void sc_navigate (Argument
*);
561 void sc_recolor (Argument
*);
562 void sc_reload (Argument
*);
563 void sc_rotate (Argument
*);
564 void sc_scroll (Argument
*);
565 void sc_search (Argument
*);
566 void sc_switch_goto_mode (Argument
*);
567 void sc_navigate_index (Argument
*);
568 void sc_toggle_index (Argument
*);
569 void sc_toggle_inputbar (Argument
*);
570 void sc_toggle_fullscreen (Argument
*);
571 void sc_toggle_statusbar (Argument
*);
572 void sc_quit (Argument
*);
573 void sc_zoom (Argument
*);
575 /* inputbar shortcut declarations */
576 void isc_abort (Argument
*);
577 void isc_command_history (Argument
*);
578 void isc_completion (Argument
*);
579 void isc_string_manipulation (Argument
*);
581 /* command declarations */
582 gboolean
cmd_bookmark (int, char **);
583 gboolean
cmd_open_bookmark (int, char **);
584 gboolean
cmd_close (int, char **);
585 gboolean
cmd_delete_bookmark (int, char **);
586 gboolean
cmd_export (int, char **);
587 gboolean
cmd_info (int, char **);
588 gboolean
cmd_map (int, char **);
589 gboolean
cmd_open (int, char **);
590 gboolean
cmd_print (int, char **);
591 gboolean
cmd_rotate (int, char **);
592 gboolean
cmd_set (int, char **);
593 gboolean
cmd_quit (int, char **);
594 gboolean
cmd_save (int, char **);
595 gboolean
cmd_savef (int, char **);
597 /* completion commands */
598 Completion
*cc_bookmark (char *);
599 Completion
*cc_export (char *);
600 Completion
*cc_open (char *);
601 Completion
*cc_print (char *);
602 Completion
*cc_set (char *);
604 /* buffer command declarations */
605 void bcmd_evalmarker (char *, Argument
*);
606 void bcmd_goto (char *, Argument
*);
607 void bcmd_scroll (char *, Argument
*);
608 void bcmd_setmarker (char *, Argument
*);
609 void bcmd_zoom (char *, Argument
*);
611 /* special command delcarations */
612 gboolean
scmd_search (gchar
*, Argument
*);
614 /* callback declarations */
615 gboolean
cb_destroy (GtkWidget
*, gpointer
);
616 gboolean
cb_draw (GtkWidget
*, GdkEventExpose
*, gpointer
);
617 gboolean
cb_index_row_activated (GtkTreeView
*, GtkTreePath
*, GtkTreeViewColumn
*, gpointer
);
618 gboolean
cb_inputbar_kb_pressed (GtkWidget
*, GdkEventKey
*, gpointer
);
619 gboolean
cb_inputbar_activate (GtkEntry
*, gpointer
);
620 gboolean
cb_inputbar_form_activate (GtkEntry
*, gpointer
);
621 gboolean
cb_inputbar_password_activate (GtkEntry
*, gpointer
);
622 gboolean
cb_view_kb_pressed (GtkWidget
*, GdkEventKey
*, gpointer
);
623 gboolean
cb_view_resized (GtkWidget
*, GtkAllocation
*, gpointer
);
624 gboolean
cb_view_button_pressed (GtkWidget
*, GdkEventButton
*, gpointer
);
625 gboolean
cb_view_button_release (GtkWidget
*, GdkEventButton
*, gpointer
);
626 gboolean
cb_view_motion_notify (GtkWidget
*, GdkEventMotion
*, gpointer
);
627 gboolean
cb_view_scrolled (GtkWidget
*, GdkEventScroll
*, gpointer
);
628 gboolean
cb_watch_file (GFileMonitor
*, GFile
*, GFile
*, GFileMonitorEvent
, gpointer
);
635 //==========================================================================
639 //==========================================================================
640 static void *safe_realloc (void **ptr
, size_t nmemb
, size_t size
) {
641 static const size_t limit
= ~((size_t)0u);
643 /* Check for overflow. */
644 if (nmemb
> limit
/size
) goto failure
;
645 tmp
= realloc(*ptr
, nmemb
*size
);
646 /* Check for out of memory. */
647 if (!tmp
) goto failure
;
650 /* Error handling. */
658 //==========================================================================
662 //==========================================================================
663 static double sys_time (void) {
666 gettimeofday(&tp
, &tzp
);
667 return (double)(tp
.tv_sec
)+(double)tp
.tv_usec
/1000000.0;
671 //==========================================================================
675 //==========================================================================
676 void init_look (void) {
678 gdk_color_parse(default_fgcolor
, &(Zathura
.Style
.default_fg
));
679 gdk_color_parse(default_bgcolor
, &(Zathura
.Style
.default_bg
));
680 gdk_color_parse(inputbar_fgcolor
, &(Zathura
.Style
.inputbar_fg
));
681 gdk_color_parse(inputbar_bgcolor
, &(Zathura
.Style
.inputbar_bg
));
682 gdk_color_parse(statusbar_fgcolor
, &(Zathura
.Style
.statusbar_fg
));
683 gdk_color_parse(statusbar_bgcolor
, &(Zathura
.Style
.statusbar_bg
));
684 gdk_color_parse(completion_fgcolor
, &(Zathura
.Style
.completion_fg
));
685 gdk_color_parse(completion_bgcolor
, &(Zathura
.Style
.completion_bg
));
686 gdk_color_parse(completion_g_fgcolor
, &(Zathura
.Style
.completion_g_fg
));
687 gdk_color_parse(completion_g_fgcolor
, &(Zathura
.Style
.completion_g_fg
));
688 gdk_color_parse(completion_hl_fgcolor
, &(Zathura
.Style
.completion_hl_fg
));
689 gdk_color_parse(completion_hl_bgcolor
, &(Zathura
.Style
.completion_hl_bg
));
690 gdk_color_parse(notification_e_fgcolor
, &(Zathura
.Style
.notification_e_fg
));
691 gdk_color_parse(notification_e_bgcolor
, &(Zathura
.Style
.notification_e_bg
));
692 gdk_color_parse(notification_w_fgcolor
, &(Zathura
.Style
.notification_w_fg
));
693 gdk_color_parse(notification_w_bgcolor
, &(Zathura
.Style
.notification_w_bg
));
694 gdk_color_parse(recolor_darkcolor
, &(Zathura
.Style
.recolor_darkcolor
));
695 gdk_color_parse(recolor_lightcolor
, &(Zathura
.Style
.recolor_lightcolor
));
696 gdk_color_parse(search_highlight
, &(Zathura
.Style
.search_highlight
));
697 gdk_color_parse(select_text
, &(Zathura
.Style
.select_text
));
699 pango_font_description_free(Zathura
.Style
.font
);
700 Zathura
.Style
.font
= pango_font_description_from_string(font
);
702 /* window and viewport */
703 gtk_widget_modify_bg(GTK_WIDGET(Zathura
.UI
.window
), GTK_STATE_NORMAL
, &(Zathura
.Style
.default_bg
));
704 gtk_widget_modify_bg(GTK_WIDGET(Zathura
.UI
.viewport
), GTK_STATE_NORMAL
, &(Zathura
.Style
.default_bg
));
707 gtk_widget_modify_bg(GTK_WIDGET(Zathura
.UI
.drawing_area
), GTK_STATE_NORMAL
, &(Zathura
.Style
.default_bg
));
710 gtk_widget_modify_bg(GTK_WIDGET(Zathura
.UI
.statusbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.statusbar_bg
));
712 gtk_widget_modify_fg(GTK_WIDGET(Zathura
.Global
.status_text
), GTK_STATE_NORMAL
, &(Zathura
.Style
.statusbar_fg
));
713 gtk_widget_modify_fg(GTK_WIDGET(Zathura
.Global
.status_state
), GTK_STATE_NORMAL
, &(Zathura
.Style
.statusbar_fg
));
714 gtk_widget_modify_fg(GTK_WIDGET(Zathura
.Global
.status_buffer
), GTK_STATE_NORMAL
, &(Zathura
.Style
.statusbar_fg
));
716 gtk_widget_modify_font(GTK_WIDGET(Zathura
.Global
.status_text
), Zathura
.Style
.font
);
717 gtk_widget_modify_font(GTK_WIDGET(Zathura
.Global
.status_state
), Zathura
.Style
.font
);
718 gtk_widget_modify_font(GTK_WIDGET(Zathura
.Global
.status_buffer
), Zathura
.Style
.font
);
721 gtk_widget_modify_base(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.inputbar_bg
));
722 gtk_widget_modify_text(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.inputbar_fg
));
723 gtk_widget_modify_font(GTK_WIDGET(Zathura
.UI
.inputbar
), Zathura
.Style
.font
);
725 g_object_set(G_OBJECT(Zathura
.UI
.inputbar
), "has-frame", FALSE
, NULL
);
728 if (show_scrollbars
) {
729 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura
.UI
.view
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
731 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura
.UI
.view
), GTK_POLICY_NEVER
, GTK_POLICY_NEVER
);
735 if (Zathura
.Global
.show_inputbar
) {
736 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.inputbar
));
738 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
742 if (Zathura
.Global
.show_statusbar
) {
743 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.statusbar
));
745 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.statusbar
));
750 //==========================================================================
754 //==========================================================================
755 gchar
*fix_path (const gchar
*path
) {
756 if (!path
) return NULL
;
757 if (path
[0] == '~') {
758 gchar
*home_path
= get_home_dir();
759 gchar
*res
= g_build_filename(home_path
, path
+1, NULL
);
763 return g_strdup(path
);
767 //==========================================================================
771 //==========================================================================
772 gchar
*path_from_env (const gchar
*var
) {
773 gchar
*env
= fix_path(g_getenv(var
)), *res
;
774 if (!env
) return NULL
;
775 res
= g_build_filename(env
, "zathura", NULL
);
781 //==========================================================================
785 //==========================================================================
786 gchar
*get_home_dir (void) {
787 const gchar
*homedir
= g_getenv("HOME");
788 return g_strdup(homedir
? homedir
: g_get_home_dir());
792 //==========================================================================
796 //==========================================================================
797 void init_directories (void) {
798 /* setup directories */
799 if (!Zathura
.Config
.config_dir
) {
800 #ifndef ZATHURA_NO_XDG
801 gchar
*env
= path_from_env("XDG_CONFIG_HOME");
802 if (env
) Zathura
.Config
.config_dir
= env
;
805 Zathura
.Config
.config_dir
= fix_path(CONFIG_DIR
);
807 if (!Zathura
.Config
.data_dir
) {
808 #ifndef ZATHURA_NO_XDG
809 gchar
*env
= path_from_env("XDG_DATA_HOME");
810 if (env
) Zathura
.Config
.data_dir
= env
;
813 Zathura
.Config
.data_dir
= fix_path(DATA_DIR
);
816 /* create zathura (config/data) directory */
817 g_mkdir_with_parents(Zathura
.Config
.config_dir
, 0771);
818 g_mkdir_with_parents(Zathura
.Config
.data_dir
, 0771);
822 //==========================================================================
826 //==========================================================================
827 void init_bookmarks (void) {
829 Zathura
.Bookmarks
.number_of_bookmarks
= 0;
830 Zathura
.Bookmarks
.bookmarks
= NULL
;
832 Zathura
.Bookmarks
.file
= g_build_filename(Zathura
.Config
.data_dir
, BOOKMARK_FILE
, NULL
);
833 //fprintf(stderr, "CFG: %s\n", Zathura.Bookmarks.file);
834 read_bookmarks_file();
838 //==========================================================================
840 // is_reserved_bm_name
842 //==========================================================================
843 static gboolean
is_reserved_bm_name (const char *bm_name
) {
844 for (int i
= 0; i
< BM_MAX
; ++i
) if (strcmp(bm_reserved_names
[i
], bm_name
) == 0) return TRUE
;
849 //==========================================================================
853 //==========================================================================
854 void init_keylist (void) {
855 ShortcutList
*e
= NULL
, *p
= NULL
;
856 for (int i
= 0; i
< LENGTH(shortcuts
); ++i
) {
857 e
= malloc(sizeof(ShortcutList
));
858 if (!e
) out_of_memory();
859 e
->element
= shortcuts
[i
];
861 if (!Zathura
.Bindings
.sclist
) Zathura
.Bindings
.sclist
= e
;
868 //==========================================================================
872 //==========================================================================
873 void init_settings (void) {
874 Zathura
.State
.filename
= g_strdup((char*) default_text
);
875 Zathura
.Global
.adjust_mode
= adjust_open
;
876 Zathura
.Global
.xcenter_mode
= xcenter_open
;
878 gtk_window_set_default_size(GTK_WINDOW(Zathura
.UI
.window
), default_width
, default_height
);
879 if (default_maximize
) gtk_window_maximize(GTK_WINDOW(Zathura
.UI
.window
));
883 //==========================================================================
887 //==========================================================================
888 void init_zathura (void) {
890 g_static_mutex_init(&(Zathura
.Lock
.pdflib_lock
));
891 g_static_mutex_init(&(Zathura
.Lock
.search_lock
));
892 g_static_mutex_init(&(Zathura
.Lock
.pdf_obj_lock
));
893 g_static_mutex_init(&(Zathura
.Lock
.select_lock
));
896 Zathura
.Global
.mode
= NORMAL
;
897 Zathura
.Global
.viewing_mode
= NORMAL
;
898 Zathura
.Global
.recolor
= 0;
899 Zathura
.Global
.goto_mode
= GOTO_MODE
;
900 Zathura
.Global
.show_index
= FALSE
;
901 Zathura
.Global
.show_inputbar
= TRUE
;
902 Zathura
.Global
.show_statusbar
= TRUE
;
904 Zathura
.State
.pages
= g_strdup("");
905 Zathura
.State
.scroll_percentage
= 0;
907 Zathura
.Marker
.markers
= NULL
;
908 Zathura
.Marker
.number_of_markers
= 0;
909 Zathura
.Marker
.last
= -1;
911 Zathura
.Search
.results
= NULL
;
912 Zathura
.Search
.page
= 0;
913 Zathura
.Search
.draw
= FALSE
;
914 Zathura
.Search
.query
= NULL
;
916 Zathura
.FileMonitor
.monitor
= NULL
;
917 Zathura
.FileMonitor
.file
= NULL
;
919 Zathura
.StdinSupport
.file
= NULL
;
922 if (Zathura
.UI
.embed
) {
923 Zathura
.UI
.window
= gtk_plug_new(Zathura
.UI
.embed
);
925 Zathura
.UI
.window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
929 Zathura
.UI
.box
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
930 Zathura
.UI
.continuous
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
931 Zathura
.UI
.view
= GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL
, NULL
));
932 Zathura
.UI
.viewport
= GTK_VIEWPORT(gtk_viewport_new(NULL
, NULL
));
933 Zathura
.UI
.drawing_area
= gtk_drawing_area_new();
934 Zathura
.UI
.statusbar
= gtk_event_box_new();
935 Zathura
.UI
.statusbar_entries
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
936 Zathura
.UI
.inputbar
= GTK_ENTRY(gtk_entry_new());
937 Zathura
.UI
.document
= gtk_event_box_new();
940 gtk_window_set_title(GTK_WINDOW(Zathura
.UI
.window
), "zathura");
941 GdkGeometry hints
= { 1, 1 };
942 gtk_window_set_geometry_hints(GTK_WINDOW(Zathura
.UI
.window
), NULL
, &hints
, GDK_HINT_MIN_SIZE
);
943 g_signal_connect(G_OBJECT(Zathura
.UI
.window
), "destroy", G_CALLBACK(cb_destroy
), NULL
);
946 gtk_box_set_spacing(Zathura
.UI
.box
, 0);
947 gtk_container_add(GTK_CONTAINER(Zathura
.UI
.window
), GTK_WIDGET(Zathura
.UI
.box
));
950 gtk_box_set_spacing(Zathura
.UI
.continuous
, 5);
953 gtk_container_add(GTK_CONTAINER(Zathura
.UI
.document
), GTK_WIDGET(Zathura
.UI
.drawing_area
));
954 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
);
956 g_signal_connect(G_OBJECT(Zathura
.UI
.document
), "button-press-event", G_CALLBACK(cb_view_button_pressed
), NULL
);
957 g_signal_connect(G_OBJECT(Zathura
.UI
.document
), "button-release-event", G_CALLBACK(cb_view_button_release
), NULL
);
958 g_signal_connect(G_OBJECT(Zathura
.UI
.document
), "motion-notify-event", G_CALLBACK(cb_view_motion_notify
), NULL
);
959 gtk_widget_show(Zathura
.UI
.document
);
962 g_signal_connect(G_OBJECT(Zathura
.UI
.view
), "key-press-event", G_CALLBACK(cb_view_kb_pressed
), NULL
);
963 g_signal_connect(G_OBJECT(Zathura
.UI
.view
), "size-allocate", G_CALLBACK(cb_view_resized
), NULL
);
964 g_signal_connect(G_OBJECT(Zathura
.UI
.view
), "scroll-event", G_CALLBACK(cb_view_scrolled
), NULL
);
965 gtk_container_add(GTK_CONTAINER(Zathura
.UI
.view
), GTK_WIDGET(Zathura
.UI
.viewport
));
966 gtk_viewport_set_shadow_type(Zathura
.UI
.viewport
, GTK_SHADOW_NONE
);
969 gtk_widget_show(Zathura
.UI
.drawing_area
);
970 g_signal_connect(G_OBJECT(Zathura
.UI
.drawing_area
), "expose-event", G_CALLBACK(cb_draw
), NULL
);
973 Zathura
.Global
.status_text
= GTK_LABEL(gtk_label_new(NULL
));
974 Zathura
.Global
.status_state
= GTK_LABEL(gtk_label_new(NULL
));
975 Zathura
.Global
.status_buffer
= GTK_LABEL(gtk_label_new(NULL
));
977 gtk_misc_set_alignment(GTK_MISC(Zathura
.Global
.status_text
), 0.0, 0.0);
978 gtk_misc_set_alignment(GTK_MISC(Zathura
.Global
.status_state
), 1.0, 0.0);
979 gtk_misc_set_alignment(GTK_MISC(Zathura
.Global
.status_buffer
), 1.0, 0.0);
981 gtk_misc_set_padding(GTK_MISC(Zathura
.Global
.status_text
), 2.0, 4.0);
982 gtk_misc_set_padding(GTK_MISC(Zathura
.Global
.status_state
), 2.0, 4.0);
983 gtk_misc_set_padding(GTK_MISC(Zathura
.Global
.status_buffer
), 2.0, 4.0);
985 gtk_label_set_use_markup(Zathura
.Global
.status_text
, TRUE
);
986 gtk_label_set_use_markup(Zathura
.Global
.status_state
, TRUE
);
987 gtk_label_set_use_markup(Zathura
.Global
.status_buffer
, TRUE
);
989 gtk_box_pack_start(Zathura
.UI
.statusbar_entries
, GTK_WIDGET(Zathura
.Global
.status_text
), TRUE
, TRUE
, 2);
990 gtk_box_pack_start(Zathura
.UI
.statusbar_entries
, GTK_WIDGET(Zathura
.Global
.status_buffer
), FALSE
, FALSE
, 2);
991 gtk_box_pack_start(Zathura
.UI
.statusbar_entries
, GTK_WIDGET(Zathura
.Global
.status_state
), FALSE
, FALSE
, 2);
993 gtk_container_add(GTK_CONTAINER(Zathura
.UI
.statusbar
), GTK_WIDGET(Zathura
.UI
.statusbar_entries
));
996 gtk_entry_set_inner_border(Zathura
.UI
.inputbar
, NULL
);
997 gtk_entry_set_has_frame( Zathura
.UI
.inputbar
, FALSE
);
998 gtk_editable_set_editable( GTK_EDITABLE(Zathura
.UI
.inputbar
), TRUE
);
1000 Zathura
.Handler
.inputbar_key_press_event
= g_signal_connect(G_OBJECT(Zathura
.UI
.inputbar
), "key-press-event", G_CALLBACK(cb_inputbar_kb_pressed
), NULL
);
1001 Zathura
.Handler
.inputbar_activate
= g_signal_connect(G_OBJECT(Zathura
.UI
.inputbar
), "activate", G_CALLBACK(cb_inputbar_activate
), NULL
);
1004 gtk_box_pack_start(Zathura
.UI
.box
, GTK_WIDGET(Zathura
.UI
.view
), TRUE
, TRUE
, 0);
1005 gtk_box_pack_start(Zathura
.UI
.box
, GTK_WIDGET(Zathura
.UI
.statusbar
), FALSE
, FALSE
, 0);
1006 gtk_box_pack_end(Zathura
.UI
.box
, GTK_WIDGET(Zathura
.UI
.inputbar
), FALSE
, FALSE
, 0);
1010 //==========================================================================
1014 //==========================================================================
1015 void add_marker (int id
) {
1016 if (id
< 0x30 || id
> 0x7A) return;
1017 /* current information */
1018 int page_number
= Zathura
.PDF
.page_number
;
1019 /* search if entry already exists */
1020 for (int i
= 0; i
< Zathura
.Marker
.number_of_markers
; ++i
) {
1021 if (Zathura
.Marker
.markers
[i
].id
== id
) {
1022 Zathura
.Marker
.markers
[i
].page
= page_number
;
1023 Zathura
.Marker
.last
= page_number
;
1027 /* add new marker */
1028 int marker_index
= Zathura
.Marker
.number_of_markers
++;
1029 Zathura
.Marker
.markers
= safe_realloc((void**)&Zathura
.Marker
.markers
, Zathura
.Marker
.number_of_markers
, sizeof(Marker
));
1030 if (!Zathura
.Marker
.markers
) out_of_memory();
1031 Zathura
.Marker
.markers
[marker_index
].id
= id
;
1032 Zathura
.Marker
.markers
[marker_index
].page
= page_number
;
1033 Zathura
.Marker
.last
= page_number
;
1037 //==========================================================================
1041 //==========================================================================
1042 void build_index (GtkTreeModel
*model
, GtkTreeIter
*parent
, PopplerIndexIter
*index_iter
) {
1044 GtkTreeIter tree_iter
;
1045 PopplerIndexIter
*child
;
1046 PopplerAction
*action
;
1049 action
= poppler_index_iter_get_action(index_iter
);
1050 if (!action
) continue;
1052 markup
= g_markup_escape_text (action
->any
.title
, -1);
1054 gtk_tree_store_append(GTK_TREE_STORE(model
), &tree_iter
, parent
);
1055 gtk_tree_store_set(GTK_TREE_STORE(model
), &tree_iter
, 0, markup
, 1, action
, -1);
1056 g_object_weak_ref(G_OBJECT(model
), (GWeakNotify
) poppler_action_free
, action
);
1059 child
= poppler_index_iter_get_child(index_iter
);
1060 if (child
) build_index(model
, &tree_iter
, child
);
1061 poppler_index_iter_free(child
);
1062 } while (poppler_index_iter_next(index_iter
));
1066 //==========================================================================
1068 // get_page_screen_height
1070 //==========================================================================
1071 int get_page_screen_height (int page_id
) {
1072 if (!Zathura
.PDF
.document
|| Zathura
.PDF
.number_of_pages
< 1) return 0;
1073 if (page_id
< 0 || page_id
>= Zathura
.PDF
.number_of_pages
) return 0;
1074 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1075 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
1076 return (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
)*scale
+Zathura
.PDF
.page_yskip_pix
;
1080 //==========================================================================
1082 // get_current_page_screen_ytop
1084 //==========================================================================
1085 int get_current_page_screen_ytop (void) {
1086 if (!Zathura
.PDF
.document
|| Zathura
.PDF
.number_of_pages
< 1) return 0;
1087 const int page_id
= Zathura
.PDF
.page_number
;
1088 if (page_id
< 0) return 0;
1089 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1090 if (page_id
>= Zathura
.PDF
.number_of_pages
) {
1091 // beyond the last page, show the whole last page
1092 if (!Zathura
.UI
.view
) return 0;
1093 GtkAdjustment
*adjustment
= gtk_scrolled_window_get_vadjustment(Zathura
.UI
.view
);
1094 int window_y
= gtk_adjustment_get_page_size(adjustment
);
1095 Page
*current_page
= Zathura
.PDF
.pages
[Zathura
.PDF
.number_of_pages
-1];
1096 const int scheight
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
)*scale
+Zathura
.PDF
.page_gap
;
1097 return (scheight
>= window_y
? scheight
-window_y
: 0);
1099 Page
*current_page
= Zathura
.PDF
.pages
[Zathura
.PDF
.page_number
];
1100 const double height
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
);
1101 return -(height
*Zathura
.PDF
.page_yskip_t
*scale
+Zathura
.PDF
.page_yskip_pix
);
1106 //==========================================================================
1108 // calculate_screen_offset
1110 // calculates screen page offset (and id) in the widget
1112 //==========================================================================
1113 void calculate_screen_offset (GtkWidget
*widget
, int scrx
, int scry
, double *offset_x
, double *offset_y
, int *out_page_id
) {
1114 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1115 int ypos
= get_current_page_screen_ytop();
1116 int page_id
= Zathura
.PDF
.page_number
;
1118 int window_x
, window_y
;
1119 gdk_drawable_get_size(widget
->window
, &window_x
, &window_y
);
1121 //fprintf(stderr, "=== scr:(%d,%d); ypos=%d; page_id=%d; win:(%d,%d) ===\n", scrx, scry, ypos, page_id, window_x, window_y);
1122 while (ypos
< window_y
&& page_id
< Zathura
.PDF
.number_of_pages
) {
1123 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
1124 const int scheight
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
)*scale
;
1125 if (scry
>= ypos
&& scry
< ypos
+scheight
) {
1127 const int scwidth
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->width
: current_page
->height
)*scale
;
1129 if (window_x
> scwidth
) {
1130 switch (Zathura
.Global
.xcenter_mode
) {
1131 case CENTER_LEFT_TOP
: *offset_x
= 0; break;
1132 case CENTER_RIGHT_BOTTOM
: *offset_x
= window_x
-scwidth
; break;
1133 default: *offset_x
= (window_x
-scwidth
)/2; break;
1139 if (offset_y
) *offset_y
= ypos
;
1140 if (out_page_id
) *out_page_id
= page_id
;
1141 //fprintf(stderr, " RES: pos=(%g,%g); page_id=%d\n", *offset_x, *offset_y, *out_page_id);
1144 ypos
+= scheight
+Zathura
.PDF
.page_gap
;
1148 if (out_page_id
) *out_page_id
= -1; // mark as invalid
1149 if (offset_x
) *offset_x
= 0;
1150 if (offset_y
) *offset_y
= 0;
1152 /* old and wrong code
1153 double width, height;
1154 const double scale = ((double)Zathura.PDF.scale/100.0);
1155 int window_x, window_y;
1157 Page *current_page = Zathura.PDF.pages[Zathura.PDF.page_number];
1158 const double page_width = current_page->width;
1159 const double page_height = current_page->height;
1161 if (Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180) {
1162 width = page_width*scale;
1163 height = page_height*scale;
1165 width = page_height*scale;
1166 height = page_width*scale;
1169 gdk_drawable_get_size(widget->window, &window_x, &window_y);
1171 *offset_x = (window_x > width ? (window_x-width)/2 : 0);
1172 *offset_y = (window_y > height ? (window_y-height)/2 : 0);
1177 //==========================================================================
1179 // calc_current_document_screen_offset
1181 //==========================================================================
1182 int calc_current_document_screen_offset (void) {
1183 if (!Zathura
.PDF
.document
|| Zathura
.PDF
.number_of_pages
== 0) return 0;
1185 int dest_page_id
= Zathura
.PDF
.page_number
;
1186 if (dest_page_id
< 0) dest_page_id
= 1;
1187 if (dest_page_id
>= Zathura
.PDF
.number_of_pages
) dest_page_id
= Zathura
.PDF
.number_of_pages
-1;
1189 //fprintf(stderr, "curr_page_id=%d; t=%g; px=%d\n", dest_page_id, Zathura.PDF.page_yskip_t, Zathura.PDF.page_yskip_pix);
1191 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1195 for (int page_id
= 0; page_id
< dest_page_id
; ++page_id
) {
1196 current_page
= Zathura
.PDF
.pages
[page_id
];
1197 const int scheight
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
)*scale
;
1198 curryofs
+= scheight
+Zathura
.PDF
.page_gap
;
1201 current_page
= Zathura
.PDF
.pages
[dest_page_id
];
1202 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
);
1204 //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);
1209 //==========================================================================
1211 // calc_document_bottom_pos
1213 //==========================================================================
1214 void calc_document_bottom_pos (int *out_page_id
, double *out_page_yskip_t
, int *out_page_yskip_pix
) {
1215 int tmppgid
= 0, tmppgofs
= 0;
1217 if (!out_page_id
) out_page_id
= &tmppgid
;
1218 if (!out_page_yskip_t
) out_page_yskip_t
= &tmpt
;
1219 if (!out_page_yskip_pix
) out_page_yskip_pix
= &tmppgofs
;
1221 if (!Zathura
.PDF
.document
|| Zathura
.PDF
.number_of_pages
< 1) {
1223 *out_page_yskip_t
= 0;
1224 *out_page_yskip_pix
= 0;
1228 const int page_id
= Zathura
.PDF
.number_of_pages
-1;
1230 *out_page_id
= page_id
;
1231 *out_page_yskip_t
= 0;
1232 *out_page_yskip_pix
= 0;
1234 if (!Zathura
.UI
.view
) return;
1236 GtkAdjustment
*adjustment
= gtk_scrolled_window_get_vadjustment(Zathura
.UI
.view
);
1237 int window_y
= gtk_adjustment_get_page_size(adjustment
);
1238 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
1239 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1240 const int scheight
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
)*scale
;
1241 if (scheight
> window_y
) {
1242 *out_page_yskip_pix
= 0;
1243 int yskip
= scheight
-window_y
;
1244 *out_page_yskip_t
= (double)yskip
/(double)scheight
;
1249 //==========================================================================
1251 // convert_screen_document_offset_to_pos
1253 //==========================================================================
1254 void convert_screen_document_offset_to_pos (int docofs
, int *out_page_id
, double *out_page_yskip_t
, int *out_page_yskip_pix
) {
1255 int tmppgid
= 0, tmppgofs
= 0;
1257 if (!out_page_id
) out_page_id
= &tmppgid
;
1258 if (!out_page_yskip_t
) out_page_yskip_t
= &tmpt
;
1259 if (!out_page_yskip_pix
) out_page_yskip_pix
= &tmppgofs
;
1261 if (!Zathura
.PDF
.document
|| Zathura
.PDF
.number_of_pages
< 1) {
1263 *out_page_yskip_t
= 0;
1264 *out_page_yskip_pix
= 0;
1268 if (docofs
< 0) docofs
= 0;
1270 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1272 //fprintf(stderr, "docofs=%d\n", docofs);
1275 for (int page_id
= 0; page_id
< Zathura
.PDF
.number_of_pages
; ++page_id
) {
1276 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
1277 const double height
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
);
1278 const int scheight
= height
*scale
;
1279 const int pgend
= curryofs
+scheight
;
1280 if (docofs
>= curryofs
&& docofs
< pgend
+Zathura
.PDF
.page_gap
) {
1281 *out_page_id
= page_id
;
1282 if (docofs
< pgend
) {
1283 // some part of the page is visible
1284 *out_page_yskip_t
= (double)(docofs
-curryofs
)/(double)scheight
;
1285 *out_page_yskip_pix
= 0;
1287 // no page visible, we are inside a gap
1288 *out_page_yskip_t
= 1;
1289 *out_page_yskip_pix
= docofs
-pgend
;
1291 // if this is the last page, don't allow to scroll it completely out of view
1292 if (Zathura
.UI
.view
&& page_id
== Zathura
.PDF
.number_of_pages
-1) {
1295 calc_document_bottom_pos(&pid
, &t
, &yofs
);
1296 if (*out_page_yskip_t
> t
) {
1297 *out_page_yskip_t
= t
;
1298 if (*out_page_yskip_pix
> yofs
) *out_page_yskip_pix
= yofs
;
1303 curryofs
= pgend
+Zathura
.PDF
.page_gap
;
1306 // beyond the last page, show the whole last page
1307 calc_document_bottom_pos(out_page_id
, out_page_yskip_t
, out_page_yskip_pix
);
1311 //==========================================================================
1313 // scroll_up_pixels_no_draw
1315 //==========================================================================
1316 void scroll_up_pixels_no_draw (int count
) {
1318 int currypos
= calc_current_document_screen_offset();
1320 convert_screen_document_offset_to_pos(currypos
, &Zathura
.PDF
.page_number
, &Zathura
.PDF
.page_yskip_t
, &Zathura
.PDF
.page_yskip_pix
);
1324 //==========================================================================
1326 // scroll_down_pixels_no_draw
1328 //==========================================================================
1329 void scroll_down_pixels_no_draw (int count
) {
1331 int currypos
= calc_current_document_screen_offset();
1333 convert_screen_document_offset_to_pos(currypos
, &Zathura
.PDF
.page_number
, &Zathura
.PDF
.page_yskip_t
, &Zathura
.PDF
.page_yskip_pix
);
1337 //==========================================================================
1339 // create_page_surface
1341 //==========================================================================
1342 cairo_surface_t
*create_page_surface (int page_id
) {
1343 if (!Zathura
.PDF
.document
|| page_id
< 0 || page_id
>= Zathura
.PDF
.number_of_pages
) return NULL
;
1345 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
1347 PageSurfCache
*cc
= surfCacheGet(page_id
, scale
);
1348 // create new surface if necessary
1350 //fprintf(stderr, "creating new page surface for page #%d\n", page_id);
1351 int rotate
= Zathura
.PDF
.rotate
;
1352 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
1353 cairo_surface_t
*surf
= NULL
;
1354 const double page_width
= current_page
->width
;
1355 const double page_height
= current_page
->height
;
1357 double width
, height
;
1358 if (rotate
== 0 || rotate
== 180) {
1359 width
= page_width
*scale
;
1360 height
= page_height
*scale
;
1362 width
= page_height
*scale
;
1363 height
= page_width
*scale
;
1366 surf
= cairo_image_surface_create(CAIRO_FORMAT_RGB24
, width
, height
);
1368 cairo_t
*cairo
= cairo_create(surf
);
1371 cairo_set_source_rgb(cairo
, 1, 1, 1);
1372 cairo_rectangle(cairo
, 0, 0, width
, height
);
1374 cairo_restore(cairo
);
1378 case 90: cairo_translate(cairo
, width
, 0); break;
1379 case 180: cairo_translate(cairo
, width
, height
); break;
1380 case 270: cairo_translate(cairo
, 0, height
); break;
1381 default: cairo_translate(cairo
, 0, 0); break;
1384 if (scale
!= 1.0) cairo_scale(cairo
, scale
, scale
);
1385 if (rotate
!= 0) cairo_rotate(cairo
, rotate
*G_PI
/180.0);
1388 poppler_page_render(current_page
->page
, cairo
);
1391 cairo_restore(cairo
);
1392 cairo_destroy(cairo
);
1394 if (Zathura
.Global
.recolor
) {
1395 unsigned char *image
= cairo_image_surface_get_data(surf
);
1396 int width
= cairo_image_surface_get_width(surf
);
1397 int height
= cairo_image_surface_get_height(surf
);
1398 int rowstride
= cairo_image_surface_get_stride(surf
);
1400 /* recolor code based on qimageblitz library flatten() function
1401 (http://sourceforge.net/projects/qimageblitz/) */
1403 int r1
= Zathura
.Style
.recolor_darkcolor
.red
/257;
1404 int g1
= Zathura
.Style
.recolor_darkcolor
.green
/257;
1405 int b1
= Zathura
.Style
.recolor_darkcolor
.blue
/257;
1406 int r2
= Zathura
.Style
.recolor_lightcolor
.red
/257;
1407 int g2
= Zathura
.Style
.recolor_lightcolor
.green
/257;
1408 int b2
= Zathura
.Style
.recolor_lightcolor
.blue
/257;
1414 float sr
= ((float)r2
-r1
)/(max
-min
);
1415 float sg
= ((float)g2
-g1
)/(max
-min
);
1416 float sb
= ((float)b2
-b1
)/(max
-min
);
1418 for (int y
= 0; y
< height
; ++y
) {
1419 unsigned char *data
= image
+y
*rowstride
;
1420 for (int x
= 0; x
< width
; ++x
) {
1421 mean
= (data
[0]+data
[1]+data
[2])/3;
1422 data
[2] = sr
*(mean
-min
)+r1
+0.5;
1423 data
[1] = sg
*(mean
-min
)+g1
+0.5;
1424 data
[0] = sb
*(mean
-min
)+b1
+0.5;
1432 //fprintf(stderr, "reusing new page surface for page #%d\n", page_id);
1439 //==========================================================================
1443 //==========================================================================
1444 void draw (int page_id
) {
1445 cairo_surface_t
*surf
= create_page_surface(page_id
);
1448 int w
= cairo_image_surface_get_width(surf
);
1449 int h
= cairo_image_surface_get_height(surf
);
1451 gtk_widget_set_size_request(Zathura
.UI
.drawing_area
, w
, h
);
1452 gtk_widget_queue_draw(Zathura
.UI
.drawing_area
);
1456 //==========================================================================
1460 //==========================================================================
1461 void change_mode (int mode
) {
1462 char *mode_text
= 0;
1463 for (unsigned int i
= 0; i
!= LENGTH(mode_names
); ++i
) {
1464 if (mode_names
[i
].mode
== mode
) {
1465 mode_text
= mode_names
[i
].display
;
1471 case ADD_MARKER
: mode_text
= ""; break;
1472 case EVAL_MARKER
: mode_text
= ""; break;
1473 default: mode_text
= ""; mode
= NORMAL
; break;
1476 Zathura
.Global
.mode
= mode
;
1477 notify(DEFAULT
, mode_text
);
1481 //==========================================================================
1485 //==========================================================================
1486 void close_file (gboolean keep_monitor
) {
1487 if (!Zathura
.PDF
.document
) return;
1489 /* clean up pages */
1490 for (int i
= 0; i
< Zathura
.PDF
.number_of_pages
; ++i
) {
1491 Page
*current_page
= Zathura
.PDF
.pages
[i
];
1492 g_object_unref(current_page
->page
);
1493 if (current_page
->label
) g_free(current_page
->label
);
1497 /* save bookmarks */
1498 if (Zathura
.Bookmarks
.data
) {
1499 read_bookmarks_file();
1500 if (save_position
) {
1501 /* set current page */
1502 g_key_file_set_integer(Zathura
.Bookmarks
.data
, Zathura
.PDF
.file
, bm_reserved_names
[BM_PAGE_ENTRY
], Zathura
.PDF
.page_number
);
1503 /* set page offset */
1504 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
);
1506 if (save_zoom_level
) {
1507 /* set zoom level */
1508 g_key_file_set_integer(Zathura
.Bookmarks
.data
, Zathura
.PDF
.file
, bm_reserved_names
[BM_PAGE_SCALE
], Zathura
.PDF
.scale
);
1510 write_bookmarks_file();
1515 if (!keep_monitor
) {
1516 g_object_unref(Zathura
.FileMonitor
.monitor
);
1517 Zathura
.FileMonitor
.monitor
= NULL
;
1518 if (Zathura
.FileMonitor
.file
) {
1519 g_object_unref(Zathura
.FileMonitor
.file
);
1520 Zathura
.FileMonitor
.file
= NULL
;
1525 g_free(Zathura
.PDF
.pages
);
1526 g_object_unref(Zathura
.PDF
.document
);
1527 g_free(Zathura
.State
.pages
);
1528 gtk_window_set_title(GTK_WINDOW(Zathura
.UI
.window
), "zathura");
1530 Zathura
.State
.pages
= g_strdup("");
1531 g_free(Zathura
.State
.filename
);
1532 Zathura
.State
.filename
= g_strdup((char *)default_text
);
1535 Zathura
.PDF
.document
= NULL
;
1537 if (!keep_monitor
) {
1538 g_free(Zathura
.PDF
.file
);
1539 g_free(Zathura
.PDF
.password
);
1540 Zathura
.PDF
.file
= NULL
;
1541 Zathura
.PDF
.password
= NULL
;
1542 Zathura
.PDF
.page_number
= 0;
1543 Zathura
.PDF
.scale
= 0;
1544 Zathura
.PDF
.rotate
= 0;
1546 Zathura
.PDF
.number_of_pages
= 0;
1547 Zathura
.PDF
.page_yskip_t
= 0;
1548 Zathura
.PDF
.page_yskip_pix
= 0;
1549 Zathura
.PDF
.page_gap
= 8;
1555 if (Zathura
.UI
.index
) {
1556 gtk_widget_destroy(Zathura
.UI
.index
);
1557 Zathura
.UI
.index
= NULL
;
1560 /* destroy information */
1561 if (Zathura
.UI
.information
) {
1562 gtk_widget_destroy(Zathura
.UI
.information
);
1563 Zathura
.UI
.information
= NULL
;
1567 if (Zathura
.Marker
.markers
) free(Zathura
.Marker
.markers
);
1568 Zathura
.Marker
.number_of_markers
= 0;
1569 Zathura
.Marker
.last
= -1;
1575 //==========================================================================
1579 //==========================================================================
1580 void enter_password (void) {
1581 /* replace default inputbar handler */
1582 g_signal_handler_disconnect((gpointer
) Zathura
.UI
.inputbar
, Zathura
.Handler
.inputbar_activate
);
1583 Zathura
.Handler
.inputbar_activate
= g_signal_connect(G_OBJECT(Zathura
.UI
.inputbar
), "activate", G_CALLBACK(cb_inputbar_password_activate
), NULL
);
1586 argument
.data
= "Enter password: ";
1587 sc_focus_inputbar(&argument
);
1591 //==========================================================================
1595 //==========================================================================
1596 void eval_marker (int id
) {
1597 /* go to last marker */
1599 int current_page
= Zathura
.PDF
.page_number
;
1600 set_page(Zathura
.Marker
.last
);
1601 Zathura
.Marker
.last
= current_page
;
1604 /* search markers */
1605 for (int i
= 0; i
< Zathura
.Marker
.number_of_markers
; ++i
) {
1606 if (Zathura
.Marker
.markers
[i
].id
== id
) {
1607 set_page(Zathura
.Marker
.markers
[i
].page
);
1614 //==========================================================================
1618 //==========================================================================
1619 void highlight_result (int page_id
, PopplerRectangle
*rectangle
) {
1620 PopplerRectangle
*trect
= poppler_rectangle_copy(rectangle
);
1621 cairo_t
*cairo
= cairo_create(create_page_surface(page_id
));
1622 cairo_set_source_rgba(cairo
, Zathura
.Style
.search_highlight
.red
, Zathura
.Style
.search_highlight
.green
, Zathura
.Style
.search_highlight
.blue
, transparency
);
1624 recalc_rectangle(page_id
, trect
);
1625 cairo_rectangle(cairo
, trect
->x1
, trect
->y1
, trect
->x2
-trect
->x1
, trect
->y2
-trect
->y1
);
1626 poppler_rectangle_free(trect
);
1628 cairo_destroy(cairo
);
1632 //==========================================================================
1636 //==========================================================================
1637 void notify (int level
, const char *message
) {
1640 gtk_widget_modify_base(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.notification_e_bg
));
1641 gtk_widget_modify_text(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.notification_e_fg
));
1644 gtk_widget_modify_base(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.notification_w_bg
));
1645 gtk_widget_modify_text(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.notification_w_fg
));
1648 gtk_widget_modify_base(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.inputbar_bg
));
1649 gtk_widget_modify_text(GTK_WIDGET(Zathura
.UI
.inputbar
), GTK_STATE_NORMAL
, &(Zathura
.Style
.inputbar_fg
));
1652 if (message
) gtk_entry_set_text(Zathura
.UI
.inputbar
, message
);
1656 //==========================================================================
1660 //==========================================================================
1661 gboolean
open_file (char *path
, char *password
) {
1664 /* specify path max */
1669 pm
= pathconf(path
, _PC_PATH_MAX
);
1670 if (pm
<= 0) pm
= 4096;
1674 if (path
[0] == '~') {
1675 gchar
*home_path
= get_home_dir();
1676 rpath
= g_build_filename(home_path
, path
+1, NULL
);
1679 rpath
= g_strdup(path
);
1683 char *file
= (char *)g_malloc0(sizeof(char)*pm
);
1684 if (!file
) out_of_memory();
1686 if (!realpath(rpath
, file
)) {
1687 notify(ERROR
, "File does not exist");
1695 /* check if file exists */
1696 if (!g_file_test(file
, G_FILE_TEST_IS_REGULAR
)) {
1697 notify(ERROR
, "File does not exist");
1703 /* close old file */
1709 GError
*error
= NULL
;
1710 char *file_uri
= g_filename_to_uri(file
, NULL
, &error
);
1712 if (file
) g_free(file
);
1713 char *message
= g_strdup_printf("Can not open file: %s", error
->message
);
1714 notify(ERROR
, message
);
1716 g_error_free(error
);
1723 Zathura
.PDF
.document
= poppler_document_new_from_file(file_uri
, password
, &error
);
1726 if (!Zathura
.PDF
.document
) {
1727 if (error
->code
== 1) {
1729 g_error_free(error
);
1730 Zathura
.PDF
.file
= file
;
1735 char *message
= g_strdup_printf("Can not open file: %s", error
->message
);
1736 notify(ERROR
, message
);
1739 g_error_free(error
);
1746 g_free(Zathura
.PDF
.password
);
1747 Zathura
.PDF
.password
= (password
? g_strdup(password
) : NULL
);
1750 if (!Zathura
.FileMonitor
.monitor
) {
1751 GFile
*file
= g_file_new_for_uri(file_uri
);
1753 Zathura
.FileMonitor
.monitor
= g_file_monitor_file(file
, G_FILE_MONITOR_NONE
, NULL
, NULL
);
1754 if (Zathura
.FileMonitor
.monitor
) g_signal_connect(G_OBJECT(Zathura
.FileMonitor
.monitor
), "changed", G_CALLBACK(cb_watch_file
), NULL
);
1755 Zathura
.FileMonitor
.file
= file
;
1762 Zathura
.PDF
.number_of_pages
= poppler_document_get_n_pages(Zathura
.PDF
.document
);
1764 g_free(Zathura
.PDF
.file
);
1765 Zathura
.PDF
.file
= file
;
1766 Zathura
.PDF
.scale
= 100;
1767 Zathura
.PDF
.rotate
= 0;
1768 Zathura
.PDF
.page_gap
= 8;
1769 if (Zathura
.State
.filename
) g_free(Zathura
.State
.filename
);
1770 Zathura
.State
.filename
= g_markup_escape_text(file
, -1);
1771 Zathura
.PDF
.pages
= g_malloc(Zathura
.PDF
.number_of_pages
*sizeof(Page
*));
1774 if (!Zathura
.PDF
.pages
) out_of_memory();
1776 /* get pages and check label mode */
1778 Zathura
.Global
.enable_labelmode
= FALSE
;
1780 char *errmsg
= NULL
;
1781 printf("loading %d pages...\n", Zathura
.PDF
.number_of_pages
);
1782 for (int i
= 0; i
< Zathura
.PDF
.number_of_pages
; ++i
) {
1783 //printf(" ...page #%d\n", i+1);
1784 Zathura
.PDF
.pages
[i
] = malloc(sizeof(Page
));
1785 if (!Zathura
.PDF
.pages
[i
]) out_of_memory();
1787 Zathura
.PDF
.pages
[i
]->id
= i
+1;
1788 Zathura
.PDF
.pages
[i
]->page
= poppler_document_get_page(Zathura
.PDF
.document
, i
);
1789 if (!Zathura
.PDF
.pages
[i
]->page
) {
1790 fprintf(stderr
, "WARNING! can't load page #%d of %d...\n", i
+1, Zathura
.PDF
.number_of_pages
);
1791 // abort here, it seems that we're out of memory (or poppler failed)
1792 free(Zathura
.PDF
.pages
[i
]);
1793 Zathura
.PDF
.pages
[i
] = NULL
;
1794 errmsg
= g_strdup_printf("Aborted loading on page #%d of %d due to errors", i
+1, Zathura
.PDF
.number_of_pages
);
1795 Zathura
.PDF
.number_of_pages
= i
;
1798 //printf(" ...page #%d: %p\n", i+1, Zathura.PDF.pages[i]->page);
1799 g_object_get(G_OBJECT(Zathura
.PDF
.pages
[i
]->page
), "label", &(Zathura
.PDF
.pages
[i
]->label
), NULL
);
1801 /* check if it is necessary to use the label mode */
1802 int label_int
= atoi(Zathura
.PDF
.pages
[i
]->label
);
1803 if (label_int
== 0 || label_int
!= i
+1) Zathura
.Global
.enable_labelmode
= TRUE
;
1805 poppler_page_get_size(Zathura
.PDF
.pages
[i
]->page
, &Zathura
.PDF
.pages
[i
]->width
, &Zathura
.PDF
.pages
[i
]->height
);
1806 if (Zathura
.PDF
.pages
[i
]->width
< 1) Zathura
.PDF
.pages
[i
]->width
= 1;
1807 if (Zathura
.PDF
.pages
[i
]->height
< 1) Zathura
.PDF
.pages
[i
]->height
= 1;
1808 //fprintf(stderr, "PAGE #%d: size=(%gx%g)\n", i, Zathura.PDF.pages[i]->width, Zathura.PDF.pages[i]->height);
1812 /* set correct goto mode */
1813 if (!Zathura
.Global
.enable_labelmode
&& GOTO_MODE
== GOTO_LABELS
) Zathura
.Global
.goto_mode
= GOTO_DEFAULT
;
1817 Zathura
.PDF
.page_yskip_t
= 0;
1818 Zathura
.PDF
.page_yskip_pix
= 0;
1821 if (Zathura
.Bookmarks
.data
&& g_key_file_has_group(Zathura
.Bookmarks
.data
, file
)) {
1822 /* get last opened page */
1823 if (save_position
&& g_key_file_has_key(Zathura
.Bookmarks
.data
, file
, bm_reserved_names
[BM_PAGE_ENTRY
], NULL
)) {
1824 start_page
= g_key_file_get_integer(Zathura
.Bookmarks
.data
, file
, bm_reserved_names
[BM_PAGE_ENTRY
], NULL
);
1826 /* get page offset */
1827 if (save_position
&& g_key_file_has_key(Zathura
.Bookmarks
.data
, file
, bm_reserved_names
[BM_PAGE_OFFSET
], NULL
)) {
1828 double ofs
= g_key_file_get_double(Zathura
.Bookmarks
.data
, file
, bm_reserved_names
[BM_PAGE_OFFSET
], NULL
);
1829 if (ofs
< 0) ofs
= 0;
1830 int px
= trunc(ofs
);
1832 if (px
> Zathura
.PDF
.page_gap
) px
= Zathura
.PDF
.page_gap
;
1833 Zathura
.PDF
.page_yskip_t
= ofs
;
1834 Zathura
.PDF
.page_yskip_pix
= px
;
1836 /* get zoom level */
1837 if (save_zoom_level
&& g_key_file_has_key(Zathura
.Bookmarks
.data
, file
, bm_reserved_names
[BM_PAGE_SCALE
], NULL
)) {
1838 Zathura
.PDF
.scale
= g_key_file_get_integer(Zathura
.Bookmarks
.data
, file
, bm_reserved_names
[BM_PAGE_SCALE
], NULL
);
1839 Zathura
.Global
.adjust_mode
= ADJUST_NONE
;
1841 if (Zathura
.PDF
.scale
> zoom_max
) Zathura
.PDF
.scale
= zoom_max
;
1842 if (Zathura
.PDF
.scale
< zoom_min
) Zathura
.PDF
.scale
= zoom_min
;
1844 /* open and read bookmark file */
1845 gsize number_of_keys
= 0;
1846 char **keys
= g_key_file_get_keys(Zathura
.Bookmarks
.data
, file
, &number_of_keys
, NULL
);
1848 for (gsize i
= 0; i
< number_of_keys
; ++i
) {
1849 if (!is_reserved_bm_name(keys
[i
])) {
1850 Zathura
.Bookmarks
.bookmarks
= safe_realloc((void **)&Zathura
.Bookmarks
.bookmarks
, Zathura
.Bookmarks
.number_of_bookmarks
+1, sizeof(Bookmark
));
1851 if (!Zathura
.Bookmarks
.bookmarks
) out_of_memory();
1853 Zathura
.Bookmarks
.bookmarks
[Zathura
.Bookmarks
.number_of_bookmarks
].id
= g_strdup(keys
[i
]);
1854 Zathura
.Bookmarks
.bookmarks
[Zathura
.Bookmarks
.number_of_bookmarks
].page
= g_key_file_get_integer(Zathura
.Bookmarks
.data
, file
, keys
[i
], NULL
);
1856 ++Zathura
.Bookmarks
.number_of_bookmarks
;
1863 /* set window title */
1864 gtk_window_set_title(GTK_WINDOW(Zathura
.UI
.window
), basename(file
));
1867 set_page_keep_ofs(start_page
);
1874 notify(WARNING
, errmsg
);
1882 //==========================================================================
1886 //==========================================================================
1887 gboolean
open_stdin (gchar
*password
) {
1888 GError
*error
= NULL
;
1890 gint handle
= g_file_open_tmp("zathura.stdin.XXXXXX.pdf", &file
, &error
);
1892 gchar
*message
= g_strdup_printf("Can not create temporary file: %s", error
->message
);
1893 notify(ERROR
, message
);
1895 g_error_free(error
);
1899 // read from stdin and dump to temporary file
1900 int stdinfno
= fileno(stdin
);
1901 if (stdinfno
== -1) {
1902 gchar
*message
= g_strdup_printf("Can not read from stdin.");
1903 notify(ERROR
, message
);
1912 char buffer
[BUFSIZ
];
1914 while ((count
= read(stdinfno
, buffer
, BUFSIZ
)) > 0) {
1915 if (write(handle
, buffer
, count
) != count
) {
1916 gchar
*message
= g_strdup_printf("Can not write to temporary file: %s", file
);
1917 notify(ERROR
, message
);
1928 gchar
*message
= g_strdup_printf("Can not read from stdin.");
1929 notify(ERROR
, message
);
1937 if (Zathura
.StdinSupport
.file
) g_unlink(Zathura
.StdinSupport
.file
);
1938 g_free(Zathura
.StdinSupport
.file
);
1939 Zathura
.StdinSupport
.file
= file
;
1941 return open_file(Zathura
.StdinSupport
.file
, password
);
1945 //==========================================================================
1949 //==========================================================================
1950 void open_uri (char *uri
) {
1951 char *escaped_uri
= g_shell_quote(uri
);
1952 char *uri_cmd
= g_strdup_printf(uri_command
, escaped_uri
);
1955 g_free(escaped_uri
);
1959 //==========================================================================
1963 //==========================================================================
1964 void out_of_memory (void) {
1965 printf("error: out of memory\n");
1971 //==========================================================================
1975 //==========================================================================
1976 void update_status (void) {
1978 gtk_label_set_markup((GtkLabel
*)Zathura
.Global
.status_text
, Zathura
.State
.filename
);
1980 if (Zathura
.PDF
.document
&& Zathura
.PDF
.pages
) {
1981 int page
= Zathura
.PDF
.page_number
;
1982 g_free(Zathura
.State
.pages
);
1983 Zathura
.State
.pages
= g_strdup_printf("[%i/%i]", page
+1, Zathura
.PDF
.number_of_pages
);
1986 char *zoom_level
= (Zathura
.PDF
.scale
!= 0 ? g_strdup_printf("%d%%", Zathura
.PDF
.scale
) : g_strdup(""));
1987 char *goto_mode
= (Zathura
.Global
.goto_mode
== GOTO_LABELS
? "L" : (Zathura
.Global
.goto_mode
== GOTO_OFFSET
? "O" : "D"));
1988 char *status_text
= g_strdup_printf("%s [%s] %s (%d%%)", zoom_level
, goto_mode
, Zathura
.State
.pages
, Zathura
.State
.scroll_percentage
);
1989 gtk_label_set_markup((GtkLabel
*)Zathura
.Global
.status_state
, status_text
);
1990 g_free(status_text
);
1995 //==========================================================================
1997 // read_bookmarks_file
1999 // this also removes groups that has no corresponding file
2001 //==========================================================================
2002 void read_bookmarks_file (void) {
2003 /* free it at first */
2004 if (Zathura
.Bookmarks
.data
) g_key_file_free(Zathura
.Bookmarks
.data
);
2005 /* create or open existing bookmark file */
2006 Zathura
.Bookmarks
.data
= g_key_file_new();
2007 if (!g_file_test(Zathura
.Bookmarks
.file
, G_FILE_TEST_IS_REGULAR
)) {
2008 /* file does not exist */
2009 gchar
*s
= g_strdup_printf("# Zathura bookmarks\n\n[%s]\n%s=0\n", main_group_name
, lastcheck_key_name
);
2010 g_file_set_contents(Zathura
.Bookmarks
.file
, s
, -1, NULL
);
2013 GError
*error
= NULL
;
2014 if (!g_key_file_load_from_file(Zathura
.Bookmarks
.data
, Zathura
.Bookmarks
.file
, G_KEY_FILE_KEEP_COMMENTS
|G_KEY_FILE_KEEP_TRANSLATIONS
, &error
)) {
2015 gchar
*message
= g_strdup_printf("Could not load bookmark file: %s", error
->message
);
2016 notify(ERROR
, message
);
2022 //==========================================================================
2024 // write_bookmarks_file
2026 //==========================================================================
2027 void write_bookmarks_file (void) {
2028 if (!Zathura
.Bookmarks
.data
) return; /* nothing to do */
2030 /* scan groups, and remove groups for which we don't have a file anymore */
2032 double lastcheck
= 0;
2033 if (g_key_file_has_key(Zathura
.Bookmarks
.data
, main_group_name
, lastcheck_key_name
, &err
)) {
2034 g_key_file_get_double(Zathura
.Bookmarks
.data
, main_group_name
, lastcheck_key_name
, &err
);
2036 if (err
) g_error_free(err
);
2037 const double currtime
= sys_time();
2038 /* check once a hour */
2039 if (lastcheck
+60*60 <= currtime
/*|| true*/) {
2040 gchar
**groups
= g_key_file_get_groups(Zathura
.Bookmarks
.data
, NULL
);
2042 for (gchar
**grp
= groups
; *grp
; ++grp
) {
2043 gchar
*gname
= *grp
;
2044 if (strcmp(gname
, main_group_name
) != 0 && access(gname
, R_OK
) != 0) {
2045 //fprintf(stderr, "***<%s>\n", gname);
2047 g_key_file_remove_group(Zathura
.Bookmarks
.data
, gname
, &err
);
2048 if (err
) g_error_free(err
);
2053 /* save check time */
2054 g_key_file_set_double(Zathura
.Bookmarks
.data
, main_group_name
, lastcheck_key_name
, currtime
);
2057 /* save bookmarks */
2058 for (int i
= 0; i
< Zathura
.Bookmarks
.number_of_bookmarks
; ++i
) {
2059 g_key_file_set_integer(Zathura
.Bookmarks
.data
, Zathura
.PDF
.file
, Zathura
.Bookmarks
.bookmarks
[i
].id
, Zathura
.Bookmarks
.bookmarks
[i
].page
);
2062 /* convert file and save it */
2063 gchar
*bookmarks
= g_key_file_to_data(Zathura
.Bookmarks
.data
, NULL
, NULL
);
2064 g_file_set_contents(Zathura
.Bookmarks
.file
, bookmarks
, -1, NULL
);
2069 //==========================================================================
2073 //==========================================================================
2074 void free_bookmarks (void) {
2075 for (int i
= 0; i
< Zathura
.Bookmarks
.number_of_bookmarks
; ++i
) g_free(Zathura
.Bookmarks
.bookmarks
[i
].id
);
2076 free(Zathura
.Bookmarks
.bookmarks
);
2077 Zathura
.Bookmarks
.bookmarks
= NULL
;
2078 Zathura
.Bookmarks
.number_of_bookmarks
= 0;
2082 //==========================================================================
2084 // read_configuration_file
2086 //==========================================================================
2087 void read_configuration_file (const char *rcfile
) {
2088 if (!rcfile
) return;
2089 if (!g_file_test(rcfile
, G_FILE_TEST_IS_REGULAR
)) return;
2090 char *content
= NULL
;
2091 if (g_file_get_contents(rcfile
, &content
, NULL
, NULL
)) {
2092 gchar
**lines
= g_strsplit(content
, "\n", -1);
2093 int n
= g_strv_length(lines
)-1;
2094 for (int i
= 0; i
<= n
; ++i
) {
2095 if (!strlen(lines
[i
])) continue;
2096 gchar
**pre_tokens
= g_strsplit_set(lines
[i
], "\t ", -1);
2097 int pre_length
= g_strv_length(pre_tokens
);
2098 gchar
** tokens
= g_malloc0(sizeof(gchar
*)*(pre_length
+1));
2099 gchar
** tokp
= tokens
;
2101 for (int f
= 0; f
!= pre_length
; ++f
) {
2102 if (strlen(pre_tokens
[f
])) {
2103 *tokp
++ = pre_tokens
[f
];
2107 if (!strcmp(tokens
[0], "set")) cmd_set(length
-1, tokens
+1);
2108 else if (!strcmp(tokens
[0], "map")) cmd_map(length
-1, tokens
+1);
2117 //==========================================================================
2119 // read_configuration
2121 //==========================================================================
2122 void read_configuration (void) {
2123 char *zathurarc
= g_build_filename(Zathura
.Config
.config_dir
, ZATHURA_RC
, NULL
);
2124 read_configuration_file(GLOBAL_RC
);
2125 read_configuration_file(zathurarc
);
2130 //==========================================================================
2134 //==========================================================================
2135 void recalc_rectangle (int page_id
, PopplerRectangle
* rectangle
) {
2136 double x1
= rectangle
->x1
;
2137 double x2
= rectangle
->x2
;
2138 double y1
= rectangle
->y1
;
2139 double y2
= rectangle
->y2
;
2140 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
2141 const double page_width
= current_page
->width
;
2142 const double page_height
= current_page
->height
;
2144 double scale
= ((double) Zathura
.PDF
.scale
/100.0);
2145 int rotate
= Zathura
.PDF
.rotate
;
2148 rectangle
->x1
= y2
*scale
;
2149 rectangle
->y1
= x1
*scale
;
2150 rectangle
->x2
= y1
*scale
;
2151 rectangle
->y2
= x2
*scale
;
2154 rectangle
->x1
= (page_width
-x2
)*scale
;
2155 rectangle
->y1
= y2
*scale
;
2156 rectangle
->x2
= (page_width
-x1
)*scale
;
2157 rectangle
->y2
= y1
*scale
;
2160 rectangle
->x1
= (page_height
-y1
)*scale
;
2161 rectangle
->y1
= (page_width
-x2
)*scale
;
2162 rectangle
->x2
= (page_height
-y2
)*scale
;
2163 rectangle
->y2
= (page_width
-x1
)*scale
;
2166 rectangle
->x1
= x1
*scale
;
2167 rectangle
->y1
= (page_height
-y1
)*scale
;
2168 rectangle
->x2
= x2
*scale
;
2169 rectangle
->y2
= (page_height
-y2
)*scale
;
2175 //==========================================================================
2177 // create_completion_row
2179 //==========================================================================
2180 GtkEventBox
*create_completion_row (GtkBox
*results
, char *command
, char *description
, gboolean group
) {
2181 GtkBox
*col
= GTK_BOX(gtk_hbox_new(FALSE
, 0));
2182 GtkEventBox
*row
= GTK_EVENT_BOX(gtk_event_box_new());
2183 GtkLabel
*show_command
= GTK_LABEL(gtk_label_new(NULL
));
2184 GtkLabel
*show_description
= GTK_LABEL(gtk_label_new(NULL
));
2185 gtk_misc_set_alignment(GTK_MISC(show_command
), 0.0, 0.0);
2186 gtk_misc_set_alignment(GTK_MISC(show_description
), 0.0, 0.0);
2188 gtk_misc_set_padding(GTK_MISC(show_command
), 2.0, 4.0);
2189 gtk_misc_set_padding(GTK_MISC(show_description
), 2.0, 4.0);
2191 gtk_misc_set_padding(GTK_MISC(show_command
), 1.0, 1.0);
2192 gtk_misc_set_padding(GTK_MISC(show_description
), 1.0, 1.0);
2194 gtk_label_set_use_markup(show_command
, TRUE
);
2195 gtk_label_set_use_markup(show_description
, TRUE
);
2196 gchar
*c
= g_markup_printf_escaped(FORMAT_COMMAND
, (command
? command
: ""));
2197 gchar
*d
= g_markup_printf_escaped(FORMAT_DESCRIPTION
, (description
? description
: ""));
2198 gtk_label_set_markup(show_command
, c
);
2199 gtk_label_set_markup(show_description
, d
);
2203 gtk_widget_modify_fg(GTK_WIDGET(show_command
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_g_fg
));
2204 gtk_widget_modify_fg(GTK_WIDGET(show_description
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_g_fg
));
2205 gtk_widget_modify_bg(GTK_WIDGET(row
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_g_bg
));
2207 gtk_widget_modify_fg(GTK_WIDGET(show_command
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_fg
));
2208 gtk_widget_modify_fg(GTK_WIDGET(show_description
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_fg
));
2209 gtk_widget_modify_bg(GTK_WIDGET(row
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_bg
));
2211 gtk_widget_modify_font(GTK_WIDGET(show_command
), Zathura
.Style
.font
);
2212 gtk_widget_modify_font(GTK_WIDGET(show_description
), Zathura
.Style
.font
);
2213 gtk_box_pack_start(GTK_BOX(col
), GTK_WIDGET(show_command
), TRUE
, TRUE
, 2);
2214 gtk_box_pack_start(GTK_BOX(col
), GTK_WIDGET(show_description
), FALSE
, FALSE
, 2);
2215 gtk_container_add(GTK_CONTAINER(row
), GTK_WIDGET(col
));
2216 gtk_box_pack_start(results
, GTK_WIDGET(row
), FALSE
, FALSE
, 0);
2221 //==========================================================================
2223 // set_completion_row_color
2225 //==========================================================================
2226 void set_completion_row_color (GtkBox
*results
, int mode
, int id
) {
2227 GtkEventBox
*row
= (GtkEventBox
*)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(results
)), id
);
2229 GtkBox
*col
= (GtkBox
*)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(row
)), 0);
2230 GtkLabel
*cmd
= (GtkLabel
*)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(col
)), 0);
2231 GtkLabel
*cdesc
= (GtkLabel
*)g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(col
)), 1);
2232 if (mode
== NORMAL
) {
2233 gtk_widget_modify_fg(GTK_WIDGET(cmd
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_fg
));
2234 gtk_widget_modify_fg(GTK_WIDGET(cdesc
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_fg
));
2235 gtk_widget_modify_bg(GTK_WIDGET(row
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_bg
));
2237 gtk_widget_modify_fg(GTK_WIDGET(cmd
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_hl_fg
));
2238 gtk_widget_modify_fg(GTK_WIDGET(cdesc
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_hl_fg
));
2239 gtk_widget_modify_bg(GTK_WIDGET(row
), GTK_STATE_NORMAL
, &(Zathura
.Style
.completion_hl_bg
));
2245 //==========================================================================
2247 // set_page_keep_ofs
2249 // used in file open function to set the page, but don't reset the offsets
2251 //==========================================================================
2252 void set_page_keep_ofs (int page
) {
2253 if (page
>= Zathura
.PDF
.number_of_pages
|| page
< 0) {
2254 notify(WARNING
, "Could not open page");
2257 Zathura
.PDF
.page_number
= page
;
2258 Zathura
.Search
.draw
= FALSE
;
2259 switch_view(Zathura
.UI
.document
);
2264 //==========================================================================
2268 //==========================================================================
2269 void set_page (int page
) {
2270 if (page
>= Zathura
.PDF
.number_of_pages
|| page
< 0) {
2271 notify(WARNING
, "Could not open page");
2274 Zathura
.PDF
.page_number
= page
;
2275 Zathura
.PDF
.page_yskip_t
= 0;
2276 Zathura
.PDF
.page_yskip_pix
= 0;
2277 Zathura
.Search
.draw
= FALSE
;
2280 switch_view(Zathura
.UI
.document
);
2282 sc_scroll(&argument
);
2286 //==========================================================================
2290 //==========================================================================
2291 void switch_view (GtkWidget
*widget
) {
2292 GtkWidget
*child
= gtk_bin_get_child(GTK_BIN(Zathura
.UI
.viewport
));
2293 if (child
== widget
) return;
2295 g_object_ref(child
);
2296 gtk_container_remove(GTK_CONTAINER(Zathura
.UI
.viewport
), child
);
2298 gtk_container_add(GTK_CONTAINER(Zathura
.UI
.viewport
), GTK_WIDGET(widget
));
2302 //==========================================================================
2306 //==========================================================================
2307 Completion
*completion_init (void) {
2308 Completion
*completion
= malloc(sizeof(Completion
));
2309 if (!completion
) out_of_memory();
2310 completion
->groups
= NULL
;
2315 //==========================================================================
2317 // completion_group_create
2319 //==========================================================================
2320 CompletionGroup
*completion_group_create (char *name
) {
2321 CompletionGroup
*group
= malloc(sizeof(CompletionGroup
));
2322 if (!group
) out_of_memory();
2323 group
->value
= (name
? g_strdup(name
) : NULL
);
2324 group
->elements
= NULL
;
2330 //==========================================================================
2332 // completion_add_group
2334 //==========================================================================
2335 void completion_add_group (Completion
*completion
, CompletionGroup
*group
) {
2336 CompletionGroup
*cg
= completion
->groups
;
2337 while (cg
&& cg
->next
) cg
= cg
->next
;
2338 if (cg
) cg
->next
= group
; else completion
->groups
= group
;
2342 //==========================================================================
2346 //==========================================================================
2347 void completion_free (Completion
*completion
) {
2348 CompletionGroup
*group
= completion
->groups
;
2350 CompletionGroup
*ng
;
2351 CompletionElement
*element
= group
->elements
;
2353 CompletionElement
*ne
= element
->next
;
2354 g_free(element
->value
);
2355 g_free(element
->description
);
2360 g_free(group
->value
);
2368 //==========================================================================
2370 // completion_group_add_element
2372 //==========================================================================
2373 void completion_group_add_element (CompletionGroup
*group
, char *name
, char *description
) {
2374 CompletionElement
*el
= group
->elements
;
2375 while (el
&& el
->next
) el
= el
->next
;
2376 CompletionElement
*new_element
= malloc(sizeof(CompletionElement
));
2377 if (!new_element
) out_of_memory();
2378 new_element
->value
= (name
? g_strdup(name
) : NULL
);
2379 new_element
->description
= (description
? g_strdup(description
) : NULL
);
2380 new_element
->next
= NULL
;
2381 if (el
) el
->next
= new_element
; else group
->elements
= new_element
;
2385 //==========================================================================
2389 // thread implementation
2391 //==========================================================================
2392 void *search (void *parameter
) {
2393 Argument
*argument
= (Argument
*)parameter
;
2394 static char *search_item
;
2395 static int direction
;
2396 static int next_page
= 0;
2397 gchar
*old_query
= NULL
;
2398 GList
*results
= NULL
;
2399 if (argument
->n
!= NO_SEARCH
) {
2400 /* search document */
2401 if (argument
->n
) direction
= (argument
->n
== BACKWARD
? -1 : 1);
2402 if (argument
->data
) {
2403 if (search_item
) g_free(search_item
);
2404 search_item
= g_strdup((char *)argument
->data
);
2406 g_free(argument
->data
);
2410 if (!Zathura
.PDF
.document
|| !search_item
|| !strlen(search_item
)) {
2413 Zathura
.Thread
.search_thread_running
= FALSE
;
2415 g_thread_exit(NULL
);
2418 old_query
= Zathura
.Search
.query
;
2420 /* delete old results */
2421 if (Zathura
.Search
.results
) {
2422 g_list_free(Zathura
.Search
.results
);
2423 Zathura
.Search
.results
= NULL
;
2426 Zathura
.Search
.query
= g_strdup(search_item
);
2428 int number_of_pages
= Zathura
.PDF
.number_of_pages
;
2429 int page_number
= Zathura
.PDF
.page_number
;
2433 int page_counter
= (g_strcmp0(old_query
,search_item
) == 0 ? 1 : 0);
2434 for( ; page_counter
<= number_of_pages
; ++page_counter
) {
2436 if (Zathura
.Thread
.search_thread_running
== FALSE
) {
2439 g_thread_exit(NULL
);
2443 next_page
= (number_of_pages
+page_number
+page_counter
*direction
)%number_of_pages
;
2446 PopplerPage
*page
= poppler_document_get_page(Zathura
.PDF
.document
, next_page
);
2451 g_thread_exit(NULL
);
2455 results
= poppler_page_find_text(page
, search_item
);
2458 g_object_unref(page
);
2463 Zathura
.Search
.draw
= TRUE
;
2464 g_free(argument
->data
);
2470 gdk_threads_enter();
2472 set_page(next_page
);
2474 if (Zathura
.Search
.results
) g_list_free(Zathura
.Search
.results
);
2476 Zathura
.Search
.results
= results
;
2477 Zathura
.Search
.page
= next_page
;
2478 Zathura
.Search
.draw
= TRUE
;
2479 Zathura
.Search
.query
= g_strdup(search_item
);
2481 gdk_threads_leave();
2485 Zathura
.Thread
.search_thread_running
= FALSE
;
2489 g_thread_exit(NULL
);
2494 //==========================================================================
2498 // shortcut implementation
2500 //==========================================================================
2501 void sc_abort (Argument
*argument
) {
2503 if (Zathura
.Global
.buffer
) {
2504 g_string_free(Zathura
.Global
.buffer
, TRUE
);
2505 Zathura
.Global
.buffer
= NULL
;
2506 gtk_label_set_markup((GtkLabel
*)Zathura
.Global
.status_buffer
, "");
2509 if (!Zathura
.Global
.show_inputbar
) gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
2511 /* Set back to normal mode */
2512 change_mode(NORMAL
);
2513 switch_view(Zathura
.UI
.document
);
2517 //==========================================================================
2521 //==========================================================================
2522 void sc_adjust_window (Argument
*argument
) {
2523 //fprintf(stderr, "RESIZED!\n");
2524 surfCacheDestroy(); // why not?
2526 if (!Zathura
.PDF
.document
) return;
2527 Zathura
.Global
.adjust_mode
= argument
->n
;
2528 GtkAdjustment
*adjustment
;
2530 if (argument
->n
== ADJUST_BESTFIT
) {
2531 adjustment
= gtk_scrolled_window_get_vadjustment(Zathura
.UI
.view
);
2532 } else if (argument
->n
== ADJUST_WIDTH
) {
2533 adjustment
= gtk_scrolled_window_get_hadjustment(Zathura
.UI
.view
);
2537 view_size
= gtk_adjustment_get_page_size(adjustment
);
2539 Page
*current_page
= Zathura
.PDF
.pages
[Zathura
.PDF
.page_number
];
2540 double page_width
= current_page
->width
;
2541 double page_height
= current_page
->height
;
2543 if (Zathura
.PDF
.rotate
== 90 || Zathura
.PDF
.rotate
== 270) {
2544 double swap
= page_width
;
2545 page_width
= page_height
;
2549 if (argument
->n
== ADJUST_BESTFIT
) {
2550 // for best fit, calculate average page height
2551 if (Zathura
.PDF
.number_of_pages
> 0) {
2553 for (int f
= 0; f
< Zathura
.PDF
.number_of_pages
; ++f
) {
2554 //allwdt += (Zathura.PDF.rotate == 90 || Zathura.PDF.rotate == 270 ? current_page->height : current_page->width);
2555 Page
*cpg
= Zathura
.PDF
.pages
[f
];
2556 total
+= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? cpg
->height
: cpg
->width
);
2558 total
/= Zathura
.PDF
.number_of_pages
;
2559 Zathura
.PDF
.scale
= (view_size
/total
)*100;
2562 Zathura
.PDF
.scale
= (view_size
/page_width
)*100;
2565 draw(Zathura
.PDF
.page_number
);
2570 //==========================================================================
2572 // sc_xcenter_window
2574 //==========================================================================
2575 void sc_xcenter_window (Argument
*argument
) {
2576 if (!Zathura
.PDF
.document
) return;
2577 Zathura
.Global
.xcenter_mode
= argument
->n
;
2578 draw(Zathura
.PDF
.page_number
);
2583 //==========================================================================
2587 //==========================================================================
2588 void sc_change_buffer (Argument
*argument
) {
2589 if (!Zathura
.Global
.buffer
) return;
2590 int buffer_length
= Zathura
.Global
.buffer
->len
;
2591 if (argument
->n
== DELETE_LAST
) {
2592 if (buffer_length
-1 == 0) {
2593 g_string_free(Zathura
.Global
.buffer
, TRUE
);
2594 Zathura
.Global
.buffer
= NULL
;
2595 gtk_label_set_markup((GtkLabel
*) Zathura
.Global
.status_buffer
, "");
2597 GString
*temp
= g_string_new_len(Zathura
.Global
.buffer
->str
, buffer_length
-1);
2598 g_string_free(Zathura
.Global
.buffer
, TRUE
);
2599 Zathura
.Global
.buffer
= temp
;
2600 gtk_label_set_markup((GtkLabel
*)Zathura
.Global
.status_buffer
, Zathura
.Global
.buffer
->str
);
2606 //==========================================================================
2610 //==========================================================================
2611 void sc_change_mode (Argument
*argument
) {
2612 if (argument
) change_mode(argument
->n
);
2616 //==========================================================================
2618 // sc_focus_inputbar
2620 //==========================================================================
2621 void sc_focus_inputbar (Argument
*argument
) {
2622 if (!(GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura
.UI
.inputbar
)))) gtk_widget_show(GTK_WIDGET(Zathura
.UI
.inputbar
));
2623 if (argument
->data
) {
2625 if (argument
->n
== APPEND_FILEPATH
) {
2626 data
= g_strdup_printf("%s%s", (const char *)argument
->data
, Zathura
.PDF
.file
);
2628 data
= g_strdup((const char *)argument
->data
);
2630 notify(DEFAULT
, data
);
2632 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.inputbar
));
2633 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), -1);
2638 //==========================================================================
2642 //==========================================================================
2643 void sc_follow (Argument
*argument
) {
2644 if (!Zathura
.PDF
.document
) return;
2645 Page
*current_page
= Zathura
.PDF
.pages
[Zathura
.PDF
.page_number
];
2649 GList
*link_list
= poppler_page_get_link_mapping(current_page
->page
);
2651 link_list
= g_list_reverse(link_list
);
2653 if (g_list_length(link_list
) <= 0) return;
2655 for (GList
*links
= link_list
; links
; links
= g_list_next(links
)) {
2656 PopplerLinkMapping
*link_mapping
= (PopplerLinkMapping
*)links
->data
;
2657 PopplerRectangle
*link_rectangle
= &link_mapping
->area
;
2658 PopplerAction
*action
= link_mapping
->action
;
2660 /* only handle URI and internal links */
2661 if (action
->type
== POPPLER_ACTION_URI
|| action
->type
== POPPLER_ACTION_GOTO_DEST
) {
2662 highlight_result(Zathura
.PDF
.page_number
, link_rectangle
);
2664 recalc_rectangle(Zathura
.PDF
.page_number
, link_rectangle
);
2665 cairo_t
*cairo
= cairo_create(create_page_surface(Zathura
.PDF
.page_number
));
2666 cairo_select_font_face(cairo
, font
, CAIRO_FONT_SLANT_NORMAL
, CAIRO_FONT_WEIGHT_BOLD
);
2667 cairo_set_font_size(cairo
, 10);
2668 cairo_move_to(cairo
, link_rectangle
->x1
+1, link_rectangle
->y1
-1);
2669 char *link_number
= g_strdup_printf("%i", link_id
++);
2670 cairo_show_text(cairo
, link_number
);
2671 cairo_destroy(cairo
);
2672 g_free(link_number
);
2676 gtk_widget_queue_draw(Zathura
.UI
.drawing_area
);
2677 poppler_page_free_link_mapping(link_list
);
2679 /* replace default inputbar handler */
2680 g_signal_handler_disconnect((gpointer
) Zathura
.UI
.inputbar
, Zathura
.Handler
.inputbar_activate
);
2681 Zathura
.Handler
.inputbar_activate
= g_signal_connect(G_OBJECT(Zathura
.UI
.inputbar
), "activate", G_CALLBACK(cb_inputbar_form_activate
), NULL
);
2683 argument
->data
= "Follow hint: ";
2684 sc_focus_inputbar(argument
);
2688 //==========================================================================
2692 //==========================================================================
2693 void sc_navigate (Argument
*argument
) {
2694 if (!Zathura
.PDF
.document
) return;
2696 int number_of_pages
= Zathura
.PDF
.number_of_pages
;
2697 int new_page
= Zathura
.PDF
.page_number
;
2699 if (argument
->n
== NEXT
) {
2700 new_page
= (scroll_wrap
? (new_page
+1)%number_of_pages
: new_page
+1);
2701 } else if (argument
->n
== PREVIOUS
) {
2702 new_page
= (scroll_wrap
? (new_page
+number_of_pages
-1)%number_of_pages
: new_page
-1);
2703 if (new_page
< 0 && Zathura
.PDF
.page_yskip_t
> 0) new_page
= 0;
2705 if (!scroll_wrap
&& (new_page
< 0 || new_page
>= number_of_pages
)) return;
2712 //==========================================================================
2716 //==========================================================================
2717 void sc_recolor (Argument
*argument
) {
2718 Zathura
.Global
.recolor
= !Zathura
.Global
.recolor
;
2719 draw(Zathura
.PDF
.page_number
);
2723 //==========================================================================
2727 //==========================================================================
2728 void sc_reload (Argument
*argument
) {
2729 draw(Zathura
.PDF
.page_number
);
2731 GtkAdjustment
*vadjustment
= gtk_scrolled_window_get_vadjustment(Zathura
.UI
.view
);
2732 GtkAdjustment
*hadjustment
= gtk_scrolled_window_get_hadjustment(Zathura
.UI
.view
);
2734 /* save old information */
2736 char *path
= (Zathura
.PDF
.file
? strdup(Zathura
.PDF
.file
) : NULL
);
2737 char *password
= (Zathura
.PDF
.password
? strdup(Zathura
.PDF
.password
) : NULL
);
2738 int scale
= Zathura
.PDF
.scale
;
2739 int page
= Zathura
.PDF
.page_number
;
2740 int rotate
= Zathura
.PDF
.rotate
;
2741 gdouble va
= gtk_adjustment_get_value(vadjustment
);
2742 gdouble ha
= gtk_adjustment_get_value(hadjustment
);
2745 /* reopen and restore settings */
2747 open_file(path
, password
);
2750 Zathura
.PDF
.scale
= scale
;
2751 Zathura
.PDF
.rotate
= rotate
;
2753 gtk_adjustment_set_value(vadjustment
, va
);
2754 gtk_adjustment_set_value(hadjustment
, ha
);
2757 if (Zathura
.PDF
.number_of_pages
!= 0) {
2758 if (page
>= Zathura
.PDF
.number_of_pages
-1) page
= Zathura
.PDF
.number_of_pages
-1;
2759 Zathura
.PDF
.page_number
= page
;
2760 draw(Zathura
.PDF
.page_number
);
2763 if (path
) free(path
);
2764 if (password
) free(password
);
2768 //==========================================================================
2772 //==========================================================================
2773 void sc_rotate (Argument
*argument
) {
2774 Zathura
.PDF
.rotate
= (Zathura
.PDF
.rotate
+90)%360;
2775 Zathura
.Search
.draw
= TRUE
;
2776 draw(Zathura
.PDF
.page_number
);
2780 //==========================================================================
2784 //==========================================================================
2785 void sc_scroll (Argument
*argument
) {
2786 GtkAdjustment
*adjustment
;
2788 if (argument
->n
== LEFT
|| argument
->n
== RIGHT
) {
2789 adjustment
= gtk_scrolled_window_get_hadjustment(Zathura
.UI
.view
);
2791 adjustment
= gtk_scrolled_window_get_vadjustment(Zathura
.UI
.view
);
2794 if (argument
->n
== WINDOW_UP
|| argument
->n
== WINDOW_DOWN
||
2795 argument
->n
== UP
|| argument
->n
== DOWN
|| argument
->n
== FULL_WINDOW_DOWN
)
2797 adjustment
= gtk_scrolled_window_get_vadjustment(Zathura
.UI
.view
);
2798 int window_y
= gtk_adjustment_get_page_size(adjustment
);
2799 int pgdelta
= window_y
/6;
2800 if (pgdelta
< 2) return; // are you nuts?
2802 argument
->n
== WINDOW_UP
|| argument
->n
== WINDOW_DOWN
? window_y
-pgdelta
:
2803 argument
->n
== FULL_WINDOW_DOWN
? window_y
:
2805 int prevypos
= calc_current_document_screen_offset();
2806 int st_page
= Zathura
.PDF
.page_number
;
2807 if (argument
->n
== WINDOW_UP
|| argument
->n
== UP
) {
2808 scroll_up_pixels_no_draw(delta
);
2810 scroll_down_pixels_no_draw(delta
);
2811 // hack for full-page scroll, why not?
2812 // if we're moved past the page into the gap, skip the gap too
2813 if (Zathura
.PDF
.page_yskip_t
>= 1.0f
&& Zathura
.PDF
.page_number
+1 < Zathura
.PDF
.number_of_pages
) {
2814 Zathura
.PDF
.page_number
+= 1;
2815 Zathura
.PDF
.page_yskip_t
= 0;
2816 Zathura
.PDF
.page_yskip_pix
= 0;
2820 if (smooth_scrolling
> 0) {
2821 // cache destination surfaces
2822 int top_page
= Zathura
.PDF
.page_number
;
2823 if (st_page
< top_page
) {
2824 // we're scrolling down, cache all pages down
2825 while (st_page
< top_page
) {
2826 create_page_surface(st_page
);
2829 // cache all potentially visible pages too
2831 while (ypos
< window_y
) {
2832 create_page_surface(top_page
);
2833 int hgt
= get_page_screen_height(top_page
);
2838 // and one more, because why not?
2839 create_page_surface(top_page
);
2841 // we're scrolling up
2842 create_page_surface(top_page
);
2844 const int currypos
= calc_current_document_screen_offset();
2845 const int delta_smooth
= (smooth_scrolling
> 666 ? 666 : (int)smooth_scrolling
);
2846 while (currypos
!= prevypos
) {
2847 if (prevypos
< currypos
) {
2848 if ((prevypos
+= delta_smooth
) > currypos
) prevypos
= currypos
;
2850 if ((prevypos
-= delta_smooth
) < currypos
) prevypos
= currypos
;
2852 convert_screen_document_offset_to_pos(prevypos
, &Zathura
.PDF
.page_number
, &Zathura
.PDF
.page_yskip_t
, &Zathura
.PDF
.page_yskip_pix
);
2853 render_view(Zathura
.UI
.drawing_area
, FALSE
); // no full clear
2856 int top_page
= Zathura
.PDF
.page_number
;
2858 if (top_page
>= 0 && top_page
< Zathura
.PDF
.number_of_pages
) {
2859 Zathura
.Search
.draw
= TRUE
;
2861 gtk_adjustment_set_value(adjustment
, 0);
2864 //gtk_adjustment_set_value(adjustment, 0);
2871 //gdouble view_size = gtk_adjustment_get_page_size(adjustment);
2872 gdouble value
= gtk_adjustment_get_value(adjustment
);
2873 //gdouble max = gtk_adjustment_get_upper(adjustment)-view_size;
2874 //static gboolean ss = FALSE;
2877 gtk_adjustment_set_value(adjustment
, 0);
2880 if ((argument
->n
== UP
|| argument
->n
== HALF_UP
|| argument
->n
== FULL_UP
) && value
== 0) {
2881 int old_page
= Zathura
.PDF
.page_number
;
2885 if (scroll_wrap
|| Zathura
.PDF
.page_number
< old_page
) {
2891 } else if ((argument
->n
== DOWN
|| argument
->n
== HALF_DOWN
|| argument
->n
== FULL_DOWN
) /*&& value == max*/) {
2901 switch (argument->n) {
2902 case FULL_UP: new_value = (value-view_size < 0 ? 0 : value-view_size); break;
2903 case FULL_DOWN: new_value = (value+view_size > max ? max : value+view_size); break;
2904 case HALF_UP: new_value = (value-view_size/2 < 0 ? 0 : value-view_size/2); break;
2905 case HALF_DOWN: new_value = (value+view_size/2 > max ? max : value+view_size/2); break;
2906 case LEFT: case UP: new_value = (value-scroll_step < 0 ? 0 : value-scroll_step); break;
2907 case TOP: new_value = 0; break;
2908 case BOTTOM: new_value = max; break;
2909 default: new_value = (value+scroll_step > max ? max : value+scroll_step); break;
2912 if (!(argument->n == LEFT || argument->n == RIGHT)) Zathura.State.scroll_percentage = (max == 0 ? 0 : new_value*100/max);
2914 if (smooth_scrolling && !ss) {
2916 if (new_value > value) {
2917 for (i = value; i+smooth_scrolling < new_value; i += smooth_scrolling) gtk_adjustment_set_value(adjustment, i);
2919 for (i = value; i+smooth_scrolling > new_value; i -= smooth_scrolling) gtk_adjustment_set_value(adjustment, i);
2923 gtk_adjustment_set_value(adjustment, new_value);
2931 //==========================================================================
2935 //==========================================================================
2936 void sc_search (Argument
*argument
) {
2938 if (Zathura
.Thread
.search_thread_running
) {
2939 Zathura
.Thread
.search_thread_running
= FALSE
;
2941 gdk_threads_leave();
2942 g_thread_join(Zathura
.Thread
.search_thread
);
2943 gdk_threads_enter();
2947 Argument
*newarg
= g_malloc0(sizeof(Argument
));
2948 newarg
->n
= argument
->n
;
2949 newarg
->data
= (argument
->data
? g_strdup(argument
->data
) : NULL
);
2950 Zathura
.Thread
.search_thread_running
= TRUE
;
2951 Zathura
.Thread
.search_thread
= g_thread_create(search
, (gpointer
) newarg
, TRUE
, NULL
);
2956 //==========================================================================
2958 // sc_switch_goto_mode
2960 //==========================================================================
2961 void sc_switch_goto_mode (Argument
*argument
) {
2962 switch (Zathura
.Global
.goto_mode
) {
2963 case GOTO_LABELS
: Zathura
.Global
.goto_mode
= GOTO_OFFSET
; break;
2964 case GOTO_OFFSET
: Zathura
.Global
.goto_mode
= GOTO_DEFAULT
; break;
2966 if (Zathura
.Global
.enable_labelmode
) Zathura
.Global
.goto_mode
= GOTO_LABELS
; else Zathura
.Global
.goto_mode
= GOTO_OFFSET
;
2973 //==========================================================================
2975 // cb_index_row_activated
2977 //==========================================================================
2978 gboolean
cb_index_row_activated (GtkTreeView
*treeview
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, gpointer user_data
) {
2979 GtkTreeModel
*model
;
2982 g_object_get(treeview
, "model", &model
, NULL
);
2984 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
2985 PopplerAction
*action
;
2986 PopplerDest
*destination
;
2988 gtk_tree_model_get(model
, &iter
, 1, &action
, -1);
2989 if (!action
) return TRUE
;
2991 if (action
->type
== POPPLER_ACTION_GOTO_DEST
) {
2992 destination
= action
->goto_dest
.dest
;
2993 int page_number
= destination
->page_num
;
2994 if (action
->goto_dest
.dest
->type
== POPPLER_DEST_NAMED
) {
2995 PopplerDest
*d
= poppler_document_find_dest(Zathura
.PDF
.document
, action
->goto_dest
.dest
->named_dest
);
2997 page_number
= d
->page_num
;
2998 poppler_dest_free(d
);
3001 set_page(page_number
-1);
3003 Zathura
.Global
.show_index
= FALSE
;
3004 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.document
));
3008 Zathura
.Global
.mode
= NORMAL
;
3009 g_object_unref(model
);
3015 //==========================================================================
3017 // sc_navigate_index
3019 //==========================================================================
3020 void sc_navigate_index (Argument
*argument
) {
3021 if (!Zathura
.UI
.index
) return;
3023 GtkTreeView
*treeview
= gtk_container_get_children(GTK_CONTAINER(Zathura
.UI
.index
))->data
;
3026 gtk_tree_view_get_cursor(treeview
, &path
, NULL
);
3029 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
3031 GtkTreeIter child_iter
;
3033 gboolean is_valid_path
= TRUE
;
3035 switch (argument
->n
) {
3037 if (!gtk_tree_path_prev(path
)) {
3038 is_valid_path
= (gtk_tree_path_get_depth(path
) > 1 && gtk_tree_path_up(path
));
3041 while (gtk_tree_view_row_expanded(treeview
, path
)) {
3042 gtk_tree_model_get_iter(model
, &iter
, path
);
3043 /* select last child */
3044 gtk_tree_model_iter_nth_child(model
, &child_iter
, &iter
, gtk_tree_model_iter_n_children(model
, &iter
)-1);
3045 gtk_tree_path_free(path
);
3046 path
= gtk_tree_model_get_path(model
, &child_iter
);
3051 if (!gtk_tree_view_collapse_row(treeview
, path
) && gtk_tree_path_get_depth(path
) > 1) {
3052 gtk_tree_path_up(path
);
3053 gtk_tree_view_collapse_row(treeview
, path
);
3057 if (gtk_tree_view_row_expanded(treeview
, path
)) {
3058 gtk_tree_path_down(path
);
3061 gtk_tree_model_get_iter(model
, &iter
, path
);
3062 if (gtk_tree_model_iter_next(model
, &iter
)) {
3063 path
= gtk_tree_model_get_path(model
, &iter
);
3066 } while ((is_valid_path
= (gtk_tree_path_get_depth(path
) > 1)) && gtk_tree_path_up(path
));
3070 if (gtk_tree_view_expand_row(treeview
, path
, FALSE
)) gtk_tree_path_down(path
);
3073 cb_index_row_activated(treeview
, path
, NULL
, NULL
);
3077 if (is_valid_path
) gtk_tree_view_set_cursor(treeview
, path
, NULL
, FALSE
);
3079 gtk_tree_path_free(path
);
3083 //==========================================================================
3087 //==========================================================================
3088 void sc_toggle_index (Argument
*argument
) {
3089 if (!Zathura
.PDF
.document
) return;
3091 GtkWidget
*treeview
;
3092 GtkTreeModel
*model
;
3093 GtkCellRenderer
*renderer
;
3094 PopplerIndexIter
*iter
;
3096 if (!Zathura
.UI
.index
) {
3097 Zathura
.UI
.index
= gtk_scrolled_window_new (NULL
, NULL
);
3098 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura
.UI
.index
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
3100 if ((iter
= poppler_index_iter_new(Zathura
.PDF
.document
))) {
3101 model
= GTK_TREE_MODEL(gtk_tree_store_new(2, G_TYPE_STRING
, G_TYPE_POINTER
));
3103 build_index(model
, NULL
, iter
);
3105 poppler_index_iter_free(iter
);
3107 notify(WARNING
, "This document does not contain any index");
3108 Zathura
.UI
.index
= NULL
;
3112 treeview
= gtk_tree_view_new_with_model (model
);
3113 g_object_unref(model
);
3114 renderer
= gtk_cell_renderer_text_new();
3115 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (treeview
), 0, "Title", renderer
, "markup", 0, NULL
);
3116 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview
), FALSE
);
3117 g_object_set(G_OBJECT(renderer
), "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
3118 g_object_set(G_OBJECT(gtk_tree_view_get_column(GTK_TREE_VIEW(treeview
), 0)), "expand", TRUE
, NULL
);
3120 gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview
), gtk_tree_path_new_first(), NULL
, FALSE
);
3121 g_signal_connect(G_OBJECT(treeview
), "row-activated", G_CALLBACK(cb_index_row_activated
), NULL
);
3123 gtk_container_add (GTK_CONTAINER (Zathura
.UI
.index
), treeview
);
3124 gtk_widget_show (treeview
);
3125 gtk_widget_show(Zathura
.UI
.index
);
3128 if (!Zathura
.Global
.show_index
) {
3129 switch_view(Zathura
.UI
.index
);
3130 Zathura
.Global
.mode
= INDEX
;
3132 switch_view(Zathura
.UI
.document
);
3133 Zathura
.Global
.mode
= NORMAL
;
3136 Zathura
.Global
.show_index
= !Zathura
.Global
.show_index
;
3140 //==========================================================================
3142 // sc_toggle_inputbar
3144 //==========================================================================
3145 void sc_toggle_inputbar (Argument
*argument
) {
3146 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura
.UI
.inputbar
))) {
3147 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
3149 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.inputbar
));
3154 //==========================================================================
3156 // sc_toggle_fullscreen
3158 //==========================================================================
3159 void sc_toggle_fullscreen (Argument
*argument
) {
3160 static gboolean fs
= TRUE
;
3163 gtk_window_fullscreen(GTK_WINDOW(Zathura
.UI
.window
));
3164 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
3165 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.statusbar
));
3168 arg
.n
= ADJUST_BESTFIT
;
3169 sc_adjust_window(&arg
);
3171 Zathura
.Global
.mode
= FULLSCREEN
;
3174 gtk_window_unfullscreen(GTK_WINDOW(Zathura
.UI
.window
));
3175 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.inputbar
));
3176 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.statusbar
));
3178 Zathura
.Global
.mode
= NORMAL
;
3185 //==========================================================================
3187 // sc_toggle_statusbar
3189 //==========================================================================
3190 void sc_toggle_statusbar (Argument
*argument
) {
3191 if (GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura
.UI
.statusbar
))) {
3192 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.statusbar
));
3194 gtk_widget_show(GTK_WIDGET(Zathura
.UI
.statusbar
));
3199 //==========================================================================
3203 //==========================================================================
3204 void sc_quit (Argument
*argument
) {
3205 cb_destroy(NULL
, NULL
);
3209 //==========================================================================
3213 //==========================================================================
3214 void sc_zoom (Argument
*argument
) {
3215 bcmd_zoom(NULL
, argument
);
3219 //**************************************************************************
3221 // inputbar shortcut declarations
3223 //**************************************************************************
3225 //==========================================================================
3229 //==========================================================================
3230 void isc_abort (Argument
*argument
) {
3231 Argument arg
= { HIDE
};
3232 isc_completion(&arg
);
3234 notify(DEFAULT
, "");
3235 change_mode(NORMAL
);
3236 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.view
));
3238 if (!Zathura
.Global
.show_inputbar
) gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
3240 /* replace default inputbar handler */
3241 g_signal_handler_disconnect((gpointer
) Zathura
.UI
.inputbar
, Zathura
.Handler
.inputbar_activate
);
3242 Zathura
.Handler
.inputbar_activate
= g_signal_connect(G_OBJECT(Zathura
.UI
.inputbar
), "activate", G_CALLBACK(cb_inputbar_activate
), NULL
);
3247 //==========================================================================
3249 // isc_command_history
3251 //==========================================================================
3252 void isc_command_history (Argument
*argument
) {
3253 static int current
= 0;
3254 int length
= g_list_length(Zathura
.Global
.history
);
3256 current
= (length
+current
+(argument
->n
== NEXT
? 1 : -1))%length
;
3257 gchar
*command
= (gchar
*)g_list_nth_data(Zathura
.Global
.history
, current
);
3258 notify(DEFAULT
, command
);
3259 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.inputbar
));
3260 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), -1);
3265 //==========================================================================
3269 //==========================================================================
3270 void isc_completion (Argument
*argument
) {
3271 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(Zathura
.UI
.inputbar
), 1, -1);
3272 gchar
*tmp_string
= gtk_editable_get_chars(GTK_EDITABLE(Zathura
.UI
.inputbar
), 0, 1);
3273 gchar identifier
= tmp_string
[0];
3274 int length
= strlen(input
);
3276 if (!input
|| !tmp_string
) {
3277 if (input
) g_free(input
);
3278 if (tmp_string
) g_free(tmp_string
);
3282 /* get current information*/
3283 char *first_space
= strstr(input
, " ");
3284 char *current_command
;
3285 char *current_parameter
;
3286 int current_command_length
;
3289 current_command
= g_strdup(input
);
3290 current_command_length
= length
;
3291 current_parameter
= NULL
;
3293 int offset
= abs(input
-first_space
);
3294 current_command
= g_strndup(input
, offset
);
3295 current_command_length
= strlen(current_command
);
3296 current_parameter
= input
+offset
+1;
3299 /* if the identifier does not match the command sign and
3300 * the completion should not be hidden, leave this function */
3301 if (identifier
!= ':' && argument
->n
!= HIDE
) {
3302 if (current_command
) g_free(current_command
);
3303 if (input
) g_free(input
);
3304 if (tmp_string
) g_free(tmp_string
);
3308 /* static elements */
3309 static GtkBox
*results
= NULL
;
3310 static CompletionRow
*rows
= NULL
;
3312 static int current_item
= 0;
3313 static int n_items
= 0;
3315 static char *previous_command
= NULL
;
3316 static char *previous_parameter
= NULL
;
3317 static int previous_id
= 0;
3318 static int previous_length
= 0;
3320 static gboolean command_mode
= TRUE
;
3322 /* delete old list iff
3323 * the completion should be hidden
3324 * the current command differs from the previous one
3325 * the current parameter differs from the previous one
3327 if (argument
->n
== HIDE
|| previous_length
!= length
||
3328 (current_parameter
&& previous_parameter
&& strcmp(current_parameter
, previous_parameter
) != 0) ||
3329 (current_command
&& previous_command
&& strcmp(current_command
, previous_command
) != 0)) {
3330 if (results
) gtk_widget_destroy(GTK_WIDGET(results
));
3333 for (int i
= 0; i
!= n_items
; ++i
) {
3334 g_free(rows
[i
].command
);
3335 g_free(rows
[i
].description
);
3342 command_mode
= TRUE
;
3343 if (argument
->n
== HIDE
) {
3344 if (current_command
) g_free(current_command
);
3345 if (input
) g_free(input
);
3346 if (tmp_string
) g_free(tmp_string
);
3351 /* create new list iff
3352 * there is no current list
3353 * the current command differs from the previous one
3354 * the current parameter differs from the previous one
3357 results
= GTK_BOX(gtk_vbox_new(FALSE
, 0));
3358 /* create list based on parameters iff
3359 * there is a current parameter given
3360 * there is an old list with commands
3361 * the current command does not differ from the previous one
3362 * the current command has an completion function
3364 if (strchr(input
, ' ')) {
3365 gboolean search_matching_command
= FALSE
;
3366 for (int i
= 0; i
< LENGTH(commands
); ++i
) {
3367 int abbr_length
= (commands
[i
].abbr
? strlen(commands
[i
].abbr
) : 0);
3368 int cmd_length
= (commands
[i
].command
? strlen(commands
[i
].command
) : 0);
3369 if ((current_command_length
<= cmd_length
&& !strncmp(current_command
, commands
[i
].command
, current_command_length
)) ||
3370 (current_command_length
<= abbr_length
&& !strncmp(current_command
, commands
[i
].abbr
, current_command_length
))) {
3371 if (commands
[i
].completion
) {
3372 previous_command
= current_command
;
3374 search_matching_command
= TRUE
;
3376 if (current_command
) g_free(current_command
);
3377 if (input
) g_free(input
);
3378 if (tmp_string
) g_free(tmp_string
);
3384 if (!search_matching_command
) {
3385 if (current_command
) g_free(current_command
);
3386 if (input
) g_free(input
);
3387 if (tmp_string
) g_free(tmp_string
);
3391 Completion
*result
= commands
[previous_id
].completion(current_parameter
);
3393 if (!result
|| !result
->groups
) {
3394 if (current_command
) g_free(current_command
);
3395 if (input
) g_free(input
);
3396 if (tmp_string
) g_free(tmp_string
);
3400 command_mode
= FALSE
;
3401 CompletionGroup
*group
= NULL
;
3402 CompletionElement
*element
= NULL
;
3404 rows
= malloc(sizeof(CompletionRow
));
3405 if (!rows
) out_of_memory();
3407 for (group
= result
->groups
; group
!= NULL
; group
= group
->next
) {
3408 int group_elements
= 0;
3410 for(element
= group
->elements
; element
!= NULL
; element
= element
->next
)
3414 if(group
->value
&& !group_elements
)
3416 rows
= safe_realloc((void**)&rows
, n_items
+1, sizeof(CompletionRow
));
3419 rows
[n_items
].command
= g_strdup(group
->value
);
3420 rows
[n_items
].description
= NULL
;
3421 rows
[n_items
].command_id
= -1;
3422 rows
[n_items
].is_group
= TRUE
;
3423 rows
[n_items
++].row
= GTK_WIDGET(create_completion_row(results
, group
->value
, NULL
, TRUE
));
3426 rows
= safe_realloc((void**)&rows
, n_items
+1, sizeof(CompletionRow
));
3429 rows
[n_items
].command
= g_strdup(element
->value
);
3430 rows
[n_items
].description
= element
->description
? g_strdup(element
->description
) : NULL
;
3431 rows
[n_items
].command_id
= previous_id
;
3432 rows
[n_items
].is_group
= FALSE
;
3433 rows
[n_items
++].row
= GTK_WIDGET(create_completion_row(results
, element
->value
, element
->description
, FALSE
));
3440 completion_free(result
);
3442 /* create list based on commands */
3446 command_mode
= TRUE
;
3448 rows
= malloc(LENGTH(commands
)*sizeof(CompletionRow
));
3452 //printf("clen=%u\n", LENGTH(commands));
3453 for(i
= 0; i
< LENGTH(commands
); i
++)
3455 int abbr_length
= commands
[i
].abbr
? strlen(commands
[i
].abbr
) : 0;
3456 int cmd_length
= commands
[i
].command
? strlen(commands
[i
].command
) : 0;
3458 /* add command to list iff
3459 * the current command would match the command
3460 * the current command would match the abbreviation
3462 if( ((current_command_length
<= cmd_length
) && !strncmp(current_command
, commands
[i
].command
, current_command_length
)) ||
3463 ((current_command_length
<= abbr_length
) && !strncmp(current_command
, commands
[i
].abbr
, current_command_length
))
3466 rows
[n_items
].command
= g_strdup(commands
[i
].command
);
3467 rows
[n_items
].description
= g_strdup(commands
[i
].description
);
3468 rows
[n_items
].command_id
= i
;
3469 rows
[n_items
].is_group
= FALSE
;
3470 rows
[n_items
++].row
= GTK_WIDGET(create_completion_row(results
, commands
[i
].command
, commands
[i
].description
, FALSE
));
3474 //printf("n_items=%u\n", n_items);
3475 rows
= safe_realloc((void**)&rows
, (n_items
+1), sizeof(CompletionRow
));
3480 gtk_box_pack_start(Zathura
.UI
.box
, GTK_WIDGET(results
), FALSE
, FALSE
, 0);
3481 gtk_widget_show_all(GTK_WIDGET(Zathura
.UI
.window
));
3483 current_item
= (argument
->n
== NEXT
) ? -1 : 0;
3486 /* update coloring iff
3487 * there is a list with items
3489 if( (results
) && (n_items
> 0) )
3491 set_completion_row_color(results
, NORMAL
, current_item
);
3493 int i
= 0, next_group
= 0;
3495 for(i
= 0; i
< n_items
; i
++)
3497 if(argument
->n
== NEXT
|| argument
->n
== NEXT_GROUP
)
3498 current_item
= (current_item
+n_items
+1)%n_items
;
3499 else if(argument
->n
== PREVIOUS
|| argument
->n
== PREVIOUS_GROUP
)
3500 current_item
= (current_item
+n_items
-1)%n_items
;
3502 if(rows
[current_item
].is_group
)
3504 if(!command_mode
&& (argument
->n
== NEXT_GROUP
|| argument
->n
== PREVIOUS_GROUP
))
3510 if(!command_mode
&& (next_group
== 0) && (argument
->n
== NEXT_GROUP
|| argument
->n
== PREVIOUS_GROUP
))
3516 set_completion_row_color(results
, HIGHLIGHT
, current_item
);
3518 /* hide other items */
3519 int uh
= ceil(n_completion_items
/2);
3520 int lh
= floor(n_completion_items
/2);
3522 for(i
= 0; i
< n_items
; i
++)
3524 if((n_items
> 1) && (
3525 (i
>= (current_item
-lh
) && (i
<= current_item
+uh
)) ||
3526 (i
< n_completion_items
&& current_item
< lh
) ||
3527 (i
>= (n_items
-n_completion_items
) && (current_item
>= (n_items
-uh
))))
3529 gtk_widget_show(rows
[i
].row
);
3531 gtk_widget_hide(rows
[i
].row
);
3535 temp
= g_strconcat(":", rows
[current_item
].command
, (n_items
== 1) ? " " : NULL
, NULL
);
3537 temp
= g_strconcat(":", previous_command
, " ", rows
[current_item
].command
, NULL
);
3539 gtk_entry_set_text(Zathura
.UI
.inputbar
, temp
);
3540 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), -1);
3543 previous_command
= g_strdup((command_mode
) ? rows
[current_item
].command
: current_command
);
3544 previous_parameter
= g_strdup((command_mode
) ? current_parameter
: rows
[current_item
].command
);
3545 previous_length
= strlen(previous_command
)+((command_mode
) ? (length
-current_command_length
) : (strlen(previous_parameter
)+1));
3546 previous_id
= rows
[current_item
].command_id
;
3550 g_free(current_command
);
3558 //==========================================================================
3560 // isc_string_manipulation
3562 //==========================================================================
3563 void isc_string_manipulation (Argument
*argument
)
3565 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(Zathura
.UI
.inputbar
), 0, -1);
3566 int length
= strlen(input
);
3567 int pos
= gtk_editable_get_position(GTK_EDITABLE(Zathura
.UI
.inputbar
));
3570 switch (argument
->n
) {
3571 case DELETE_LAST_WORD
:
3577 /* remove trailing spaces */
3578 for(; i
>= 0 && input
[i
] == ' '; i
--);
3580 /* find the beginning of the word */
3581 while((i
> 0) && (input
[i
] != ' ') && (input
[i
] != '/'))
3584 gtk_editable_delete_text(GTK_EDITABLE(Zathura
.UI
.inputbar
), i
, pos
);
3585 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), i
);
3587 case DELETE_LAST_CHAR
:
3591 gtk_editable_delete_text(GTK_EDITABLE(Zathura
.UI
.inputbar
), pos
-1, pos
);
3593 case DELETE_TO_LINE_START
:
3594 gtk_editable_delete_text(GTK_EDITABLE(Zathura
.UI
.inputbar
), 1, pos
);
3597 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), pos
+1);
3600 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), (pos
== 0) ? 0 : pos
-1);
3602 default: /* unreachable */
3608 //**************************************************************************
3610 // command implementation
3612 //**************************************************************************
3614 //==========================================================================
3618 //==========================================================================
3619 gboolean
cmd_bookmark (int argc
, char **argv
) {
3620 if(!Zathura
.PDF
.document
|| argc
< 1)
3625 GString
*id
= g_string_new("");
3627 for(i
= 0; i
< argc
; i
++)
3630 id
= g_string_append_c(id
, ' ');
3632 id
= g_string_append(id
, argv
[i
]);
3635 if(strlen(id
->str
) == 0)
3637 notify(WARNING
, "Can't set bookmark: bookmark name is empty");
3638 g_string_free(id
, TRUE
);
3642 if(is_reserved_bm_name(id
->str
))
3644 notify(WARNING
, "Can't set bookmark: reserved bookmark name");
3645 g_string_free(id
, TRUE
);
3649 /* reload the bookmark file */
3650 read_bookmarks_file();
3652 /* check for existing bookmark to overwrite */
3653 for(i
= 0; i
< Zathura
.Bookmarks
.number_of_bookmarks
; i
++)
3655 if(!strcmp(id
->str
, Zathura
.Bookmarks
.bookmarks
[i
].id
))
3657 Zathura
.Bookmarks
.bookmarks
[i
].page
= Zathura
.PDF
.page_number
;
3658 g_string_free(id
, TRUE
);
3663 /* add new bookmark */
3664 Zathura
.Bookmarks
.bookmarks
= safe_realloc((void**)&Zathura
.Bookmarks
.bookmarks
,
3665 Zathura
.Bookmarks
.number_of_bookmarks
+1, sizeof(Bookmark
));
3666 if(!Zathura
.Bookmarks
.bookmarks
)
3669 Zathura
.Bookmarks
.bookmarks
[Zathura
.Bookmarks
.number_of_bookmarks
].id
= g_strdup(id
->str
);
3670 Zathura
.Bookmarks
.bookmarks
[Zathura
.Bookmarks
.number_of_bookmarks
].page
= Zathura
.PDF
.page_number
;
3671 Zathura
.Bookmarks
.number_of_bookmarks
++;
3673 /* write the bookmark file */
3674 write_bookmarks_file();
3676 g_string_free(id
, TRUE
);
3681 //==========================================================================
3683 // cmd_open_bookmark
3685 //==========================================================================
3686 gboolean
cmd_open_bookmark (int argc
, char **argv
)
3688 if(!Zathura
.PDF
.document
|| argc
< 1)
3693 GString
*id
= g_string_new("");
3695 for(i
= 0; i
< argc
; i
++)
3698 id
= g_string_append_c(id
, ' ');
3700 id
= g_string_append(id
, argv
[i
]);
3704 for(i
= 0; i
< Zathura
.Bookmarks
.number_of_bookmarks
; i
++)
3706 if(!strcmp(id
->str
, Zathura
.Bookmarks
.bookmarks
[i
].id
))
3708 set_page(Zathura
.Bookmarks
.bookmarks
[i
].page
);
3709 g_string_free(id
, TRUE
);
3714 notify(WARNING
, "No matching bookmark found");
3715 g_string_free(id
, TRUE
);
3720 //==========================================================================
3724 //==========================================================================
3725 gboolean
cmd_close (int argc
, char **argv
)
3733 //==========================================================================
3735 // cmd_delete_bookmark
3737 //==========================================================================
3738 gboolean
cmd_delete_bookmark (int argc
, char **argv
)
3740 if(!Zathura
.PDF
.document
|| argc
< 1)
3745 GString
*id
= g_string_new("");
3747 for(i
= 0; i
< argc
; i
++)
3750 id
= g_string_append_c(id
, ' ');
3752 id
= g_string_append(id
, argv
[i
]);
3755 /* reload bookmark file */
3756 read_bookmarks_file();
3758 /* check for bookmark to delete */
3759 for(i
= 0; i
< Zathura
.Bookmarks
.number_of_bookmarks
; i
++)
3761 if(!strcmp(id
->str
, Zathura
.Bookmarks
.bookmarks
[i
].id
))
3763 /* update key file */
3764 g_key_file_remove_key(Zathura
.Bookmarks
.data
, Zathura
.PDF
.file
, Zathura
.Bookmarks
.bookmarks
[i
].id
, NULL
);
3766 g_free(Zathura
.Bookmarks
.bookmarks
[i
].id
);
3767 /* update bookmarks */
3768 Zathura
.Bookmarks
.bookmarks
[i
].id
= Zathura
.Bookmarks
.bookmarks
[Zathura
.Bookmarks
.number_of_bookmarks
-1].id
;
3769 Zathura
.Bookmarks
.bookmarks
[i
].page
= Zathura
.Bookmarks
.bookmarks
[Zathura
.Bookmarks
.number_of_bookmarks
-1].page
;
3770 Zathura
.Bookmarks
.bookmarks
= safe_realloc((void**)&Zathura
.Bookmarks
.bookmarks
,
3771 Zathura
.Bookmarks
.number_of_bookmarks
, sizeof(Bookmark
));
3772 if(!Zathura
.Bookmarks
.bookmarks
)
3775 Zathura
.Bookmarks
.number_of_bookmarks
--;
3776 g_string_free(id
, TRUE
);
3778 /* write bookmark file */
3779 write_bookmarks_file();
3785 g_string_free(id
, TRUE
);
3790 //==========================================================================
3794 //==========================================================================
3795 gboolean
cmd_export (int argc
, char **argv
)
3797 if(argc
== 0 || !Zathura
.PDF
.document
)
3802 notify(WARNING
, "No export path specified");
3807 if(!strcmp(argv
[0], "images"))
3810 for(page_number
= 0; page_number
< Zathura
.PDF
.number_of_pages
; page_number
++)
3814 cairo_surface_t
*image
;
3817 image_list
= poppler_page_get_image_mapping(Zathura
.PDF
.pages
[page_number
]->page
);
3820 if(!g_list_length(image_list
))
3822 notify(WARNING
, "This document does not contain any images");
3826 for(images
= image_list
; images
; images
= g_list_next(images
))
3828 PopplerImageMapping
*image_mapping
;
3833 image_mapping
= (PopplerImageMapping
*) images
->data
;
3834 image_id
= image_mapping
->image_id
;
3837 image
= poppler_page_get_image(Zathura
.PDF
.pages
[page_number
]->page
, image_id
);
3843 filename
= g_strdup_printf("%s_p%i_i%i.png", Zathura
.PDF
.file
, page_number
+1, image_id
);
3845 if(argv
[1][0] == '~')
3847 gchar
* home_path
= get_home_dir();
3848 file
= g_strdup_printf("%s%s%s", home_path
, argv
[1]+1, filename
);
3852 file
= g_strdup_printf("%s%s", argv
[1], filename
);
3854 cairo_surface_write_to_png(image
, file
);
3861 else if(!strcmp(argv
[0], "attachments"))
3864 if(!poppler_document_has_attachments(Zathura
.PDF
.document
))
3866 notify(WARNING
, "PDF file has no attachments");
3871 GList
*attachment_list
= poppler_document_get_attachments(Zathura
.PDF
.document
);
3877 for(attachments
= attachment_list
; attachments
; attachments
= g_list_next(attachments
))
3879 PopplerAttachment
*attachment
= (PopplerAttachment
*) attachments
->data
;
3881 if(argv
[1][0] == '~')
3883 gchar
* home_path
= get_home_dir();
3884 file
= g_strdup_printf("%s%s%s", home_path
, argv
[1]+1, attachment
->name
);
3888 file
= g_strdup_printf("%s%s", argv
[1], attachment
->name
);
3891 poppler_attachment_save(attachment
, file
, NULL
);
3902 //==========================================================================
3906 //==========================================================================
3907 gboolean
cmd_info (int argc
, char **argv
)
3909 if(!Zathura
.PDF
.document
)
3912 static gboolean visible
= FALSE
;
3914 if(!Zathura
.UI
.information
)
3918 GtkCellRenderer
*renderer
;
3919 GtkTreeSelection
*selection
;
3921 list
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_STRING
);
3923 /* read document information */
3924 gchar
*title
, *author
;
3925 gchar
*subject
, *keywords
;
3926 gchar
*creator
, *producer
;
3927 GTime creation_date
, modification_date
;
3929 g_object_get(Zathura
.PDF
.document
,
3932 "subject", &subject
,
3933 "keywords", &keywords
,
3934 "creator", &creator
,
3935 "producer", &producer
,
3936 "creation-date", &creation_date
,
3937 "mod-date", &modification_date
,
3940 /* append information to list */
3941 gtk_list_store_append(list
, &iter
);
3942 gtk_list_store_set(list
, &iter
, 0, "Author", 1, author
? author
: "", -1);
3943 gtk_list_store_append(list
, &iter
);
3944 gtk_list_store_set(list
, &iter
, 0, "Title", 1, title
? title
: "", -1);
3945 gtk_list_store_append(list
, &iter
);
3946 gtk_list_store_set(list
, &iter
, 0, "Subject", 1, subject
? subject
: "", -1);
3947 gtk_list_store_append(list
, &iter
);
3948 gtk_list_store_set(list
, &iter
, 0, "Keywords", 1, keywords
? keywords
: "", -1);
3949 gtk_list_store_append(list
, &iter
);
3950 gtk_list_store_set(list
, &iter
, 0, "Creator", 1, creator
? creator
: "", -1);
3951 gtk_list_store_append(list
, &iter
);
3952 gtk_list_store_set(list
, &iter
, 0, "Producer", 1, producer
? producer
: "", -1);
3954 Zathura
.UI
.information
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(list
));
3955 renderer
= gtk_cell_renderer_text_new();
3957 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(Zathura
.UI
.information
), -1,
3958 "Name", renderer
, "text", 0, NULL
);
3959 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(Zathura
.UI
.information
), -1,
3960 "Value", renderer
, "text", 1, NULL
);
3962 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(Zathura
.UI
.information
));
3963 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
3965 gtk_widget_show_all(Zathura
.UI
.information
);
3969 switch_view(Zathura
.UI
.information
);
3971 switch_view(Zathura
.UI
.document
);
3979 //==========================================================================
3983 //==========================================================================
3984 gboolean
cmd_map (int argc
, char **argv
)
3991 /* search for the right shortcut function */
3995 for(sc_c
= 0; sc_c
< LENGTH(shortcut_names
); sc_c
++)
3997 if(!strcmp(argv
[1], shortcut_names
[sc_c
].name
))
4006 notify(WARNING
, "No such shortcut function exists");
4010 /* parse modifier and key */
4013 int keyl
= strlen(ks
);
4016 // single key (e.g.: g)
4020 // modifier and key (e.g.: <S-g>
4021 // special key or modifier and key/special key (e.g.: <S-g>, <Space>)
4023 else if(keyl
>= 3 && ks
[0] == '<' && ks
[keyl
-1] == '>')
4025 char* specialkey
= NULL
;
4027 /* check for modifier */
4028 if(keyl
>= 5 && ks
[2] == '-')
4030 /* evaluate modifier */
4034 mask
= GDK_SHIFT_MASK
;
4037 mask
= GDK_CONTROL_MASK
;
4041 /* no valid modifier */
4044 notify(WARNING
, "No valid modifier given.");
4048 /* modifier and special key */
4050 specialkey
= g_strndup(ks
+3, keyl
-4);
4057 /* search special key */
4059 for(g_c
= 0; specialkey
&& g_c
< LENGTH(gdk_keys
); g_c
++)
4061 if(!strcmp(specialkey
, gdk_keys
[g_c
].identifier
))
4063 key
= gdk_keys
[g_c
].key
;
4074 notify(WARNING
, "No valid key binding given.");
4078 /* parse argument */
4079 Argument arg
= {0, 0};
4085 /* compare argument with given argument names... */
4087 for(arg_c
= 0; arg_c
< LENGTH(argument_names
); arg_c
++)
4089 if(!strcmp(argv
[2], argument_names
[arg_c
].name
))
4091 arg_id
= argument_names
[arg_c
].argument
;
4096 /* if not, save it do .data */
4107 for(mode_c
= 0; mode_c
< LENGTH(mode_names
); mode_c
++)
4109 if(!strcmp(argv
[3], mode_names
[mode_c
].name
))
4111 mode
= mode_names
[mode_c
].mode
;
4117 /* search for existing binding to overwrite it */
4118 ShortcutList
* sc
= Zathura
.Bindings
.sclist
;
4119 while(sc
&& sc
->next
!= NULL
)
4121 if(sc
->element
.key
== key
&& sc
->element
.mask
== mask
4122 && sc
->element
.mode
== mode
)
4124 sc
->element
.function
= shortcut_names
[sc_id
].function
;
4125 sc
->element
.argument
= arg
;
4132 /* create new entry */
4133 ShortcutList
* entry
= malloc(sizeof(ShortcutList
));
4137 entry
->element
.mask
= mask
;
4138 entry
->element
.key
= key
;
4139 entry
->element
.function
= shortcut_names
[sc_id
].function
;
4140 entry
->element
.mode
= mode
;
4141 entry
->element
.argument
= arg
;
4144 /* append to list */
4145 if(!Zathura
.Bindings
.sclist
)
4146 Zathura
.Bindings
.sclist
= entry
;
4155 //==========================================================================
4159 //==========================================================================
4160 gboolean
cmd_open (int argc
, char **argv
)
4162 if(argc
== 0 || strlen(argv
[0]) == 0)
4165 /* assembly the arguments back to one string */
4167 GString
*filepath
= g_string_new("");
4168 for(i
= 0; i
< argc
; i
++)
4171 filepath
= g_string_append_c(filepath
, ' ');
4173 filepath
= g_string_append(filepath
, argv
[i
]);
4176 gboolean res
= open_file(filepath
->str
, NULL
);
4177 g_string_free(filepath
, TRUE
);
4182 //==========================================================================
4186 //==========================================================================
4187 gboolean
cmd_print (int argc
, char **argv
)
4189 if(!Zathura
.PDF
.document
)
4194 notify(WARNING
, "No printer specified");
4198 char* printer
= argv
[0];
4199 char* sites
= (argc
>= 2) ? g_strdup(argv
[1]) : g_strdup_printf("1-%i", Zathura
.PDF
.number_of_pages
);
4200 GString
*addit
= g_string_new("");
4203 for(i
= 2; i
< argc
; i
++)
4206 addit
= g_string_append_c(addit
, ' ');
4208 addit
= g_string_append(addit
, argv
[i
]);
4211 char* escaped_filename
= g_shell_quote(Zathura
.PDF
.file
);
4212 char* command
= g_strdup_printf(print_command
, printer
, sites
, addit
->str
, escaped_filename
);
4216 g_free(escaped_filename
);
4218 g_string_free(addit
, TRUE
);
4224 //==========================================================================
4228 //==========================================================================
4229 gboolean
cmd_rotate (int argc
, char **argv
)
4235 //==========================================================================
4239 //==========================================================================
4240 gboolean
cmd_set (int argc
, char **argv
)
4246 for(i
= 0; i
< LENGTH(settings
); i
++)
4248 if(!strcmp(argv
[0], settings
[i
].name
))
4250 /* check var type */
4251 if(settings
[i
].type
== 'b')
4253 gboolean
*x
= (gboolean
*) (settings
[i
].variable
);
4258 if(!strcmp(argv
[1], "false") || !strcmp(argv
[1], "0"))
4264 else if(settings
[i
].type
== 'i')
4269 int *x
= (int*) (settings
[i
].variable
);
4273 for(arg_c
= 0; arg_c
< LENGTH(argument_names
); arg_c
++)
4275 if(!strcmp(argv
[1], argument_names
[arg_c
].name
))
4277 id
= argument_names
[arg_c
].argument
;
4287 else if(settings
[i
].type
== 'f')
4292 float *x
= (float*) (settings
[i
].variable
);
4296 else if(settings
[i
].type
== 's')
4301 /* assembly the arguments back to one string */
4303 GString
*s
= g_string_new("");
4304 for(j
= 1; j
< argc
; j
++)
4307 s
= g_string_append_c(s
, ' ');
4309 s
= g_string_append(s
, argv
[j
]);
4312 char **x
= (char**) settings
[i
].variable
;
4315 else if(settings
[i
].type
== 'c')
4320 char *x
= (char*) (settings
[i
].variable
);
4326 if(settings
[i
].reinit
)
4330 if(settings
[i
].render
)
4332 if(!Zathura
.PDF
.document
)
4335 draw(Zathura
.PDF
.page_number
);
4345 //==========================================================================
4349 //==========================================================================
4350 gboolean
cmd_quit (int argc
, char **argv
)
4352 cb_destroy(NULL
, NULL
);
4357 //==========================================================================
4361 //==========================================================================
4362 gboolean
save_file (int argc
, char **argv
, gboolean overwrite
)
4364 if(argc
== 0 || !Zathura
.PDF
.document
)
4367 gchar
* file_path
= NULL
;
4369 if(argv
[0][0] == '~')
4371 gchar
* home_path
= get_home_dir();
4372 file_path
= g_build_filename(home_path
, argv
[0]+1, NULL
);
4376 file_path
= g_strdup(argv
[0]);
4378 if (!overwrite
&& g_file_test(file_path
, G_FILE_TEST_EXISTS
))
4380 char* message
= g_strdup_printf("File already exists: %s. Use :write! to overwrite it.", file_path
);
4381 notify(ERROR
, message
);
4388 if (file_path
[0] == '/')
4389 path
= g_strdup_printf("file://%s", file_path
);
4392 char* cur
= g_get_current_dir();
4393 path
= g_strdup_printf("file://%s/%s", cur
, file_path
);
4400 GError
* error
= NULL
;
4401 if (!poppler_document_save(Zathura
.PDF
.document
, path
, &error
))
4404 char* message
= g_strdup_printf("Can not write file: %s", error
->message
);
4405 notify(ERROR
, message
);
4407 g_error_free(error
);
4419 //==========================================================================
4423 //==========================================================================
4424 gboolean
cmd_save (int argc
, char **argv
)
4426 return save_file(argc
, argv
, FALSE
);
4430 //==========================================================================
4434 //==========================================================================
4435 gboolean
cmd_savef (int argc
, char **argv
)
4437 return save_file(argc
, argv
, TRUE
);
4441 //**************************************************************************
4443 // completion command implementation
4445 //**************************************************************************
4447 //==========================================================================
4451 //==========================================================================
4452 Completion
*cc_bookmark (char *input
) {
4453 Completion
* completion
= completion_init();
4454 CompletionGroup
* group
= completion_group_create(NULL
);
4456 completion_add_group(completion
, group
);
4459 int input_length
= input
? strlen(input
) : 0;
4461 for(i
= 0; i
< Zathura
.Bookmarks
.number_of_bookmarks
; i
++)
4463 if( (input_length
<= strlen(Zathura
.Bookmarks
.bookmarks
[i
].id
)) &&
4464 !strncmp(input
, Zathura
.Bookmarks
.bookmarks
[i
].id
, input_length
) )
4466 completion_group_add_element(group
, Zathura
.Bookmarks
.bookmarks
[i
].id
, g_strdup_printf("Page %d", Zathura
.Bookmarks
.bookmarks
[i
].page
));
4474 //==========================================================================
4478 //==========================================================================
4479 Completion
*cc_export (char *input
)
4481 Completion
* completion
= completion_init();
4482 CompletionGroup
* group
= completion_group_create(NULL
);
4484 completion_add_group(completion
, group
);
4486 completion_group_add_element(group
, "images", "Export images");
4487 completion_group_add_element(group
, "attachments", "Export attachments");
4493 //==========================================================================
4497 //==========================================================================
4498 Completion
*cc_open (char *input
)
4500 Completion
* completion
= completion_init();
4501 CompletionGroup
* group
= completion_group_create(NULL
);
4503 completion_add_group(completion
, group
);
4506 if(input
&& input
[0] == '~')
4508 gchar
* home_path
= get_home_dir();
4509 char *file
= g_strdup_printf(":open %s/%s", home_path
, input
+1);
4511 gtk_entry_set_text(Zathura
.UI
.inputbar
, file
);
4512 gtk_editable_set_position(GTK_EDITABLE(Zathura
.UI
.inputbar
), -1);
4514 completion_free(completion
);
4519 char* path
= g_strdup("/");
4520 char* file
= g_strdup("");
4521 int file_length
= 0;
4523 /* parse input string */
4524 if(input
&& strlen(input
) > 0)
4526 char* dinput
= g_strdup(input
);
4527 char* binput
= g_strdup(input
);
4528 char* path_temp
= dirname(dinput
);
4529 char* file_temp
= basename(binput
);
4530 char last_char
= input
[strlen(input
)-1];
4532 if( !strcmp(path_temp
, "/") && !strcmp(file_temp
, "/") )
4535 file
= g_strdup("");
4537 else if( !strcmp(path_temp
, "/") && strcmp(file_temp
, "/") && last_char
!= '/')
4540 file
= g_strdup(file_temp
);
4542 else if( !strcmp(path_temp
, "/") && strcmp(file_temp
, "/") && last_char
== '/')
4545 path
= g_strdup_printf("/%s/", file_temp
);
4547 else if(last_char
== '/')
4550 path
= g_strdup(input
);
4556 path
= g_strdup_printf("%s/", path_temp
);
4557 file
= g_strdup(file_temp
);
4564 file_length
= strlen(file
);
4566 /* open directory */
4567 GDir
* dir
= g_dir_open(path
, 0, NULL
);
4572 completion_free(completion
);
4576 /* create element list */
4579 while((name
= (char*) g_dir_read_name(dir
)) != NULL
)
4581 char* d_name
= g_filename_display_name(name
);
4582 int d_length
= strlen(d_name
);
4584 if( ((file_length
<= d_length
) && !strncmp(file
, d_name
, file_length
)) ||
4585 (file_length
== 0) )
4587 char* d
= g_strdup_printf("%s%s", path
, d_name
);
4588 if(g_file_test(d
, G_FILE_TEST_IS_DIR
))
4591 d
= g_strdup_printf("%s/", subdir
);
4594 completion_group_add_element(group
, d
, NULL
);
4608 //==========================================================================
4612 //==========================================================================
4613 Completion
*cc_print (char *input
)
4615 Completion
* completion
= completion_init();
4616 CompletionGroup
* group
= completion_group_create(NULL
);
4618 completion_add_group(completion
, group
);
4620 int input_length
= input
? strlen(input
) : 0;
4623 char *current_line
= NULL
, current_char
;
4627 fp
= popen(list_printer_command
, "r");
4631 completion_free(completion
);
4635 while((current_char
= fgetc(fp
)) != EOF
)
4638 current_line
= malloc(sizeof(char));
4642 current_line
= safe_realloc((void**)¤t_line
, count
+1, sizeof(char));
4646 if(current_char
!= '\n')
4647 current_line
[count
++] = current_char
;
4650 current_line
[count
] = '\0';
4651 int line_length
= strlen(current_line
);
4653 if( (input_length
<= line_length
) ||
4654 (!strncmp(input
, current_line
, input_length
)) )
4656 completion_group_add_element(group
, current_line
, NULL
);
4660 current_line
= NULL
;
4671 //==========================================================================
4675 //==========================================================================
4676 Completion
*cc_set (char *input
)
4678 Completion
* completion
= completion_init();
4679 CompletionGroup
* group
= completion_group_create(NULL
);
4681 completion_add_group(completion
, group
);
4684 int input_length
= input
? strlen(input
) : 0;
4686 for(i
= 0; i
< LENGTH(settings
); i
++)
4688 if( (input_length
<= strlen(settings
[i
].name
)) &&
4689 !strncmp(input
, settings
[i
].name
, input_length
) )
4691 completion_group_add_element(group
, settings
[i
].name
, settings
[i
].description
);
4699 //**************************************************************************
4701 // buffer command implementation
4703 //**************************************************************************
4705 //==========================================================================
4709 //==========================================================================
4710 void bcmd_goto (char* buffer
, Argument
* argument
)
4712 if(!Zathura
.PDF
.document
)
4715 int b_length
= strlen(buffer
);
4719 if(!strcmp(buffer
, "gg"))
4721 else if(!strcmp(buffer
, "G"))
4722 set_page(Zathura
.PDF
.number_of_pages
-1);
4725 char* id
= g_strndup(buffer
, b_length
-1);
4728 if(Zathura
.Global
.goto_mode
== GOTO_LABELS
)
4731 for(i
= 0; i
< Zathura
.PDF
.number_of_pages
; i
++)
4732 if(!strcmp(id
, Zathura
.PDF
.pages
[i
]->label
))
4733 pid
= Zathura
.PDF
.pages
[i
]->id
;
4743 //==========================================================================
4747 //==========================================================================
4748 gboolean
try_goto (const char *buffer
)
4750 char* endptr
= NULL
;
4751 long page_number
= strtol(buffer
, &endptr
, 10)-1;
4753 /* conversion error */
4757 /* behave like vim: <= 1 => first line, >= #lines => last line */
4758 page_number
= MAX(0, MIN(Zathura
.PDF
.number_of_pages
-1, page_number
));
4759 set_page(page_number
);
4766 //==========================================================================
4770 //==========================================================================
4772 void bcmd_scroll (char *buffer, Argument *argument)
4774 int b_length = strlen(buffer);
4778 int percentage = atoi(g_strndup(buffer, b_length-1));
4779 percentage = (percentage < 0) ? 0 : ((percentage > 100) ? 100 : percentage);
4781 GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
4783 gdouble view_size = gtk_adjustment_get_page_size(adjustment);
4784 gdouble max = gtk_adjustment_get_upper(adjustment)-view_size;
4785 gdouble nvalue = (percentage*max)/100;
4790 Zathura.State.scroll_percentage = percentage;
4791 gtk_adjustment_set_value(adjustment, nvalue);
4797 //==========================================================================
4801 //==========================================================================
4802 void bcmd_zoom (char *buffer
, Argument
*argument
)
4804 Zathura
.Global
.adjust_mode
= ADJUST_NONE
;
4806 if(argument
->n
== ZOOM_IN
)
4808 if((Zathura
.PDF
.scale
+zoom_step
) <= zoom_max
)
4809 Zathura
.PDF
.scale
+= zoom_step
;
4811 Zathura
.PDF
.scale
= zoom_max
;
4813 else if(argument
->n
== ZOOM_OUT
)
4815 if((Zathura
.PDF
.scale
-zoom_step
) >= zoom_min
)
4816 Zathura
.PDF
.scale
-= zoom_step
;
4818 Zathura
.PDF
.scale
= zoom_min
;
4820 else if(argument
->n
== ZOOM_SPECIFIC
)
4822 int b_length
= strlen(buffer
);
4826 int value
= atoi(g_strndup(buffer
, b_length
-1));
4827 if(value
<= zoom_min
)
4828 Zathura
.PDF
.scale
= zoom_min
;
4829 else if(value
>= zoom_max
)
4830 Zathura
.PDF
.scale
= zoom_max
;
4832 Zathura
.PDF
.scale
= value
;
4835 Zathura
.PDF
.scale
= 100;
4837 Zathura
.Search
.draw
= TRUE
;
4838 draw(Zathura
.PDF
.page_number
);
4843 //**************************************************************************
4845 // special command implementation
4847 //**************************************************************************
4849 //==========================================================================
4853 //==========================================================================
4854 gboolean
scmd_search (gchar
*input
, Argument
*argument
)
4856 if(!input
|| !strlen(input
))
4859 argument
->data
= input
;
4860 sc_search(argument
);
4866 //**************************************************************************
4868 // callback implementation
4870 //**************************************************************************
4872 //==========================================================================
4876 //==========================================================================
4877 gboolean
cb_destroy (GtkWidget
*widget
, gpointer data
) {
4878 pango_font_description_free(Zathura
.Style
.font
);
4880 if(Zathura
.PDF
.document
)
4883 /* clean up bookmarks */
4884 g_free(Zathura
.Bookmarks
.file
);
4885 if (Zathura
.Bookmarks
.data
)
4886 g_key_file_free(Zathura
.Bookmarks
.data
);
4888 /* destroy mutexes */
4889 g_static_mutex_free(&(Zathura
.Lock
.pdflib_lock
));
4890 g_static_mutex_free(&(Zathura
.Lock
.search_lock
));
4891 g_static_mutex_free(&(Zathura
.Lock
.pdf_obj_lock
));
4892 g_static_mutex_free(&(Zathura
.Lock
.select_lock
));
4895 if(Zathura
.FileMonitor
.monitor
)
4896 g_object_unref(Zathura
.FileMonitor
.monitor
);
4897 if(Zathura
.FileMonitor
.file
)
4898 g_object_unref(Zathura
.FileMonitor
.file
);
4900 g_list_free(Zathura
.Global
.history
);
4902 /* clean shortcut list */
4903 ShortcutList
* sc
= Zathura
.Bindings
.sclist
;
4907 ShortcutList
* ne
= sc
->next
;
4912 g_free(Zathura
.State
.filename
);
4913 g_free(Zathura
.State
.pages
);
4915 g_free(Zathura
.Config
.config_dir
);
4916 g_free(Zathura
.Config
.data_dir
);
4917 if (Zathura
.StdinSupport
.file
)
4918 g_unlink(Zathura
.StdinSupport
.file
);
4919 g_free(Zathura
.StdinSupport
.file
);
4927 //==========================================================================
4931 // if we don't want a full clear, we'll only clear sides and gaps
4933 //==========================================================================
4934 void render_view (GtkWidget
*widget
, gboolean fullclear
) {
4935 if (!widget
|| !widget
->window
) return;
4937 if (!Zathura
.PDF
.document
) {
4938 gdk_window_clear(widget
->window
);
4942 int page_id
= Zathura
.PDF
.page_number
;
4943 if (page_id
< 0 || page_id
> Zathura
.PDF
.number_of_pages
) {
4944 gdk_window_clear(widget
->window
);
4948 if (fullclear
) gdk_window_clear(widget
->window
);
4950 int window_x
, window_y
;
4951 gdk_drawable_get_size(widget
->window
, &window_x
, &window_y
);
4953 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
4955 int yofs
= get_current_page_screen_ytop();
4957 while (yofs
< window_y
&& page_id
< Zathura
.PDF
.number_of_pages
) {
4958 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
4959 const int width
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->width
: current_page
->height
)*scale
;
4960 const int height
= (Zathura
.PDF
.rotate
== 0 || Zathura
.PDF
.rotate
== 180 ? current_page
->height
: current_page
->width
)*scale
;
4964 if (window_x
> width
) {
4965 switch (Zathura
.Global
.xcenter_mode
) {
4966 case CENTER_LEFT_TOP
: offset_x
= 0; break;
4967 case CENTER_RIGHT_BOTTOM
: offset_x
= window_x
-width
; break;
4968 default: offset_x
= (window_x
-width
)/2; break;
4974 if (Zathura
.Search
.draw
) {
4976 for (list
= Zathura
.Search
.results
; list
&& list
->data
; list
= g_list_next(list
)) {
4977 highlight_result(Zathura
.Search
.page
, (PopplerRectangle
*)list
->data
);
4979 Zathura
.Search
.draw
= FALSE
;
4982 cairo_t
*cairo
= gdk_cairo_create(widget
->window
);
4983 cairo_surface_t
*surf
= create_page_surface(page_id
);
4984 cairo_set_source_surface(cairo
, surf
, offset_x
, yofs
);
4986 cairo_destroy(cairo
);
4988 // clear sides and gap
4991 if (offset_x
> 0) gdk_window_clear_area(widget
->window
, 0, yofs
, offset_x
, height
);
4993 if (offset_x
+width
< window_x
) gdk_window_clear_area(widget
->window
, offset_x
+width
, yofs
, window_x
-(offset_x
+width
), height
);
4995 if (Zathura
.PDF
.page_gap
> 0) gdk_window_clear_area(widget
->window
, 0, yofs
+height
, window_x
, Zathura
.PDF
.page_gap
);
4998 yofs
+= height
+Zathura
.PDF
.page_gap
;
5004 //==========================================================================
5008 //==========================================================================
5009 gboolean
cb_draw (GtkWidget
*widget
, GdkEventExpose
*expose
, gpointer data
) {
5010 render_view(widget
, FALSE
);
5015 //==========================================================================
5017 // cb_inputbar_kb_pressed
5019 //==========================================================================
5020 gboolean
cb_inputbar_kb_pressed (GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
5024 /* inputbar shortcuts */
5025 for(i
= 0; i
< LENGTH(inputbar_shortcuts
); i
++)
5027 if(event
->keyval
== inputbar_shortcuts
[i
].key
&&
5028 (((event
->state
&inputbar_shortcuts
[i
].mask
) == inputbar_shortcuts
[i
].mask
)
5029 || inputbar_shortcuts
[i
].mask
== 0))
5031 inputbar_shortcuts
[i
].function(&(inputbar_shortcuts
[i
].argument
));
5036 /* special commands */
5037 char* identifier_string
= gtk_editable_get_chars(GTK_EDITABLE(Zathura
.UI
.inputbar
), 0, 1);
5038 char identifier
= identifier_string
[0];
5040 for(i
= 0; i
< LENGTH(special_commands
); i
++)
5042 if((identifier
== special_commands
[i
].identifier
) &&
5043 (special_commands
[i
].always
== 1))
5045 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(Zathura
.UI
.inputbar
), 1, -1);
5046 guint new_utf_char
= gdk_keyval_to_unicode(event
->keyval
);
5048 if(new_utf_char
!= 0)
5050 gchar
* newchar
= g_malloc0(6*sizeof(gchar
));
5057 gint len
= g_unichar_to_utf8(new_utf_char
, newchar
);
5059 gchar
* tmp
= g_strconcat(input
, newchar
, NULL
);
5068 if((special_commands
[i
].function
== scmd_search
) && (event
->keyval
== GDK_Return
))
5070 Argument argument
= { NO_SEARCH
, NULL
};
5071 scmd_search(input
, &argument
);
5075 special_commands
[i
].function(input
, &(special_commands
[i
].argument
));
5078 g_free(identifier_string
);
5084 g_free(identifier_string
);
5090 //==========================================================================
5092 // cb_inputbar_activate
5094 //==========================================================================
5095 gboolean
cb_inputbar_activate (GtkEntry
*entry
, gpointer data
)
5097 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 1, -1);
5098 gchar
**tokens
= g_strsplit(input
, " ", -1);
5101 gchar
*command
= tokens
[0];
5102 int length
= g_strv_length(tokens
);
5104 gboolean retv
= FALSE
;
5105 gboolean succ
= FALSE
;
5115 /* append input to the command history */
5116 Zathura
.Global
.history
= g_list_append(Zathura
.Global
.history
, g_strdup(gtk_entry_get_text(entry
)));
5118 /* special commands */
5119 char identifier
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 0, 1)[0];
5120 for(i
= 0; i
< LENGTH(special_commands
); i
++)
5122 if(identifier
== special_commands
[i
].identifier
)
5124 /* special commands that are evaluated every key change are not
5126 if(special_commands
[i
].always
== 1)
5133 retv
= special_commands
[i
].function(input
, &(special_commands
[i
].argument
));
5134 if(retv
) isc_abort(NULL
);
5135 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.view
));
5141 /* search commands */
5142 for(i
= 0; i
< LENGTH(commands
); i
++)
5144 if((g_strcmp0(command
, commands
[i
].command
) == 0) ||
5145 (g_strcmp0(command
, commands
[i
].abbr
) == 0))
5147 retv
= commands
[i
].function(length
-1, tokens
+1);
5157 /* it maybe a goto command */
5158 if(!try_goto(command
))
5159 notify(ERROR
, "Unknown command.");
5162 Argument arg
= { HIDE
};
5163 isc_completion(&arg
);
5164 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.view
));
5171 //==========================================================================
5173 // cb_inputbar_form_activate
5175 //==========================================================================
5176 gboolean
cb_inputbar_form_activate (GtkEntry
*entry
, gpointer data
)
5178 if(!Zathura
.PDF
.document
)
5181 Page
* current_page
= Zathura
.PDF
.pages
[Zathura
.PDF
.page_number
];
5182 int number_of_links
= 0, link_id
= 1, new_page_id
= Zathura
.PDF
.page_number
;
5185 GList
*link_list
= poppler_page_get_link_mapping(current_page
->page
);
5187 link_list
= g_list_reverse(link_list
);
5189 if((number_of_links
= g_list_length(link_list
)) <= 0)
5193 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 1, -1);
5194 gchar
*token
= input
+strlen("Follow hint: ")-1;
5198 int li
= atoi(token
);
5199 if(li
<= 0 || li
> number_of_links
)
5201 set_page(Zathura
.PDF
.page_number
);
5203 notify(WARNING
, "Invalid hint");
5209 for(links
= link_list
; links
; links
= g_list_next(links
))
5211 PopplerLinkMapping
*link_mapping
= (PopplerLinkMapping
*) links
->data
;
5212 PopplerAction
*action
= link_mapping
->action
;
5214 /* only handle URI and internal links */
5215 if(action
->type
== POPPLER_ACTION_URI
)
5218 open_uri(action
->uri
.uri
);
5220 else if(action
->type
== POPPLER_ACTION_GOTO_DEST
)
5224 if(action
->goto_dest
.dest
->type
== POPPLER_DEST_NAMED
)
5226 PopplerDest
* destination
= poppler_document_find_dest(Zathura
.PDF
.document
, action
->goto_dest
.dest
->named_dest
);
5229 new_page_id
= destination
->page_num
-1;
5230 poppler_dest_free(destination
);
5234 new_page_id
= action
->goto_dest
.dest
->page_num
-1;
5243 poppler_page_free_link_mapping(link_list
);
5246 set_page(new_page_id
);
5253 //==========================================================================
5255 // cb_inputbar_password_activate
5257 //==========================================================================
5258 gboolean
cb_inputbar_password_activate (GtkEntry
*entry
, gpointer data
)
5260 gchar
*input
= gtk_editable_get_chars(GTK_EDITABLE(entry
), 1, -1);
5261 gchar
*token
= input
+strlen("Enter password: ")-1;
5265 if(!open_file(Zathura
.PDF
.file
, token
))
5271 /* replace default inputbar handler */
5272 g_signal_handler_disconnect((gpointer
) Zathura
.UI
.inputbar
, Zathura
.Handler
.inputbar_activate
);
5273 Zathura
.Handler
.inputbar_activate
= g_signal_connect(G_OBJECT(Zathura
.UI
.inputbar
), "activate", G_CALLBACK(cb_inputbar_activate
), NULL
);
5281 //==========================================================================
5283 // cb_view_kb_pressed
5285 //==========================================================================
5286 gboolean
cb_view_kb_pressed (GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
5288 //fprintf(stderr, "KEY=0x%04X\n", event->keyval);
5289 ShortcutList
* sc
= Zathura
.Bindings
.sclist
;
5293 event
->keyval
== sc
->element
.key
5294 && (CLEAN(event
->state
) == sc
->element
.mask
|| (sc
->element
.key
>= 0x21
5295 && sc
->element
.key
<= 0x7E && CLEAN(event
->state
) == GDK_SHIFT_MASK
))
5296 && (Zathura
.Global
.mode
&sc
->element
.mode
|| sc
->element
.mode
== ALL
)
5297 && sc
->element
.function
5300 if(!(Zathura
.Global
.buffer
&& strlen(Zathura
.Global
.buffer
->str
)) || (sc
->element
.mask
== GDK_CONTROL_MASK
) ||
5301 (sc
->element
.key
<= 0x21 || sc
->element
.key
>= 0x7E)
5304 sc
->element
.function(&(sc
->element
.argument
));
5312 if(Zathura
.Global
.mode
== ADD_MARKER
)
5314 add_marker(event
->keyval
);
5315 change_mode(NORMAL
);
5318 else if(Zathura
.Global
.mode
== EVAL_MARKER
)
5320 eval_marker(event
->keyval
);
5321 change_mode(NORMAL
);
5325 /* append only numbers and characters to buffer */
5326 if( (event
->keyval
>= 0x21) && (event
->keyval
<= 0x7E))
5328 if(!Zathura
.Global
.buffer
)
5329 Zathura
.Global
.buffer
= g_string_new("");
5331 Zathura
.Global
.buffer
= g_string_append_c(Zathura
.Global
.buffer
, event
->keyval
);
5332 gtk_label_set_markup((GtkLabel
*) Zathura
.Global
.status_buffer
, Zathura
.Global
.buffer
->str
);
5335 /* search buffer commands */
5336 if(Zathura
.Global
.buffer
)
5339 for(i
= 0; i
< LENGTH(buffer_commands
); i
++)
5344 regcomp(®ex
, buffer_commands
[i
].regex
, REG_EXTENDED
);
5345 status
= regexec(®ex
, Zathura
.Global
.buffer
->str
, (size_t) 0, NULL
, 0);
5350 buffer_commands
[i
].function(Zathura
.Global
.buffer
->str
, &(buffer_commands
[i
].argument
));
5351 g_string_free(Zathura
.Global
.buffer
, TRUE
);
5352 Zathura
.Global
.buffer
= NULL
;
5353 gtk_label_set_markup((GtkLabel
*) Zathura
.Global
.status_buffer
, "");
5364 //==========================================================================
5368 //==========================================================================
5369 gboolean
cb_view_resized (GtkWidget
*widget
, GtkAllocation
*allocation
, gpointer data
)
5372 arg
.n
= Zathura
.Global
.adjust_mode
;
5373 sc_adjust_window(&arg
);
5379 //==========================================================================
5381 // cb_view_button_pressed
5383 //==========================================================================
5384 gboolean
cb_view_button_pressed (GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
5386 if(!Zathura
.PDF
.document
)
5390 draw(Zathura
.PDF
.page_number
);
5392 Zathura
.SelectPoint
.x
= event
->x
;
5393 Zathura
.SelectPoint
.y
= event
->y
;
5400 //==========================================================================
5402 // cb_view_button_release
5404 //==========================================================================
5405 gboolean
cb_view_button_release (GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
) {
5406 if (!Zathura
.PDF
.document
) return FALSE
;
5408 double offset_x
= 0, offset_y
= 0;
5410 PopplerRectangle rectangle
;
5412 /* build selection rectangle */
5413 rectangle
.x1
= event
->x
;
5414 rectangle
.y1
= event
->y
;
5417 rectangle
.x2
= Zathura
.SelectPoint
.x
;
5418 rectangle
.y2
= Zathura
.SelectPoint
.y
;
5421 /* calculate offset */
5422 calculate_screen_offset(widget
, event
->x
, event
->y
, &offset_x
, &offset_y
, &page_id
);
5423 if (page_id
< 0) return FALSE
; // not on any page
5425 /* draw selection rectangle */
5426 /* first, evict page surface, because we need it recreated; this is a hack */
5427 surfCacheGet(page_id
, -666.666); // specifying invalid scale will destroy the surface, if there was any
5428 cairo_t
*cairo
= cairo_create(create_page_surface(page_id
));
5429 cairo_set_source_rgba(cairo
, Zathura
.Style
.select_text
.red
, Zathura
.Style
.select_text
.green
, Zathura
.Style
.select_text
.blue
, transparency
);
5430 cairo_rectangle(cairo
, rectangle
.x1
-offset_x
, rectangle
.y1
-offset_y
, (rectangle
.x2
-rectangle
.x1
), (rectangle
.y2
-rectangle
.y1
));
5432 cairo_destroy(cairo
);
5433 gtk_widget_queue_draw(Zathura
.UI
.drawing_area
);
5435 /* resize selection rectangle to document page */
5436 Page
*current_page
= Zathura
.PDF
.pages
[page_id
];
5437 const double page_width
= current_page
->width
;
5438 const double page_height
= current_page
->height
;
5439 const double scale
= ((double)Zathura
.PDF
.scale
/100.0);
5441 rectangle
.x1
= (rectangle
.x1
-offset_x
)/scale
;
5442 rectangle
.y1
= (rectangle
.y1
-offset_y
)/scale
;
5443 rectangle
.x2
= (rectangle
.x2
-offset_x
)/scale
;
5444 rectangle
.y2
= (rectangle
.y2
-offset_y
)/scale
;
5447 int rotate
= Zathura
.PDF
.rotate
;
5448 double x1
= rectangle
.x1
;
5449 double x2
= rectangle
.x2
;
5450 double y1
= rectangle
.y1
;
5451 double y2
= rectangle
.y2
;
5456 rectangle
.y1
= page_height
-x2
;
5458 rectangle
.y2
= page_height
-x1
;
5461 rectangle
.x1
= (page_height
-y1
);
5462 rectangle
.y1
= (page_width
-x2
);
5463 rectangle
.x2
= (page_height
-y2
);
5464 rectangle
.y2
= (page_width
-x1
);
5467 rectangle
.x1
= page_width
-y2
;
5469 rectangle
.x2
= page_width
-y1
;
5474 /* reset points of the rectangle so that p1 is in the top-left corner
5475 * and p2 is in the bottom right corner */
5476 if (rectangle
.x1
> rectangle
.x2
) {
5477 const double d
= rectangle
.x1
;
5478 rectangle
.x1
= rectangle
.x2
;
5481 if (rectangle
.y2
> rectangle
.y1
) {
5482 const double d
= rectangle
.y1
;
5483 rectangle
.y1
= rectangle
.y2
;
5487 #if !POPPLER_CHECK_VERSION(0,15,0)
5488 /* adapt y coordinates */
5489 rectangle
.y1
= page_height
-rectangle
.y1
;
5490 rectangle
.y2
= page_height
-rectangle
.y2
;
5493 /* get selected text */
5495 char *selected_text
= poppler_page_get_selected_text(Zathura
.PDF
.pages
[page_id
]->page
, SELECTION_STYLE
, &rectangle
);
5496 if (selected_text
) {
5497 gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY
), selected_text
, -1);
5498 g_free(selected_text
);
5506 //==========================================================================
5508 // cb_view_motion_notify
5510 //==========================================================================
5511 gboolean
cb_view_motion_notify (GtkWidget
*widget
, GdkEventMotion
*event
, gpointer data
)
5517 //==========================================================================
5521 //==========================================================================
5522 gboolean
cb_view_scrolled (GtkWidget
*widget
, GdkEventScroll
*event
, gpointer data
)
5525 for(i
= 0; i
< LENGTH(mouse_scroll_events
); i
++)
5527 if(event
->direction
== mouse_scroll_events
[i
].direction
)
5529 mouse_scroll_events
[i
].function(&(mouse_scroll_events
[i
].argument
));
5538 //==========================================================================
5542 //==========================================================================
5543 gboolean
cb_watch_file (GFileMonitor
*monitor
, GFile
*file
, GFile
*other_file
, GFileMonitorEvent event
, gpointer data
) {
5544 if(event
!= G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
)
5554 //**************************************************************************
5558 //**************************************************************************
5560 int main (int argc
, char *argv
[]) {
5562 Zathura
.UI
.embed
= 0;
5564 Zathura
.Config
.config_dir
= 0;
5565 Zathura
.Config
.data_dir
= 0;
5567 char* config_dir
= 0;
5569 GOptionEntry entries
[] =
5571 { "reparent", 'e', 0, G_OPTION_ARG_INT
, &Zathura
.UI
.embed
, "Reparents to window specified by xid", "xid" },
5572 { "config-dir", 'c', 0, G_OPTION_ARG_FILENAME
, &config_dir
, "Path to the config directory", "path" },
5573 { "data-dir", 'd', 0, G_OPTION_ARG_FILENAME
, &data_dir
, "Path to the data directory", "path" },
5577 GOptionContext
* context
= g_option_context_new(" [file] [password]");
5578 g_option_context_add_main_entries(context
, entries
, NULL
);
5580 GError
* error
= NULL
;
5581 if(!g_option_context_parse(context
, &argc
, &argv
, &error
))
5583 printf("Error parsing command line arguments: %s\n", error
->message
);
5584 g_option_context_free(context
);
5585 g_error_free(error
);
5588 g_option_context_free(context
);
5591 Zathura
.Config
.config_dir
= g_strdup(config_dir
);
5593 Zathura
.Config
.data_dir
= g_strdup(data_dir
);
5595 g_thread_init(NULL
);
5598 gtk_init(&argc
, &argv
);
5603 read_configuration();
5610 char* password
= (argc
== 3) ? argv
[2] : NULL
;
5611 if (strcmp(argv
[1], "-") == 0)
5612 open_stdin(password
);
5614 open_file(argv
[1], password
);
5617 switch_view(Zathura
.UI
.document
);
5620 gtk_widget_show_all(GTK_WIDGET(Zathura
.UI
.window
));
5621 gtk_widget_grab_focus(GTK_WIDGET(Zathura
.UI
.view
));
5623 if(!Zathura
.Global
.show_inputbar
)
5624 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.inputbar
));
5626 if(!Zathura
.Global
.show_statusbar
)
5627 gtk_widget_hide(GTK_WIDGET(Zathura
.UI
.statusbar
));
5629 gdk_threads_enter();
5631 gdk_threads_leave();