mirror of
https://git.pwmt.org/pwmt/zathura.git
synced 2024-11-10 23:53:53 +01:00
2199 lines
61 KiB
C
2199 lines
61 KiB
C
/* See LICENSE file for license and copyright information */
|
|
|
|
#define _BSD_SOURCE || _XOPEN_SOURCE >= 500
|
|
|
|
#include <regex.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <libgen.h>
|
|
#include <pthread.h>
|
|
|
|
#include <poppler/glib/poppler.h>
|
|
#include <cairo.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
/* macros */
|
|
#define LENGTH(x) sizeof(x)/sizeof((x)[0])
|
|
|
|
/* enums */
|
|
enum { NEXT, PREVIOUS, LEFT, RIGHT, UP, DOWN,
|
|
BOTTOM, TOP, HIDE, NORMAL, HIGHLIGHT,
|
|
INSERT, VISUAL, DELETE_LAST_WORD, DEFAULT,
|
|
ERROR, WARNING, NEXT_GROUP, PREVIOUS_GROUP,
|
|
ZOOM_IN, ZOOM_OUT, ZOOM_ORIGINAL, FORWARD,
|
|
BACKWARD, ADJUST_BESTFIT, ADJUST_WIDTH,
|
|
CONTINUOUS };
|
|
|
|
/* typedefs */
|
|
struct CElement
|
|
{
|
|
char *value;
|
|
char *description;
|
|
struct CElement *next;
|
|
};
|
|
|
|
typedef struct CElement CompletionElement;
|
|
|
|
struct CGroup
|
|
{
|
|
char *value;
|
|
CompletionElement *elements;
|
|
struct CGroup *next;
|
|
};
|
|
|
|
typedef struct CGroup CompletionGroup;
|
|
|
|
typedef struct
|
|
{
|
|
CompletionGroup* groups;
|
|
} Completion;
|
|
|
|
typedef struct
|
|
{
|
|
char* command;
|
|
char* description;
|
|
int command_id;
|
|
gboolean is_group;
|
|
GtkWidget* row;
|
|
} CompletionRow;
|
|
|
|
typedef struct
|
|
{
|
|
int n;
|
|
void *data;
|
|
} Argument;
|
|
|
|
typedef struct
|
|
{
|
|
int mask;
|
|
int key;
|
|
void (*function)(Argument*);
|
|
int mode;
|
|
Argument argument;
|
|
} Shortcut;
|
|
|
|
typedef struct
|
|
{
|
|
int mask;
|
|
int key;
|
|
void (*function)(Argument*);
|
|
Argument argument;
|
|
} InputbarShortcut;
|
|
|
|
typedef struct
|
|
{
|
|
char* command;
|
|
char* abbr;
|
|
gboolean (*function)(int, char**);
|
|
Completion* (*completion)(char*);
|
|
char* description;
|
|
} Command;
|
|
|
|
typedef struct
|
|
{
|
|
char* regex;
|
|
void (*function)(char*, Argument*);
|
|
Argument argument;
|
|
} BufferCommand;
|
|
|
|
typedef struct
|
|
{
|
|
char identifier;
|
|
gboolean (*function)(char*, Argument*);
|
|
int always;
|
|
Argument argument;
|
|
} SpecialCommand;
|
|
|
|
typedef struct
|
|
{
|
|
PopplerPage *page;
|
|
cairo_surface_t *surface;
|
|
GtkWidget *drawing_area;
|
|
pthread_mutex_t lock;
|
|
} Page;
|
|
|
|
typedef struct
|
|
{
|
|
char* name;
|
|
void* variable;
|
|
char type;
|
|
gboolean render;
|
|
char* description;
|
|
} Setting;
|
|
|
|
/* zathura */
|
|
struct
|
|
{
|
|
struct
|
|
{
|
|
GtkWindow *window;
|
|
GtkBox *box;
|
|
GtkBox *continuous;
|
|
GtkScrolledWindow *view;
|
|
GtkViewport *viewport;
|
|
GtkWidget *statusbar;
|
|
GtkBox *statusbar_entries;
|
|
GtkEntry *inputbar;
|
|
GtkWidget *index;
|
|
GtkWidget *information;
|
|
} UI;
|
|
|
|
struct
|
|
{
|
|
GdkColor default_fg;
|
|
GdkColor default_bg;
|
|
GdkColor inputbar_fg;
|
|
GdkColor inputbar_bg;
|
|
GdkColor statusbar_fg;
|
|
GdkColor statusbar_bg;
|
|
GdkColor completion_fg;
|
|
GdkColor completion_bg;
|
|
GdkColor completion_g_bg;
|
|
GdkColor completion_g_fg;
|
|
GdkColor completion_hl_fg;
|
|
GdkColor completion_hl_bg;
|
|
GdkColor notification_e_fg;
|
|
GdkColor notification_e_bg;
|
|
GdkColor notification_w_fg;
|
|
GdkColor notification_w_bg;
|
|
GdkColor search_highlight;
|
|
PangoFontDescription *font;
|
|
} Style;
|
|
|
|
struct
|
|
{
|
|
GString *buffer;
|
|
GList *history;
|
|
int mode;
|
|
int viewing_mode;
|
|
gboolean reverse_video;
|
|
GtkLabel *status_text;
|
|
GtkLabel *status_buffer;
|
|
GtkLabel *status_state;
|
|
} Global;
|
|
|
|
struct
|
|
{
|
|
pthread_mutex_t scale_lock;
|
|
pthread_mutex_t rotate_lock;
|
|
pthread_mutex_t render_lock;
|
|
pthread_mutex_t search_lock;
|
|
pthread_mutex_t viewport_lock;
|
|
} Lock;
|
|
|
|
struct
|
|
{
|
|
char* filename;
|
|
char* pages;
|
|
} State;
|
|
|
|
struct
|
|
{
|
|
PopplerDocument *document;
|
|
char *file;
|
|
Page **pages;
|
|
int page_number;
|
|
int number_of_pages;
|
|
int scale;
|
|
int rotate;
|
|
pthread_t render_thread;
|
|
pthread_t search_thread;
|
|
} PDF;
|
|
|
|
} Zathura;
|
|
|
|
/* function declarations */
|
|
void init_zathura();
|
|
void build_index(GtkTreeModel*, GtkTreeIter*, PopplerIndexIter*);
|
|
void change_mode(int);
|
|
void highlight_result(int, PopplerRectangle*);
|
|
void draw(int);
|
|
void notify(int, char*);
|
|
void update_status();
|
|
void recalcRectangle(int, PopplerRectangle*);
|
|
void setCompletionRowColor(GtkBox*, int, int);
|
|
void set_page(int);
|
|
void switch_view(GtkWidget*);
|
|
GtkEventBox* createCompletionRow(GtkBox*, char*, char*, gboolean);
|
|
|
|
/* thread declaration */
|
|
void* render(void*);
|
|
void* search(void*);
|
|
|
|
/* shortcut declarations */
|
|
void sc_abort(Argument*);
|
|
void sc_adjust_window(Argument*);
|
|
void sc_change_mode(Argument*);
|
|
void sc_focus_inputbar(Argument*);
|
|
void sc_navigate(Argument*);
|
|
void sc_revert_video(Argument*);
|
|
void sc_rotate(Argument*);
|
|
void sc_scroll(Argument*);
|
|
void sc_search(Argument*);
|
|
void sc_toggle_index(Argument*);
|
|
void sc_toggle_inputbar(Argument*);
|
|
void sc_toggle_statusbar(Argument*);
|
|
void sc_quit(Argument*);
|
|
|
|
/* inputbar shortcut declarations */
|
|
void isc_abort(Argument*);
|
|
void isc_command_history(Argument*);
|
|
void isc_completion(Argument*);
|
|
void isc_string_manipulation(Argument*);
|
|
|
|
/* command declarations */
|
|
gboolean cmd_close(int, char**);
|
|
gboolean cmd_info(int, char**);
|
|
gboolean cmd_open(int, char**);
|
|
gboolean cmd_print(int, char**);
|
|
gboolean cmd_rotate(int, char**);
|
|
gboolean cmd_set(int, char**);
|
|
gboolean cmd_quit(int, char**);
|
|
gboolean cmd_save(int, char**);
|
|
|
|
/* completion commands */
|
|
Completion* cc_open(char*);
|
|
Completion* cc_print(char*);
|
|
Completion* cc_set(char*);
|
|
|
|
/* buffer command declarations */
|
|
void bcmd_goto(char*, Argument*);
|
|
void bcmd_zoom(char*, Argument*);
|
|
|
|
/* special command delcarations */
|
|
gboolean scmd_search(char*, Argument*);
|
|
|
|
/* callback declarations */
|
|
gboolean cb_destroy(GtkWidget*, gpointer);
|
|
gboolean cb_draw(GtkWidget*, GdkEventExpose*, gpointer);
|
|
gboolean cb_view_kb_pressed(GtkWidget*, GdkEventKey*, gpointer);
|
|
gboolean cb_index_selection_changed(GtkTreeSelection*, GtkWidget*);
|
|
gboolean cb_inputbar_kb_pressed(GtkWidget*, GdkEventKey*, gpointer);
|
|
gboolean cb_inputbar_activate(GtkEntry*, gpointer);
|
|
|
|
/* configuration */
|
|
#include "config.h"
|
|
|
|
/* function implementation */
|
|
void
|
|
init_zathura()
|
|
{
|
|
/* mutexes */
|
|
pthread_mutex_init(&(Zathura.Lock.scale_lock), NULL);
|
|
pthread_mutex_init(&(Zathura.Lock.rotate_lock), NULL);
|
|
pthread_mutex_init(&(Zathura.Lock.render_lock), NULL);
|
|
pthread_mutex_init(&(Zathura.Lock.search_lock), NULL);
|
|
pthread_mutex_init(&(Zathura.Lock.viewport_lock), NULL);
|
|
|
|
/* look */
|
|
gdk_color_parse(default_fgcolor, &(Zathura.Style.default_fg));
|
|
gdk_color_parse(default_bgcolor, &(Zathura.Style.default_bg));
|
|
gdk_color_parse(inputbar_fgcolor, &(Zathura.Style.inputbar_fg));
|
|
gdk_color_parse(inputbar_bgcolor, &(Zathura.Style.inputbar_bg));
|
|
gdk_color_parse(statusbar_fgcolor, &(Zathura.Style.statusbar_fg));
|
|
gdk_color_parse(statusbar_bgcolor, &(Zathura.Style.statusbar_bg));
|
|
gdk_color_parse(completion_fgcolor, &(Zathura.Style.completion_fg));
|
|
gdk_color_parse(completion_bgcolor, &(Zathura.Style.completion_bg));
|
|
gdk_color_parse(completion_g_fgcolor, &(Zathura.Style.completion_g_fg));
|
|
gdk_color_parse(completion_g_fgcolor, &(Zathura.Style.completion_g_fg));
|
|
gdk_color_parse(completion_hl_fgcolor, &(Zathura.Style.completion_hl_fg));
|
|
gdk_color_parse(completion_hl_bgcolor, &(Zathura.Style.completion_hl_bg));
|
|
gdk_color_parse(notification_e_fgcolor, &(Zathura.Style.notification_e_fg));
|
|
gdk_color_parse(notification_e_bgcolor, &(Zathura.Style.notification_e_bg));
|
|
gdk_color_parse(notification_w_fgcolor, &(Zathura.Style.notification_w_fg));
|
|
gdk_color_parse(notification_w_bgcolor, &(Zathura.Style.notification_w_bg));
|
|
gdk_color_parse(search_highlight, &(Zathura.Style.search_highlight));
|
|
Zathura.Style.font = pango_font_description_from_string(font);
|
|
|
|
/* other */
|
|
Zathura.Global.mode = NORMAL;
|
|
Zathura.Global.viewing_mode = NORMAL;
|
|
Zathura.Global.reverse_video = FALSE;
|
|
|
|
Zathura.State.filename = (char*) DEFAULT_TEXT;
|
|
Zathura.State.pages = "";
|
|
|
|
/* UI */
|
|
Zathura.UI.window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
|
|
Zathura.UI.box = GTK_BOX(gtk_vbox_new(FALSE, 0));
|
|
Zathura.UI.continuous = GTK_BOX(gtk_vbox_new(FALSE, 0));
|
|
Zathura.UI.view = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
|
|
Zathura.UI.viewport = GTK_VIEWPORT(gtk_viewport_new(NULL, NULL));
|
|
Zathura.UI.statusbar = gtk_event_box_new();
|
|
Zathura.UI.statusbar_entries = GTK_BOX(gtk_hbox_new(FALSE, 0));
|
|
Zathura.UI.inputbar = GTK_ENTRY(gtk_entry_new());
|
|
|
|
/* window */
|
|
gtk_window_set_title(Zathura.UI.window, "zathura");
|
|
GdkGeometry hints = { 1, 1 };
|
|
gtk_window_set_geometry_hints(Zathura.UI.window, NULL, &hints, GDK_HINT_MIN_SIZE);
|
|
gtk_window_set_default_size(Zathura.UI.window, DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
|
g_signal_connect(G_OBJECT(Zathura.UI.window), "destroy", G_CALLBACK(cb_destroy), NULL);
|
|
|
|
/* box */
|
|
gtk_box_set_spacing(Zathura.UI.box, 0);
|
|
gtk_container_add(GTK_CONTAINER(Zathura.UI.window), GTK_WIDGET(Zathura.UI.box));
|
|
|
|
/* continuous */
|
|
gtk_box_set_spacing(Zathura.UI.continuous, 5);
|
|
|
|
/* view */
|
|
g_signal_connect(G_OBJECT(Zathura.UI.view), "key-press-event", G_CALLBACK(cb_view_kb_pressed), NULL);
|
|
gtk_container_add(GTK_CONTAINER(Zathura.UI.view), GTK_WIDGET(Zathura.UI.viewport));
|
|
gtk_viewport_set_shadow_type(Zathura.UI.viewport, GTK_SHADOW_NONE);
|
|
|
|
#if SHOW_SCROLLBARS
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.UI.view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
#else
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.UI.view), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
|
|
#endif
|
|
|
|
/* statusbar */
|
|
gtk_widget_modify_bg(GTK_WIDGET(Zathura.UI.statusbar), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_bg));
|
|
|
|
Zathura.Global.status_text = GTK_LABEL(gtk_label_new(NULL));
|
|
Zathura.Global.status_state = GTK_LABEL(gtk_label_new(NULL));
|
|
Zathura.Global.status_buffer = GTK_LABEL(gtk_label_new(NULL));
|
|
|
|
gtk_widget_modify_fg(GTK_WIDGET(Zathura.Global.status_text), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_fg));
|
|
gtk_widget_modify_fg(GTK_WIDGET(Zathura.Global.status_state), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_fg));
|
|
gtk_widget_modify_fg(GTK_WIDGET(Zathura.Global.status_buffer), GTK_STATE_NORMAL, &(Zathura.Style.statusbar_fg));
|
|
|
|
gtk_widget_modify_font(GTK_WIDGET(Zathura.Global.status_text), Zathura.Style.font);
|
|
gtk_widget_modify_font(GTK_WIDGET(Zathura.Global.status_state), Zathura.Style.font);
|
|
gtk_widget_modify_font(GTK_WIDGET(Zathura.Global.status_buffer), Zathura.Style.font);
|
|
|
|
gtk_misc_set_alignment(GTK_MISC(Zathura.Global.status_text), 0.0, 0.0);
|
|
gtk_misc_set_alignment(GTK_MISC(Zathura.Global.status_state), 1.0, 0.0);
|
|
gtk_misc_set_alignment(GTK_MISC(Zathura.Global.status_buffer), 1.0, 0.0);
|
|
|
|
gtk_misc_set_padding(GTK_MISC(Zathura.Global.status_text), 2.0, 4.0);
|
|
gtk_misc_set_padding(GTK_MISC(Zathura.Global.status_state), 2.0, 4.0);
|
|
gtk_misc_set_padding(GTK_MISC(Zathura.Global.status_buffer), 2.0, 4.0);
|
|
|
|
gtk_label_set_use_markup(Zathura.Global.status_text, TRUE);
|
|
gtk_label_set_use_markup(Zathura.Global.status_state, TRUE);
|
|
gtk_label_set_use_markup(Zathura.Global.status_buffer, TRUE);
|
|
|
|
gtk_box_pack_start(Zathura.UI.statusbar_entries, GTK_WIDGET(Zathura.Global.status_text), TRUE, TRUE, 2);
|
|
gtk_box_pack_start(Zathura.UI.statusbar_entries, GTK_WIDGET(Zathura.Global.status_buffer), FALSE, FALSE, 2);
|
|
gtk_box_pack_start(Zathura.UI.statusbar_entries, GTK_WIDGET(Zathura.Global.status_state), FALSE, FALSE, 2);
|
|
|
|
gtk_container_add(GTK_CONTAINER(Zathura.UI.statusbar), GTK_WIDGET(Zathura.UI.statusbar_entries));
|
|
|
|
/* inputbar */
|
|
gtk_entry_set_inner_border(Zathura.UI.inputbar, NULL);
|
|
gtk_entry_set_has_frame( Zathura.UI.inputbar, FALSE);
|
|
gtk_editable_set_editable( GTK_EDITABLE(Zathura.UI.inputbar), TRUE);
|
|
|
|
gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_bg));
|
|
gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_fg));
|
|
gtk_widget_modify_font(GTK_WIDGET(Zathura.UI.inputbar), Zathura.Style.font);
|
|
|
|
g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "key-press-event", G_CALLBACK(cb_inputbar_kb_pressed), NULL);
|
|
g_signal_connect(G_OBJECT(Zathura.UI.inputbar), "activate", G_CALLBACK(cb_inputbar_activate), NULL);
|
|
|
|
/* packing */
|
|
gtk_box_pack_start(Zathura.UI.box, GTK_WIDGET(Zathura.UI.view), TRUE, TRUE, 0);
|
|
gtk_box_pack_start(Zathura.UI.box, GTK_WIDGET(Zathura.UI.statusbar), FALSE, FALSE, 0);
|
|
gtk_box_pack_end( Zathura.UI.box, GTK_WIDGET(Zathura.UI.inputbar), FALSE, FALSE, 0);
|
|
}
|
|
|
|
void
|
|
build_index(GtkTreeModel* model, GtkTreeIter* parent, PopplerIndexIter* index_iter)
|
|
{
|
|
do
|
|
{
|
|
GtkTreeIter tree_iter;
|
|
PopplerIndexIter *child;
|
|
PopplerAction *action;
|
|
gboolean expand;
|
|
gchar *markup;
|
|
|
|
action = poppler_index_iter_get_action(index_iter);
|
|
expand = poppler_index_iter_is_open(index_iter);
|
|
|
|
if(!action)
|
|
continue;
|
|
|
|
markup = g_markup_escape_text(action->any.title, -1);
|
|
|
|
gtk_tree_store_append(GTK_TREE_STORE(model), &tree_iter, parent);
|
|
gtk_tree_store_set(GTK_TREE_STORE(model), &tree_iter, 0, markup,
|
|
1, action, -1);
|
|
g_object_weak_ref(G_OBJECT(model), (GWeakNotify) poppler_action_free, action);
|
|
g_free(markup);
|
|
|
|
child = poppler_index_iter_get_child(index_iter);
|
|
if(child)
|
|
build_index(model, &tree_iter, child);
|
|
poppler_index_iter_free(child);
|
|
}
|
|
while(poppler_index_iter_next(index_iter));
|
|
}
|
|
|
|
void
|
|
draw(int page_id)
|
|
{
|
|
if(!Zathura.PDF.document || page_id < 0 || page_id > Zathura.PDF.number_of_pages)
|
|
return;
|
|
|
|
double page_width, page_height;
|
|
double width, height;
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.scale_lock));
|
|
double scale = ((double) Zathura.PDF.scale / 100.0);
|
|
pthread_mutex_unlock(&(Zathura.Lock.scale_lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.rotate_lock));
|
|
int rotate = Zathura.PDF.rotate;
|
|
pthread_mutex_unlock(&(Zathura.Lock.rotate_lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[page_id]->lock));
|
|
Page *current_page = Zathura.PDF.pages[page_id];
|
|
|
|
if(current_page->surface)
|
|
cairo_surface_destroy(current_page->surface);
|
|
current_page->surface = NULL;
|
|
|
|
poppler_page_get_size(current_page->page, &page_width, &page_height);
|
|
|
|
if(rotate == 0 || rotate == 180)
|
|
{
|
|
width = page_width * scale;
|
|
height = page_height * scale;
|
|
}
|
|
else
|
|
{
|
|
width = page_height * scale;
|
|
height = page_width * scale;
|
|
}
|
|
|
|
cairo_t *cairo;
|
|
current_page->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
|
|
cairo = cairo_create(current_page->surface);
|
|
|
|
cairo_save(cairo);
|
|
cairo_set_source_rgb(cairo, 1, 1, 1);
|
|
cairo_rectangle(cairo, 0, 0, width, height);
|
|
cairo_fill(cairo);
|
|
cairo_restore(cairo);
|
|
cairo_save(cairo);
|
|
|
|
switch(rotate)
|
|
{
|
|
case 90:
|
|
cairo_translate(cairo, width, 0);
|
|
break;
|
|
case 180:
|
|
cairo_translate(cairo, width, height);
|
|
break;
|
|
case 270:
|
|
cairo_translate(cairo, 0, height);
|
|
break;
|
|
default:
|
|
cairo_translate(cairo, 0, 0);
|
|
}
|
|
|
|
if(scale != 1.0)
|
|
cairo_scale(cairo, scale, scale);
|
|
|
|
if(rotate != 0)
|
|
cairo_rotate(cairo, rotate * G_PI / 180.0);
|
|
|
|
poppler_page_render(current_page->page, cairo);
|
|
|
|
cairo_restore(cairo);
|
|
cairo_destroy(cairo);
|
|
|
|
unsigned char* image = cairo_image_surface_get_data(current_page->surface);
|
|
int x, y, z = 0;
|
|
|
|
if(Zathura.Global.reverse_video)
|
|
{
|
|
for(x = 0; x < cairo_image_surface_get_width(current_page->surface); x++)
|
|
for(y = 0; y < cairo_image_surface_get_height(current_page->surface) * 4; y++)
|
|
image[z++] ^= 0x00FFFFFF;
|
|
}
|
|
|
|
gtk_widget_set_size_request(current_page->drawing_area, width, height);
|
|
gtk_widget_queue_draw(current_page->drawing_area);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[page_id]->lock));
|
|
}
|
|
|
|
void
|
|
change_mode(int mode)
|
|
{
|
|
char* mode_text;
|
|
|
|
switch(mode)
|
|
{
|
|
case INSERT:
|
|
mode_text = "-- INSERT --";
|
|
break;
|
|
case VISUAL:
|
|
mode_text = "-- VISUAL --";
|
|
break;
|
|
default:
|
|
mode_text = "";
|
|
mode = NORMAL;
|
|
break;
|
|
}
|
|
|
|
Zathura.Global.mode = mode;
|
|
notify(DEFAULT, mode_text);
|
|
}
|
|
|
|
void
|
|
highlight_result(int page_id, PopplerRectangle* rectangle)
|
|
{
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[page_id]->lock));
|
|
cairo_t *cairo = cairo_create(Zathura.PDF.pages[page_id]->surface);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[page_id]->lock));
|
|
cairo_set_source_rgba(cairo, Zathura.Style.search_highlight.red, Zathura.Style.search_highlight.green,
|
|
Zathura.Style.search_highlight.blue, TRANSPARENCY);
|
|
|
|
recalcRectangle(page_id, rectangle);
|
|
cairo_rectangle(cairo, rectangle->x1, rectangle->y1, (rectangle->x2 - rectangle->x1), (rectangle->y2 - rectangle->y1));
|
|
cairo_fill(cairo);
|
|
}
|
|
|
|
void notify(int level, char* message)
|
|
{
|
|
switch(level)
|
|
{
|
|
case ERROR:
|
|
gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_e_bg));
|
|
gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_e_fg));
|
|
break;
|
|
case WARNING:
|
|
gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_w_bg));
|
|
gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.notification_w_fg));
|
|
break;
|
|
default:
|
|
gtk_widget_modify_base(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_bg));
|
|
gtk_widget_modify_text(GTK_WIDGET(Zathura.UI.inputbar), GTK_STATE_NORMAL, &(Zathura.Style.inputbar_fg));
|
|
break;
|
|
}
|
|
|
|
if(message)
|
|
gtk_entry_set_text(Zathura.UI.inputbar, message);
|
|
}
|
|
|
|
void
|
|
update_status()
|
|
{
|
|
/* update text */
|
|
gtk_label_set_markup((GtkLabel*) Zathura.Global.status_text, Zathura.State.filename);
|
|
|
|
/* update state */
|
|
pthread_mutex_lock(&(Zathura.Lock.scale_lock));
|
|
char* zoom_level = (Zathura.PDF.scale != 0) ? g_strdup_printf("%d%%", Zathura.PDF.scale) : "";
|
|
pthread_mutex_unlock(&(Zathura.Lock.scale_lock));
|
|
char* status_text = g_strdup_printf("%s %s", zoom_level, Zathura.State.pages);
|
|
gtk_label_set_markup((GtkLabel*) Zathura.Global.status_state, status_text);
|
|
}
|
|
|
|
void
|
|
recalcRectangle(int page_id, PopplerRectangle* rectangle)
|
|
{
|
|
double page_width, page_height;
|
|
double x1 = rectangle->x1;
|
|
double x2 = rectangle->x2;
|
|
double y1 = rectangle->y1;
|
|
double y2 = rectangle->y2;
|
|
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[page_id]->lock));
|
|
poppler_page_get_size(Zathura.PDF.pages[page_id]->page, &page_width, &page_height);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[page_id]->lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.scale_lock));
|
|
double scale = ((double) Zathura.PDF.scale / 100.0);
|
|
pthread_mutex_unlock(&(Zathura.Lock.scale_lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.rotate_lock));
|
|
int rotate = Zathura.PDF.rotate;
|
|
pthread_mutex_unlock(&(Zathura.Lock.rotate_lock));
|
|
|
|
switch(rotate)
|
|
{
|
|
case 90:
|
|
rectangle->x1 = (page_height - y2) * scale;
|
|
rectangle->y1 = x1 * scale;
|
|
rectangle->x2 = (page_height - y1) * scale;
|
|
rectangle->y2 = x2 * scale;
|
|
break;
|
|
case 180:
|
|
rectangle->x1 = (page_width - x2) * scale;
|
|
rectangle->y1 = (page_height - y2) * scale;
|
|
rectangle->x2 = (page_width - x1) * scale;
|
|
rectangle->y2 = (page_height - y1) * scale;
|
|
break;
|
|
case 270:
|
|
rectangle->x1 = y1 * Zathura.PDF.scale;
|
|
rectangle->y1 = (page_width - x2) * scale;
|
|
rectangle->x2 = y2 * Zathura.PDF.scale;
|
|
rectangle->y2 = (page_width - x1) * scale;
|
|
break;
|
|
default:
|
|
rectangle->x1 = x1 * scale;
|
|
rectangle->y1 = y1 * scale;
|
|
rectangle->x2 = x2 * scale;
|
|
rectangle->y2 = y2 * scale;
|
|
}
|
|
}
|
|
|
|
GtkEventBox*
|
|
createCompletionRow(GtkBox* results, char* command, char* description, gboolean group)
|
|
{
|
|
GtkBox *col = GTK_BOX(gtk_hbox_new(FALSE, 0));
|
|
GtkEventBox *row = GTK_EVENT_BOX(gtk_event_box_new());
|
|
|
|
GtkLabel *show_command = GTK_LABEL(gtk_label_new(NULL));
|
|
GtkLabel *show_description = GTK_LABEL(gtk_label_new(NULL));
|
|
|
|
gtk_misc_set_alignment(GTK_MISC(show_command), 0.0, 0.0);
|
|
gtk_misc_set_alignment(GTK_MISC(show_description), 0.0, 0.0);
|
|
|
|
if(group)
|
|
{
|
|
gtk_misc_set_padding(GTK_MISC(show_command), 2.0, 4.0);
|
|
gtk_misc_set_padding(GTK_MISC(show_description), 2.0, 4.0);
|
|
}
|
|
else
|
|
{
|
|
gtk_misc_set_padding(GTK_MISC(show_command), 1.0, 1.0);
|
|
gtk_misc_set_padding(GTK_MISC(show_description), 1.0, 1.0);
|
|
}
|
|
|
|
gtk_label_set_use_markup(show_command, TRUE);
|
|
gtk_label_set_use_markup(show_description, TRUE);
|
|
|
|
gtk_label_set_markup(show_command, g_markup_printf_escaped(FORMAT_COMMAND, command ? command : ""));
|
|
gtk_label_set_markup(show_description, g_markup_printf_escaped(FORMAT_DESCRIPTION, description ? description : ""));
|
|
|
|
if(group)
|
|
{
|
|
gtk_widget_modify_fg(GTK_WIDGET(show_command), GTK_STATE_NORMAL, &(Zathura.Style.completion_g_fg));
|
|
gtk_widget_modify_fg(GTK_WIDGET(show_description), GTK_STATE_NORMAL, &(Zathura.Style.completion_g_fg));
|
|
gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_g_bg));
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_modify_fg(GTK_WIDGET(show_command), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
|
|
gtk_widget_modify_fg(GTK_WIDGET(show_description), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
|
|
gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_bg));
|
|
}
|
|
|
|
gtk_widget_modify_font(GTK_WIDGET(show_command), Zathura.Style.font);
|
|
gtk_widget_modify_font(GTK_WIDGET(show_description), Zathura.Style.font);
|
|
|
|
gtk_box_pack_start(GTK_BOX(col), GTK_WIDGET(show_command), TRUE, TRUE, 2);
|
|
gtk_box_pack_start(GTK_BOX(col), GTK_WIDGET(show_description), FALSE, FALSE, 2);
|
|
|
|
gtk_container_add(GTK_CONTAINER(row), GTK_WIDGET(col));
|
|
|
|
gtk_box_pack_start(results, GTK_WIDGET(row), FALSE, FALSE, 0);
|
|
|
|
return row;
|
|
}
|
|
|
|
void
|
|
setCompletionRowColor(GtkBox* results, int mode, int id)
|
|
{
|
|
GtkEventBox *row = (GtkEventBox*) g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(results)), id);
|
|
|
|
if(row)
|
|
{
|
|
GtkBox *col = (GtkBox*) g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(row)), 0);
|
|
GtkLabel *cmd = (GtkLabel*) g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(col)), 0);
|
|
GtkLabel *cdesc = (GtkLabel*) g_list_nth_data(gtk_container_get_children(GTK_CONTAINER(col)), 1);
|
|
|
|
if(mode == NORMAL)
|
|
{
|
|
gtk_widget_modify_fg(GTK_WIDGET(cmd), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
|
|
gtk_widget_modify_fg(GTK_WIDGET(cdesc), GTK_STATE_NORMAL, &(Zathura.Style.completion_fg));
|
|
gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_bg));
|
|
}
|
|
else
|
|
{
|
|
gtk_widget_modify_fg(GTK_WIDGET(cmd), GTK_STATE_NORMAL, &(Zathura.Style.completion_hl_fg));
|
|
gtk_widget_modify_fg(GTK_WIDGET(cdesc), GTK_STATE_NORMAL, &(Zathura.Style.completion_hl_fg));
|
|
gtk_widget_modify_bg(GTK_WIDGET(row), GTK_STATE_NORMAL, &(Zathura.Style.completion_hl_bg));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
set_page(int page)
|
|
{
|
|
if(page > Zathura.PDF.number_of_pages || page < 0)
|
|
{
|
|
notify(WARNING, "Could not open page");
|
|
return;
|
|
}
|
|
|
|
Zathura.PDF.page_number = page;
|
|
Zathura.State.pages = g_strdup_printf("[%i/%i]", page + 1, Zathura.PDF.number_of_pages);
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[page]->lock));
|
|
switch_view(Zathura.PDF.pages[page]->drawing_area);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[page]->lock));
|
|
}
|
|
|
|
void
|
|
switch_view(GtkWidget* widget)
|
|
{
|
|
pthread_mutex_lock(&(Zathura.Lock.viewport_lock));
|
|
GtkWidget* child = gtk_bin_get_child(GTK_BIN(Zathura.UI.viewport));
|
|
if(child)
|
|
{
|
|
g_object_ref(child);
|
|
gtk_container_remove(GTK_CONTAINER(Zathura.UI.viewport), child);
|
|
}
|
|
pthread_mutex_unlock(&(Zathura.Lock.viewport_lock));
|
|
|
|
gtk_container_add(GTK_CONTAINER(Zathura.UI.viewport), GTK_WIDGET(widget));
|
|
}
|
|
|
|
/* thread implementation */
|
|
void*
|
|
render(void* parameter)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
pthread_exit(NULL);
|
|
|
|
intptr_t t = (intptr_t) parameter;
|
|
int begin_page = (int) t;
|
|
|
|
int page;
|
|
for(page = 0; page < Zathura.PDF.number_of_pages; page++)
|
|
draw((begin_page + page) % Zathura.PDF.number_of_pages);
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
void* search(void* parameter)
|
|
{
|
|
Argument* argument = (Argument*) parameter;
|
|
|
|
static char* search_item;
|
|
static int direction;
|
|
int page_counter;
|
|
int next_page;
|
|
GList* results;
|
|
GList* list;
|
|
|
|
if(argument->data)
|
|
search_item = g_strdup((char*) argument->data);
|
|
|
|
if(!Zathura.PDF.document || !search_item || !strlen(search_item))
|
|
pthread_exit(NULL);
|
|
|
|
/* search document */
|
|
if(argument->n)
|
|
direction = (argument->n == BACKWARD) ? -1 : 1;
|
|
|
|
for(page_counter = 0; page_counter < Zathura.PDF.number_of_pages; page_counter++)
|
|
{
|
|
next_page = (Zathura.PDF.number_of_pages + Zathura.PDF.page_number +
|
|
page_counter * direction) % Zathura.PDF.number_of_pages;
|
|
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[next_page]->lock));
|
|
results = poppler_page_find_text(Zathura.PDF.pages[next_page]->page, search_item);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[next_page]->lock));
|
|
|
|
if(results)
|
|
break;
|
|
}
|
|
|
|
/* draw results */
|
|
if(results)
|
|
{
|
|
for(list = results; list && list->data; list = g_list_next(list))
|
|
highlight_result(next_page, (PopplerRectangle*) list->data);
|
|
|
|
set_page(next_page);
|
|
}
|
|
else
|
|
{
|
|
printf("Nothing found for %s\n", search_item);
|
|
}
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
/* shortcut implementation */
|
|
void
|
|
sc_abort(Argument* argument)
|
|
{
|
|
/* Clear buffer */
|
|
if(Zathura.Global.buffer)
|
|
{
|
|
g_string_free(Zathura.Global.buffer, TRUE);
|
|
Zathura.Global.buffer = NULL;
|
|
gtk_label_set_markup((GtkLabel*) Zathura.Global.status_buffer, "");
|
|
}
|
|
|
|
/* Set back to normal mode */
|
|
change_mode(NORMAL);
|
|
}
|
|
|
|
void
|
|
sc_adjust_window(Argument* argument)
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
sc_change_mode(Argument* argument)
|
|
{
|
|
if(argument)
|
|
change_mode(argument->n);
|
|
}
|
|
|
|
void
|
|
sc_focus_inputbar(Argument* argument)
|
|
{
|
|
if(argument->data)
|
|
{
|
|
notify(DEFAULT, argument->data);
|
|
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.inputbar));
|
|
gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
|
|
}
|
|
}
|
|
|
|
void
|
|
sc_navigate(Argument* argument)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
return;
|
|
|
|
int number_of_pages = Zathura.PDF.number_of_pages;
|
|
int new_page = Zathura.PDF.page_number;
|
|
|
|
if(argument->n == NEXT)
|
|
new_page = (new_page + number_of_pages + 1) % number_of_pages;
|
|
else if(argument->n == PREVIOUS)
|
|
new_page = (new_page + number_of_pages - 1) % number_of_pages;
|
|
|
|
set_page(new_page);
|
|
update_status();
|
|
}
|
|
|
|
void
|
|
sc_revert_video(Argument* argument)
|
|
{
|
|
pthread_mutex_lock(&(Zathura.Lock.render_lock));
|
|
if(Zathura.PDF.render_thread)
|
|
pthread_cancel(Zathura.PDF.render_thread);
|
|
|
|
Zathura.Global.reverse_video = !Zathura.Global.reverse_video;
|
|
|
|
intptr_t t = Zathura.PDF.page_number;
|
|
pthread_create(&(Zathura.PDF.render_thread), NULL, render, (gpointer) t);
|
|
pthread_mutex_unlock(&(Zathura.Lock.render_lock));
|
|
}
|
|
|
|
void
|
|
sc_rotate(Argument* argument)
|
|
{
|
|
pthread_mutex_lock(&(Zathura.Lock.render_lock));
|
|
if(Zathura.PDF.render_thread)
|
|
pthread_cancel(Zathura.PDF.render_thread);
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.rotate_lock));
|
|
Zathura.PDF.rotate = (Zathura.PDF.rotate + 90) % 360;
|
|
pthread_mutex_unlock(&(Zathura.Lock.rotate_lock));
|
|
|
|
intptr_t t = Zathura.PDF.page_number;
|
|
pthread_create(&(Zathura.PDF.render_thread), NULL, render, (gpointer) t);
|
|
pthread_mutex_unlock(&(Zathura.Lock.render_lock));
|
|
}
|
|
|
|
void
|
|
sc_scroll(Argument* argument)
|
|
{
|
|
GtkAdjustment* adjustment;
|
|
|
|
if( (argument->n == LEFT) || (argument->n == RIGHT) )
|
|
adjustment = gtk_scrolled_window_get_hadjustment(Zathura.UI.view);
|
|
else
|
|
adjustment = gtk_scrolled_window_get_vadjustment(Zathura.UI.view);
|
|
|
|
gdouble view_size = gtk_adjustment_get_page_size(adjustment);
|
|
gdouble value = gtk_adjustment_get_value(adjustment);
|
|
gdouble max = gtk_adjustment_get_upper(adjustment) - view_size;
|
|
|
|
if( (argument->n == LEFT) || (argument->n == DOWN))
|
|
gtk_adjustment_set_value(adjustment, (value - SCROLL_STEP) < 0 ? 0 : (value - SCROLL_STEP));
|
|
else if (argument->n == TOP)
|
|
gtk_adjustment_set_value(adjustment, 0);
|
|
else if(argument->n == BOTTOM)
|
|
gtk_adjustment_set_value(adjustment, max);
|
|
else
|
|
gtk_adjustment_set_value(adjustment, (value + SCROLL_STEP) > max ? max : (value + SCROLL_STEP));
|
|
}
|
|
|
|
void
|
|
sc_search(Argument* argument)
|
|
{
|
|
pthread_mutex_lock(&(Zathura.Lock.search_lock));
|
|
if(Zathura.PDF.search_thread)
|
|
pthread_cancel(Zathura.PDF.search_thread);
|
|
|
|
pthread_create(&(Zathura.PDF.search_thread), NULL, search, (gpointer) argument);
|
|
pthread_mutex_unlock(&(Zathura.Lock.search_lock));
|
|
}
|
|
|
|
void
|
|
sc_toggle_index(Argument* argument)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
return;
|
|
|
|
if(!Zathura.UI.index)
|
|
{
|
|
GtkWidget *treeview;
|
|
GtkTreeModel *model;
|
|
PopplerIndexIter *index_iter;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeSelection *selection;
|
|
|
|
Zathura.UI.index = gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.UI.index),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
|
|
index_iter = poppler_index_iter_new(Zathura.PDF.document);
|
|
|
|
if(index_iter)
|
|
{
|
|
model = GTK_TREE_MODEL(gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER));
|
|
build_index(model, NULL, index_iter);
|
|
poppler_index_iter_free(index_iter);
|
|
treeview = gtk_tree_view_new_with_model(model);
|
|
g_object_unref(model);
|
|
|
|
renderer = gtk_cell_renderer_text_new();
|
|
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), 0, "Title", renderer,
|
|
"markup", 0, NULL);
|
|
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
|
|
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
|
|
g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(cb_index_selection_changed),
|
|
NULL);
|
|
|
|
gtk_container_add(GTK_CONTAINER(Zathura.UI.index), treeview);
|
|
gtk_widget_show(GTK_WIDGET(treeview));
|
|
}
|
|
else
|
|
{
|
|
notify(WARNING, "This document does not contain any index");
|
|
Zathura.UI.index = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static gboolean show = TRUE;
|
|
|
|
if(show)
|
|
{
|
|
gtk_widget_show(Zathura.UI.index);
|
|
switch_view(Zathura.UI.index);
|
|
}
|
|
else
|
|
{
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[Zathura.PDF.page_number]->lock));
|
|
switch_view( Zathura.PDF.pages[Zathura.PDF.page_number]->drawing_area );
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[Zathura.PDF.page_number]->lock));
|
|
}
|
|
|
|
show = !show;
|
|
}
|
|
|
|
void
|
|
sc_toggle_inputbar(Argument* argument)
|
|
{
|
|
if(GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura.UI.inputbar)))
|
|
gtk_widget_hide(GTK_WIDGET(Zathura.UI.inputbar));
|
|
else
|
|
gtk_widget_show(GTK_WIDGET(Zathura.UI.inputbar));
|
|
}
|
|
|
|
void
|
|
sc_toggle_statusbar(Argument* argument)
|
|
{
|
|
if(GTK_WIDGET_VISIBLE(GTK_WIDGET(Zathura.UI.statusbar)))
|
|
gtk_widget_hide(GTK_WIDGET(Zathura.UI.statusbar));
|
|
else
|
|
gtk_widget_show(GTK_WIDGET(Zathura.UI.statusbar));
|
|
}
|
|
|
|
void
|
|
sc_quit(Argument* argument)
|
|
{
|
|
cb_destroy(NULL, NULL);
|
|
}
|
|
|
|
/* inputbar shortcut declarations */
|
|
void
|
|
isc_abort(Argument* argument)
|
|
{
|
|
Argument arg = { HIDE };
|
|
isc_completion(&arg);
|
|
|
|
notify(DEFAULT, "");
|
|
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
|
|
}
|
|
|
|
void
|
|
isc_command_history(Argument* argument)
|
|
{
|
|
static int current = 0;
|
|
int length = g_list_length(Zathura.Global.history);
|
|
|
|
if(length > 0)
|
|
{
|
|
if(argument->n == NEXT)
|
|
current = (length + current + 1) % length;
|
|
else
|
|
current = (length + current - 1) % length;
|
|
|
|
gchar* command = (gchar*) g_list_nth_data(Zathura.Global.history, current);
|
|
notify(DEFAULT, command);
|
|
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.inputbar));
|
|
gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_completion(Argument* argument)
|
|
{
|
|
gchar *input = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 1, -1);
|
|
gchar identifier = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 0, 1)[0];
|
|
int length = strlen(input);
|
|
|
|
if(!length && !identifier)
|
|
return;
|
|
|
|
/* get current information*/
|
|
char* first_space = strstr(input, " ");
|
|
char* current_command;
|
|
char* current_parameter;
|
|
int current_command_length;
|
|
int current_parameter_length;
|
|
|
|
if(!first_space)
|
|
{
|
|
current_command = input;
|
|
current_command_length = length;
|
|
current_parameter = NULL;
|
|
current_parameter_length = 0;
|
|
}
|
|
else
|
|
{
|
|
int offset = abs(input - first_space);
|
|
current_command = g_strndup(input, offset);
|
|
current_command_length = strlen(current_command);
|
|
current_parameter = input + offset + 1;
|
|
current_parameter_length = strlen(current_parameter);
|
|
}
|
|
|
|
/* if the identifier does not match the command sign and
|
|
* the completion should not be hidden, leave this function */
|
|
if((identifier != ':') && (argument->n != HIDE))
|
|
return;
|
|
|
|
/* static elements */
|
|
static GtkBox *results = NULL;
|
|
static CompletionRow *rows = NULL;
|
|
|
|
static int current_item = 0;
|
|
static int n_items = 0;
|
|
|
|
static char *previous_command = NULL;
|
|
static char *previous_parameter = NULL;
|
|
static int previous_id = 0;
|
|
static int previous_length = 0;
|
|
|
|
static gboolean command_mode = TRUE;
|
|
|
|
/* delete old list iff
|
|
* the completion should be hidden
|
|
* the current command differs from the previous one
|
|
* the current parameter differs from the previous one
|
|
*/
|
|
if( (argument->n == HIDE) ||
|
|
(current_parameter && previous_parameter && strcmp(current_parameter, previous_parameter)) ||
|
|
(current_command && previous_command && strcmp(current_command, previous_command)) ||
|
|
(previous_length != length)
|
|
)
|
|
{
|
|
if(results)
|
|
gtk_widget_destroy(GTK_WIDGET(results));
|
|
|
|
results = NULL;
|
|
|
|
if(rows)
|
|
free(rows);
|
|
|
|
rows = NULL;
|
|
current_item = 0;
|
|
n_items = 0;
|
|
command_mode = TRUE;
|
|
|
|
if(argument->n == HIDE)
|
|
return;
|
|
}
|
|
|
|
/* create new list iff
|
|
* there is no current list
|
|
* the current command differs from the previous one
|
|
* the current parameter differs from the previous one
|
|
*/
|
|
if( (!results) )
|
|
{
|
|
results = GTK_BOX(gtk_vbox_new(FALSE, 0));
|
|
|
|
/* create list based on parameters iff
|
|
* there is a current parameter given
|
|
* there is an old list with commands
|
|
* the current command does not differ from the previous one
|
|
* the current command has an completion function
|
|
*/
|
|
if( (previous_command) && (current_parameter) && !strcmp(current_command, previous_command) )
|
|
{
|
|
if(previous_id < 0 || !commands[previous_id].completion)
|
|
return;
|
|
|
|
Completion *result = commands[previous_id].completion(current_parameter);
|
|
|
|
if(!result || !result->groups)
|
|
return;
|
|
|
|
command_mode = FALSE;
|
|
CompletionGroup* group = NULL;
|
|
CompletionElement* element = NULL;
|
|
|
|
rows = malloc(sizeof(CompletionRow));
|
|
|
|
for(group = result->groups; group != NULL; group = group->next)
|
|
{
|
|
int group_elements = 0;
|
|
|
|
for(element = group->elements; element != NULL; element = element->next)
|
|
{
|
|
if( (element->value) &&
|
|
(current_parameter_length <= strlen(element->value)) && !strncmp(current_parameter, element->value, current_parameter_length)
|
|
)
|
|
{
|
|
rows = realloc(rows, (n_items + 1) * sizeof(CompletionRow));
|
|
rows[n_items].command = element->value;
|
|
rows[n_items].description = element->description;
|
|
rows[n_items].command_id = previous_id;
|
|
rows[n_items].is_group = FALSE;
|
|
rows[n_items++].row = GTK_WIDGET(createCompletionRow(results, element->value, element->description, FALSE));
|
|
group_elements++;
|
|
}
|
|
}
|
|
|
|
if(group->value && group_elements > 0)
|
|
{
|
|
rows = realloc(rows, (n_items + 1) * sizeof(CompletionRow));
|
|
rows[n_items].command = group->value;
|
|
rows[n_items].description = NULL;
|
|
rows[n_items].command_id = -1;
|
|
rows[n_items].is_group = TRUE;
|
|
rows[n_items].row = GTK_WIDGET(createCompletionRow(results, group->value, NULL, TRUE));
|
|
|
|
/* Swap group element with first element of the list */
|
|
CompletionRow temp = rows[n_items - group_elements];
|
|
gtk_box_reorder_child(results, rows[n_items].row, n_items - group_elements);
|
|
rows[n_items - group_elements] = rows[n_items];
|
|
rows[n_items] = temp;
|
|
|
|
n_items++;
|
|
}
|
|
}
|
|
|
|
/* clean up */
|
|
group = result->groups;
|
|
|
|
while(group)
|
|
{
|
|
element = group->elements;
|
|
while(element)
|
|
{
|
|
CompletionElement* ne = element->next;
|
|
free(element);
|
|
element = ne;
|
|
}
|
|
|
|
CompletionGroup *ng = group->next;
|
|
free(group);
|
|
group = ng;
|
|
}
|
|
}
|
|
/* create list based on commands */
|
|
else
|
|
{
|
|
int i = 0;
|
|
command_mode = TRUE;
|
|
|
|
rows = malloc(LENGTH(commands) * sizeof(CompletionRow));
|
|
|
|
for(i = 0; i < LENGTH(commands); i++)
|
|
{
|
|
int abbr_length = strlen(commands[i].abbr);
|
|
int cmd_length = strlen(commands[i].command);
|
|
|
|
/* add command to list iff
|
|
* the current command would match the command
|
|
* the current command would match the abbreviation
|
|
*/
|
|
if( ((current_command_length <= cmd_length) && !strncmp(current_command, commands[i].command, current_command_length)) ||
|
|
((current_command_length <= abbr_length) && !strncmp(current_command, commands[i].abbr, current_command_length))
|
|
)
|
|
{
|
|
rows[n_items].command = commands[i].command;
|
|
rows[n_items].description = commands[i].description;
|
|
rows[n_items].command_id = i;
|
|
rows[n_items].is_group = FALSE;
|
|
rows[n_items++].row = GTK_WIDGET(createCompletionRow(results, commands[i].command, commands[i].description, FALSE));
|
|
}
|
|
}
|
|
|
|
rows = realloc(rows, n_items * sizeof(CompletionRow));
|
|
}
|
|
|
|
gtk_box_pack_start(Zathura.UI.box, GTK_WIDGET(results), FALSE, FALSE, 0);
|
|
gtk_widget_show_all(GTK_WIDGET(Zathura.UI.window));
|
|
|
|
current_item = (argument->n == NEXT) ? -1 : 0;
|
|
}
|
|
|
|
/* update coloring iff
|
|
* there is a list with items
|
|
*/
|
|
if( (results) && (n_items > 0) )
|
|
{
|
|
setCompletionRowColor(results, NORMAL, current_item);
|
|
char* temp;
|
|
int i = 0, next_group = 0;
|
|
|
|
for(i = 0; i < n_items; i++)
|
|
{
|
|
if(argument->n == NEXT || argument->n == NEXT_GROUP)
|
|
current_item = (current_item + n_items + 1) % n_items;
|
|
else if(argument->n == PREVIOUS || argument->n == PREVIOUS_GROUP)
|
|
current_item = (current_item + n_items - 1) % n_items;
|
|
|
|
if(rows[current_item].is_group)
|
|
{
|
|
if(!command_mode && (argument->n == NEXT_GROUP || argument->n == PREVIOUS_GROUP))
|
|
next_group = 1;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if(!command_mode && (next_group == 0) && (argument->n == NEXT_GROUP || argument->n == PREVIOUS_GROUP))
|
|
continue;
|
|
break;
|
|
}
|
|
}
|
|
|
|
setCompletionRowColor(results, HIGHLIGHT, current_item);
|
|
|
|
if(command_mode)
|
|
{
|
|
char* cp = (current_parameter) ? g_strconcat(" ", current_parameter, NULL) : 0;
|
|
temp = g_strconcat(":", rows[current_item].command, cp, NULL);
|
|
}
|
|
else
|
|
{
|
|
temp = g_strconcat(":", previous_command, " ", rows[current_item].command, NULL);
|
|
}
|
|
|
|
gtk_entry_set_text(Zathura.UI.inputbar, temp);
|
|
gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
|
|
g_free(temp);
|
|
|
|
previous_command = (command_mode) ? rows[current_item].command : current_command;
|
|
previous_parameter = (command_mode) ? current_parameter : rows[current_item].command;
|
|
previous_length = strlen(previous_command) + ((command_mode) ? (length - current_command_length) : (strlen(previous_parameter) + 1));
|
|
previous_id = rows[current_item].command_id;
|
|
}
|
|
}
|
|
|
|
void
|
|
isc_string_manipulation(Argument* argument)
|
|
{
|
|
if(argument->n == DELETE_LAST_WORD)
|
|
{
|
|
gchar *input = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 0, -1);
|
|
int length = strlen(input);
|
|
int i = 0;
|
|
|
|
for(i = length; i > 0; i--)
|
|
{
|
|
if( (input[i] == ' ') ||
|
|
(input[i] == '/') )
|
|
{
|
|
if(i == (length - 1))
|
|
continue;
|
|
|
|
i = (input[i] == ' ') ? (i - 1) : i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
notify(DEFAULT, g_strndup(input, i + 1));
|
|
gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
|
|
}
|
|
}
|
|
|
|
/* command implementation */
|
|
gboolean
|
|
cmd_close(int argc, char** argv)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
{
|
|
if(argc != -1)
|
|
notify(ERROR, "No file has been opened");
|
|
return FALSE;
|
|
}
|
|
|
|
/* check for current render thread */
|
|
pthread_mutex_lock(&(Zathura.Lock.render_lock));
|
|
if(Zathura.PDF.render_thread)
|
|
pthread_cancel(Zathura.PDF.render_thread);
|
|
pthread_mutex_unlock(&(Zathura.Lock.render_lock));
|
|
|
|
/* clean up pages */
|
|
int i;
|
|
for(i = 0; i < Zathura.PDF.number_of_pages; i++)
|
|
{
|
|
Page* current_page = Zathura.PDF.pages[i];
|
|
g_object_unref(current_page->page);
|
|
if(current_page->surface)
|
|
cairo_surface_destroy(current_page->surface);
|
|
gtk_widget_destroy(current_page->drawing_area);
|
|
pthread_mutex_destroy(¤t_page->lock);
|
|
}
|
|
|
|
/* reset values */
|
|
free(Zathura.PDF.pages);
|
|
g_object_unref(Zathura.PDF.document);
|
|
|
|
Zathura.State.pages = "";
|
|
Zathura.State.filename = (char*) DEFAULT_TEXT;
|
|
Zathura.PDF.document = NULL;
|
|
Zathura.PDF.file = "";
|
|
Zathura.PDF.page_number = 0;
|
|
Zathura.PDF.number_of_pages = 0;
|
|
Zathura.PDF.scale = 0;
|
|
Zathura.PDF.rotate = 0;
|
|
|
|
/* destroy index */
|
|
if(Zathura.UI.index)
|
|
{
|
|
gtk_widget_destroy(Zathura.UI.index);
|
|
Zathura.UI.index = NULL;
|
|
}
|
|
|
|
/* destroy information */
|
|
if(Zathura.UI.information)
|
|
{
|
|
gtk_widget_destroy(Zathura.UI.information);
|
|
Zathura.UI.information = NULL;
|
|
}
|
|
|
|
update_status();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_info(int argc, char** argv)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
return TRUE;
|
|
|
|
if(!Zathura.UI.information)
|
|
{
|
|
GtkListStore *list;
|
|
GtkTreeIter iter;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeSelection *selection;
|
|
|
|
list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
|
|
|
|
/* read document information */
|
|
gchar *title, *author;
|
|
gchar *subject, *keywords;
|
|
gchar *creator, *producer;
|
|
GTime creation_date, modification_date;
|
|
|
|
g_object_get(Zathura.PDF.document,
|
|
"title", &title,
|
|
"author", &author,
|
|
"subject", &subject,
|
|
"keywords", &keywords,
|
|
"creator", &creator,
|
|
"producer", &producer,
|
|
"creation-date", &creation_date,
|
|
"mod-date", &modification_date,
|
|
NULL);
|
|
|
|
/* append information to list */
|
|
gtk_list_store_append(list, &iter);
|
|
gtk_list_store_set(list, &iter, 0, "Author", 1, author ? author : "", -1);
|
|
gtk_list_store_append(list, &iter);
|
|
gtk_list_store_set(list, &iter, 0, "Title", 1, title ? title : "", -1);
|
|
gtk_list_store_append(list, &iter);
|
|
gtk_list_store_set(list, &iter, 0, "Subject", 1, subject ? subject : "", -1);
|
|
gtk_list_store_append(list, &iter);
|
|
gtk_list_store_set(list, &iter, 0, "Keywords", 1, keywords ? keywords : "", -1);
|
|
gtk_list_store_append(list, &iter);
|
|
gtk_list_store_set(list, &iter, 0, "Creator", 1, creator ? creator : "", -1);
|
|
gtk_list_store_append(list, &iter);
|
|
gtk_list_store_set(list, &iter, 0, "Producer", 1, producer ? producer : "", -1);
|
|
|
|
Zathura.UI.information = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
|
|
renderer = gtk_cell_renderer_text_new();
|
|
|
|
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(Zathura.UI.information), -1,
|
|
"Name", renderer, "text", 0, NULL);
|
|
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(Zathura.UI.information), -1,
|
|
"Value", renderer, "text", 1, NULL);
|
|
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(Zathura.UI.information));
|
|
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
|
|
|
|
gtk_widget_show_all(Zathura.UI.information);
|
|
}
|
|
|
|
static gboolean show = TRUE;
|
|
|
|
if(show)
|
|
switch_view(Zathura.UI.information);
|
|
else
|
|
{
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[Zathura.PDF.page_number]->lock));
|
|
switch_view( Zathura.PDF.pages[Zathura.PDF.page_number]->drawing_area );
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[Zathura.PDF.page_number]->lock));
|
|
}
|
|
|
|
show = !show;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_open(int argc, char** argv)
|
|
{
|
|
if(argc == 0 || strlen(argv[0]) == 0)
|
|
return FALSE;
|
|
|
|
/* get filename */
|
|
char* file = realpath(argv[0], NULL);
|
|
|
|
if(argv[0][0] == '~')
|
|
file = g_strdup_printf("%s%s", getenv("HOME"), argv[0] + 1);
|
|
|
|
/* check if file exists */
|
|
if(!g_file_test(file, G_FILE_TEST_IS_REGULAR))
|
|
{
|
|
notify(ERROR, "File does not exist");
|
|
return FALSE;
|
|
}
|
|
|
|
/* close old file */
|
|
cmd_close(-1, NULL);
|
|
|
|
/* open file */
|
|
Zathura.PDF.document = poppler_document_new_from_file(g_strdup_printf("file://%s", file),
|
|
(argc == 2) ? argv[1] : NULL, NULL);
|
|
|
|
if(!Zathura.PDF.document)
|
|
{
|
|
notify(ERROR, "Can not open file");
|
|
return FALSE;
|
|
}
|
|
|
|
Zathura.PDF.number_of_pages = poppler_document_get_n_pages(Zathura.PDF.document);
|
|
Zathura.PDF.file = file;
|
|
pthread_mutex_lock(&(Zathura.Lock.scale_lock));
|
|
Zathura.PDF.scale = 100;
|
|
pthread_mutex_unlock(&(Zathura.Lock.scale_lock));
|
|
pthread_mutex_lock(&(Zathura.Lock.rotate_lock));
|
|
Zathura.PDF.rotate = 0;
|
|
pthread_mutex_unlock(&(Zathura.Lock.rotate_lock));
|
|
Zathura.PDF.pages = malloc(Zathura.PDF.number_of_pages * sizeof(Page*));
|
|
Zathura.State.filename = file;
|
|
|
|
/* get pages */
|
|
int i;
|
|
for(i = 0; i < Zathura.PDF.number_of_pages; i++)
|
|
{
|
|
Zathura.PDF.pages[i] = malloc(sizeof(Page));
|
|
Zathura.PDF.pages[i]->page = poppler_document_get_page(Zathura.PDF.document, i);
|
|
Zathura.PDF.pages[i]->surface = NULL;
|
|
Zathura.PDF.pages[i]->drawing_area = gtk_drawing_area_new();
|
|
pthread_mutex_init(&(Zathura.PDF.pages[i]->lock), NULL);
|
|
|
|
gtk_widget_modify_bg(GTK_WIDGET(Zathura.PDF.pages[i]->drawing_area), GTK_STATE_NORMAL, &(Zathura.Style.default_bg));
|
|
gtk_widget_show(Zathura.PDF.pages[i]->drawing_area);
|
|
|
|
intptr_t t = i;
|
|
g_signal_connect(G_OBJECT(Zathura.PDF.pages[i]->drawing_area), "expose-event", G_CALLBACK(cb_draw), (gpointer) t);
|
|
}
|
|
|
|
/* render pages */
|
|
pthread_mutex_lock(&(Zathura.Lock.render_lock));
|
|
if(Zathura.PDF.render_thread)
|
|
pthread_cancel(Zathura.PDF.render_thread);
|
|
|
|
pthread_create(&(Zathura.PDF.render_thread), NULL, render, NULL);
|
|
pthread_mutex_unlock(&(Zathura.Lock.render_lock));
|
|
|
|
set_page(0);
|
|
update_status();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_print(int argc, char** argv)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
return TRUE;
|
|
|
|
if(argc == 0)
|
|
{
|
|
notify(WARNING, "No printer specified");
|
|
return FALSE;
|
|
}
|
|
|
|
char* printer = argv[0];
|
|
char* sites = (argc == 2) ? argv[1] : g_strdup_printf("%i", Zathura.PDF.number_of_pages);
|
|
char* print_command = g_strdup_printf(PRINT_COMMAND, printer, sites, Zathura.PDF.file);
|
|
system(print_command);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_rotate(int argc, char** argv)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_set(int argc, char** argv)
|
|
{
|
|
if(argc <= 0 || argc >= 3)
|
|
return FALSE;
|
|
|
|
int i;
|
|
for(i = 0; i < LENGTH(settings); i++)
|
|
{
|
|
if(!strcmp(argv[0], settings[i].name))
|
|
{
|
|
/* check var type */
|
|
if(settings[i].type == 'b')
|
|
{
|
|
gboolean *x = (gboolean*) (settings[i].variable);
|
|
gboolean new_value = TRUE;
|
|
|
|
if(!strcmp(argv[1], "false") || !strcmp(argv[1], "0"))
|
|
new_value = FALSE;
|
|
|
|
*x = new_value;
|
|
}
|
|
else if(settings[i].type == 'i')
|
|
{
|
|
if(argc != 2)
|
|
return FALSE;
|
|
|
|
int *x = (int*) (settings[i].variable);
|
|
*x = atoi(argv[1]);
|
|
}
|
|
else if(settings[i].type == 's')
|
|
{
|
|
if(argc != 2)
|
|
return FALSE;
|
|
|
|
char **x = (char**) settings[i].variable;
|
|
*x = argv[1];
|
|
}
|
|
else if(settings[i].type == 'c')
|
|
{
|
|
if(argc != 2)
|
|
return FALSE;
|
|
|
|
char *x = (char*) (settings[i].variable);
|
|
*x = argv[1][0];
|
|
}
|
|
|
|
/* render */
|
|
if(settings[i].render)
|
|
{
|
|
if(!Zathura.PDF.document)
|
|
return FALSE;
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.render_lock));
|
|
if(Zathura.PDF.render_thread)
|
|
pthread_cancel(Zathura.PDF.render_thread);
|
|
|
|
intptr_t t = Zathura.PDF.page_number;
|
|
pthread_create(&(Zathura.PDF.render_thread), NULL, render, (gpointer) t);
|
|
pthread_mutex_unlock(&(Zathura.Lock.render_lock));
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_quit(int argc, char** argv)
|
|
{
|
|
cb_destroy(NULL, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cmd_save(int argc, char** argv)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* completion command implementation */
|
|
Completion*
|
|
cc_open(char* input)
|
|
{
|
|
/* init completion group */
|
|
Completion *completion = malloc(sizeof(Completion));
|
|
CompletionGroup* group = malloc(sizeof(CompletionGroup));
|
|
|
|
group->value = NULL;
|
|
group->next = NULL;
|
|
group->elements = NULL;
|
|
|
|
completion->groups = group;
|
|
|
|
/* read dir */
|
|
char* path = "/";
|
|
char* file = "";
|
|
int file_length = 0;
|
|
|
|
/* ~ */
|
|
if(input[0] == '~')
|
|
{
|
|
char *file = g_strdup_printf(":open %s/%s", getenv("HOME"), input + 1);
|
|
gtk_entry_set_text(Zathura.UI.inputbar, file);
|
|
gtk_editable_set_position(GTK_EDITABLE(Zathura.UI.inputbar), -1);
|
|
return NULL;
|
|
}
|
|
|
|
/* parse input string */
|
|
if(input && strlen(input) > 0)
|
|
{
|
|
char* path_temp = dirname(strdup(input));
|
|
char* file_temp = basename(strdup(input));
|
|
char last_char = input[strlen(input) - 1];
|
|
|
|
if( !strcmp(path_temp, "/") && !strcmp(file_temp, "/") )
|
|
file = "";
|
|
else if( !strcmp(path_temp, "/") && strcmp(file_temp, "/") && last_char != '/')
|
|
file = file_temp;
|
|
else if( !strcmp(path_temp, "/") && strcmp(file_temp, "/") && last_char == '/')
|
|
path = g_strdup_printf("/%s/", file_temp);
|
|
else if(last_char == '/')
|
|
path = input;
|
|
else
|
|
{
|
|
path = g_strdup_printf("%s/", path_temp);
|
|
file = file_temp;
|
|
}
|
|
}
|
|
|
|
file_length = strlen(file);
|
|
|
|
/* open directory */
|
|
GDir* dir = g_dir_open(path, 0, NULL);
|
|
if(!dir)
|
|
return NULL;
|
|
|
|
/* create element list */
|
|
CompletionElement *last_element = NULL;
|
|
char* name = NULL;
|
|
int element_counter = 0;
|
|
|
|
while((name = (char*) g_dir_read_name(dir)) != NULL)
|
|
{
|
|
char* d_name = g_filename_display_name(name);
|
|
int d_length = strlen(d_name);
|
|
|
|
if( ((file_length <= d_length) && !strncmp(file, d_name, file_length)) ||
|
|
(file_length == 0) )
|
|
{
|
|
CompletionElement* el = malloc(sizeof(CompletionElement));
|
|
el->value = g_strdup_printf("%s%s", path, d_name);
|
|
el->description = NULL;
|
|
el->next = NULL;
|
|
|
|
if(element_counter++ != 0)
|
|
last_element->next = el;
|
|
else
|
|
group->elements = el;
|
|
|
|
last_element = el;
|
|
}
|
|
}
|
|
|
|
g_dir_close(dir);
|
|
|
|
return completion;
|
|
}
|
|
|
|
Completion*
|
|
cc_print(char* input)
|
|
{
|
|
/* init completion group */
|
|
Completion *completion = malloc(sizeof(Completion));
|
|
CompletionGroup* group = malloc(sizeof(CompletionGroup));
|
|
|
|
group->value = NULL;
|
|
group->next = NULL;
|
|
group->elements = NULL;
|
|
|
|
completion->groups = group;
|
|
CompletionElement *last_element = NULL;
|
|
int element_counter = 0;
|
|
int input_length = input ? strlen(input) : 0;
|
|
|
|
/* read printers */
|
|
char *current_line = NULL, current_char;
|
|
int count = 0;
|
|
FILE *fp;
|
|
|
|
fp = popen(LIST_PRINTER_COMMAND, "r");
|
|
|
|
if(!fp)
|
|
return NULL;
|
|
|
|
while((current_char = fgetc(fp)) != EOF)
|
|
{
|
|
if(!current_line)
|
|
current_line = malloc(sizeof(char));
|
|
|
|
current_line = realloc(current_line, (count + 1) * sizeof(char));
|
|
|
|
if(current_char != '\n')
|
|
current_line[count++] = current_char;
|
|
else
|
|
{
|
|
current_line[count] = '\0';
|
|
int line_length = strlen(current_line);
|
|
|
|
if( (input_length <= line_length) ||
|
|
(!strncmp(input, current_line, input_length)) )
|
|
{
|
|
CompletionElement* el = malloc(sizeof(CompletionElement));
|
|
el->value = g_strdup(current_line);
|
|
el->description = NULL;
|
|
el->next = NULL;
|
|
|
|
if(element_counter++ != 0)
|
|
last_element->next = el;
|
|
else
|
|
group->elements = el;
|
|
|
|
last_element = el;
|
|
}
|
|
|
|
free(current_line);
|
|
current_line = NULL;
|
|
count = 0;
|
|
}
|
|
}
|
|
|
|
pclose(fp);
|
|
|
|
return completion;
|
|
}
|
|
|
|
Completion*
|
|
cc_set(char* input)
|
|
{
|
|
/* init completion group */
|
|
Completion *completion = malloc(sizeof(Completion));
|
|
CompletionGroup* group = malloc(sizeof(CompletionGroup));
|
|
|
|
group->value = NULL;
|
|
group->next = NULL;
|
|
group->elements = NULL;
|
|
|
|
completion->groups = group;
|
|
CompletionElement *last_element = NULL;
|
|
int element_counter = 0;
|
|
int i = 0;
|
|
int input_length = input ? strlen(input) : 0;
|
|
|
|
for(i = 0; i < LENGTH(settings); i++)
|
|
{
|
|
if( (input_length <= strlen(settings[i].name)) &&
|
|
!strncmp(input, settings[i].name, input_length) )
|
|
{
|
|
CompletionElement* el = malloc(sizeof(CompletionElement));
|
|
el->value = settings[i].name;
|
|
el->description = settings[i].description;
|
|
el->next = NULL;
|
|
|
|
if(element_counter++ != 0)
|
|
last_element->next = el;
|
|
else
|
|
group->elements = el;
|
|
|
|
last_element = el;
|
|
}
|
|
}
|
|
|
|
return completion;
|
|
}
|
|
|
|
/* buffer command implementation */
|
|
void
|
|
bcmd_goto(char* buffer, Argument* argument)
|
|
{
|
|
int b_length = strlen(buffer);
|
|
if(b_length < 1)
|
|
return;
|
|
|
|
if(!strcmp(buffer, "gg"))
|
|
set_page(0);
|
|
else if(!strcmp(buffer, "G"))
|
|
set_page(Zathura.PDF.number_of_pages - 1);
|
|
else
|
|
set_page(atoi(g_strndup(buffer, b_length - 1)) - 1);
|
|
|
|
update_status();
|
|
}
|
|
|
|
void
|
|
bcmd_zoom(char* buffer, Argument* argument)
|
|
{
|
|
pthread_mutex_lock(&(Zathura.Lock.scale_lock));
|
|
if(argument->n == ZOOM_IN)
|
|
{
|
|
if((Zathura.PDF.scale + ZOOM_STEP) <= ZOOM_MAX)
|
|
Zathura.PDF.scale += ZOOM_STEP;
|
|
}
|
|
else if(argument->n == ZOOM_OUT)
|
|
{
|
|
if((Zathura.PDF.scale - ZOOM_STEP) >= ZOOM_MIN)
|
|
Zathura.PDF.scale -= ZOOM_STEP;
|
|
}
|
|
else
|
|
Zathura.PDF.scale = 100;
|
|
pthread_mutex_unlock(&(Zathura.Lock.scale_lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.render_lock));
|
|
if(Zathura.PDF.render_thread)
|
|
pthread_cancel(Zathura.PDF.render_thread);
|
|
|
|
intptr_t t = Zathura.PDF.page_number;
|
|
pthread_create(&(Zathura.PDF.render_thread), NULL, render, (gpointer) t);
|
|
pthread_mutex_unlock(&(Zathura.Lock.render_lock));
|
|
update_status();
|
|
}
|
|
|
|
/* special command implementation */
|
|
gboolean
|
|
scmd_search(char* input, Argument* argument)
|
|
{
|
|
printf("%s\n", input);
|
|
argument->data = input;
|
|
sc_search(argument);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* callback implementation */
|
|
gboolean
|
|
cb_destroy(GtkWidget* widget, gpointer data)
|
|
{
|
|
pango_font_description_free(Zathura.Style.font);
|
|
|
|
if(Zathura.PDF.document)
|
|
cmd_close(0, NULL);
|
|
|
|
/* mutexes */
|
|
pthread_mutex_destroy(&(Zathura.Lock.scale_lock));
|
|
pthread_mutex_destroy(&(Zathura.Lock.rotate_lock));
|
|
pthread_mutex_destroy(&(Zathura.Lock.render_lock));
|
|
pthread_mutex_destroy(&(Zathura.Lock.search_lock));
|
|
pthread_mutex_destroy(&(Zathura.Lock.viewport_lock));
|
|
|
|
gtk_main_quit();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean cb_draw(GtkWidget* widget, GdkEventExpose* expose, gpointer data)
|
|
{
|
|
intptr_t t = (intptr_t) data;
|
|
int page_id = (int) t;
|
|
|
|
if(page_id < 0 || page_id > Zathura.PDF.number_of_pages)
|
|
return FALSE;
|
|
|
|
gdk_window_clear(widget->window);
|
|
cairo_t *cairo = gdk_cairo_create(widget->window);
|
|
|
|
double page_width, page_height, width, height;
|
|
pthread_mutex_lock(&(Zathura.Lock.scale_lock));
|
|
double scale = ((double) Zathura.PDF.scale / 100.0);
|
|
pthread_mutex_unlock(&(Zathura.Lock.scale_lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[page_id]->lock));
|
|
poppler_page_get_size(Zathura.PDF.pages[page_id]->page, &page_width, &page_height);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[page_id]->lock));
|
|
|
|
pthread_mutex_lock(&(Zathura.Lock.rotate_lock));
|
|
if(Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180)
|
|
{
|
|
width = page_width * scale;
|
|
height = page_height * scale;
|
|
}
|
|
else
|
|
{
|
|
width = page_height * scale;
|
|
height = page_width * scale;
|
|
}
|
|
pthread_mutex_unlock(&(Zathura.Lock.rotate_lock));
|
|
|
|
int window_x, window_y;
|
|
gdk_drawable_get_size(widget->window, &window_x, &window_y);
|
|
|
|
int offset_x, offset_y;
|
|
|
|
if (window_x > width)
|
|
offset_x = (window_x - width) / 2;
|
|
else
|
|
offset_x = 0;
|
|
|
|
if (window_y > height)
|
|
offset_y = (window_y - height) / 2;
|
|
else
|
|
offset_y = 0;
|
|
|
|
|
|
pthread_mutex_lock(&(Zathura.PDF.pages[page_id]->lock));
|
|
cairo_set_source_surface(cairo, Zathura.PDF.pages[page_id]->surface, offset_x, offset_y);
|
|
cairo_paint(cairo);
|
|
cairo_destroy(cairo);
|
|
pthread_mutex_unlock(&(Zathura.PDF.pages[page_id]->lock));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cb_view_kb_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
|
|
{
|
|
int i;
|
|
for(i = 0; i < LENGTH(shortcuts); i++)
|
|
{
|
|
if (event->keyval == shortcuts[i].key &&
|
|
(((event->state & shortcuts[i].mask) == shortcuts[i].mask) || shortcuts[i].mask == 0)
|
|
&& (Zathura.Global.mode == shortcuts[i].mode || shortcuts[i].mode == -1))
|
|
{
|
|
shortcuts[i].function(&(shortcuts[i].argument));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* append only numbers and characters to buffer */
|
|
if( (event->keyval >= 0x30) && (event->keyval <= 0x7A))
|
|
{
|
|
if(!Zathura.Global.buffer)
|
|
Zathura.Global.buffer = g_string_new("");
|
|
|
|
Zathura.Global.buffer = g_string_append_c(Zathura.Global.buffer, event->keyval);
|
|
gtk_label_set_markup((GtkLabel*) Zathura.Global.status_buffer, Zathura.Global.buffer->str);
|
|
}
|
|
|
|
/* search buffer commands */
|
|
if(Zathura.Global.buffer)
|
|
{
|
|
for(i = 0; i < LENGTH(buffer_commands); i++)
|
|
{
|
|
regex_t regex;
|
|
int status;
|
|
|
|
regcomp(®ex, buffer_commands[i].regex, REG_EXTENDED);
|
|
status = regexec(®ex, Zathura.Global.buffer->str, (size_t) 0, NULL, 0);
|
|
regfree(®ex);
|
|
|
|
if(status == 0)
|
|
{
|
|
buffer_commands[i].function(Zathura.Global.buffer->str, &(buffer_commands[i].argument));
|
|
g_string_free(Zathura.Global.buffer, TRUE);
|
|
Zathura.Global.buffer = NULL;
|
|
gtk_label_set_markup((GtkLabel*) Zathura.Global.status_buffer, "");
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
cb_index_selection_changed(GtkTreeSelection* treeselection, GtkWidget* action_view)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
if(gtk_tree_selection_get_selected(treeselection, &model, &iter))
|
|
{
|
|
PopplerAction* action;
|
|
PopplerDest* destination;
|
|
|
|
gtk_tree_model_get(model, &iter, 1, &action, -1);
|
|
|
|
if(action->type == POPPLER_ACTION_GOTO_DEST)
|
|
{
|
|
destination = action->goto_dest.dest;
|
|
|
|
if(destination->type == POPPLER_DEST_NAMED)
|
|
{
|
|
destination = poppler_document_find_dest(Zathura.PDF.document, destination->named_dest);
|
|
|
|
if(destination)
|
|
{
|
|
set_page(destination->page_num - 1);
|
|
update_status();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
cb_inputbar_kb_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
|
|
{
|
|
int i;
|
|
|
|
/* inputbar shortcuts */
|
|
for(i = 0; i < LENGTH(inputbar_shortcuts); i++)
|
|
{
|
|
if(event->keyval == inputbar_shortcuts[i].key &&
|
|
(((event->state & inputbar_shortcuts[i].mask) == inputbar_shortcuts[i].mask)
|
|
|| inputbar_shortcuts[i].mask == 0))
|
|
{
|
|
inputbar_shortcuts[i].function(&(inputbar_shortcuts[i].argument));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* special commands */
|
|
char identifier = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 0, 1)[0];
|
|
for(i = 0; i < LENGTH(special_commands); i++)
|
|
{
|
|
if((identifier == special_commands[i].identifier) &&
|
|
(special_commands[i].always == 1))
|
|
{
|
|
gchar *input = gtk_editable_get_chars(GTK_EDITABLE(Zathura.UI.inputbar), 1, -1);
|
|
special_commands[i].function(input, &(special_commands[i].argument));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
cb_inputbar_activate(GtkEntry* entry, gpointer data)
|
|
{
|
|
gchar *input = gtk_editable_get_chars(GTK_EDITABLE(entry), 1, -1);
|
|
gchar **tokens = g_strsplit(input, " ", -1);
|
|
gchar *command = tokens[0];
|
|
int length = g_strv_length(tokens);
|
|
int i = 0;
|
|
gboolean retv = FALSE;
|
|
gboolean succ = FALSE;
|
|
|
|
/* no input */
|
|
if(length < 1)
|
|
{
|
|
isc_abort(NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
/* append input to the command history */
|
|
Zathura.Global.history = g_list_append(Zathura.Global.history, g_strdup(gtk_entry_get_text(entry)));
|
|
|
|
/* special commands */
|
|
char identifier = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, 1)[0];
|
|
for(i = 0; i < LENGTH(special_commands); i++)
|
|
{
|
|
if(identifier == special_commands[i].identifier)
|
|
{
|
|
retv = special_commands[i].function(input, &(special_commands[i].argument));
|
|
if(retv) isc_abort(NULL);
|
|
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* search commands */
|
|
for(i = 0; i < LENGTH(commands); i++)
|
|
{
|
|
if((g_strcmp0(command, commands[i].command) == 0) ||
|
|
(g_strcmp0(command, commands[i].abbr) == 0))
|
|
{
|
|
retv = commands[i].function(length - 1, tokens + 1);
|
|
succ = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(retv)
|
|
isc_abort(NULL);
|
|
|
|
if(!succ)
|
|
notify(ERROR, "Unknown command.");
|
|
|
|
Argument arg = { HIDE };
|
|
isc_completion(&arg);
|
|
gtk_widget_grab_focus(GTK_WIDGET(Zathura.UI.view));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* main function */
|
|
int main(int argc, char* argv[])
|
|
{
|
|
gtk_init(&argc, &argv);
|
|
|
|
init_zathura();
|
|
update_status();
|
|
|
|
if(argc >= 2)
|
|
cmd_open(2, &argv[1]);
|
|
|
|
gtk_widget_show_all(GTK_WIDGET(Zathura.UI.window));
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|