From 2754798c73cb520f8aab1c4e32cc1e8dfb086a02 Mon Sep 17 00:00:00 2001 From: Moritz Lipp Date: Wed, 12 Aug 2009 21:45:49 +0200 Subject: [PATCH] Basic PDF functionality With this commit Zathura gains PDF refered functionality and therefore several shortcuts and commands Added following functionality: * Open PDF file (via command or argument) * Scroll on the current page * Thumb through the PDF file * Rotate PDF page * Zoom in and out In progess: * Search PDF * Better notification system --- Makefile | 5 +- config.h | 29 ++++- zathura.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 370 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index b95cc67..7f53c80 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ LIBS = gtk+-2.0 poppler poppler-glib -FLAGS = `pkg-config --cflags --libs $(LIBS)` +FLAGS = `pkg-config --cflags --libs $(LIBS)` SOURCE = zathura.c TARGET = zathura @@ -17,6 +17,9 @@ debug: $(TARGET) valgrind: debug $(TARGET) valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./${TARGET} +scrollbars: $(TARGET) + gcc $(FLAGS) -Wall -o $(TARGET) $(SOURCE) -DSHOW_SCROLLBARS + install: all @echo installing executeable to /usr/bin @mkdir -p /usr/bin diff --git a/config.h b/config.h index 3d91ce7..c9e8d68 100644 --- a/config.h +++ b/config.h @@ -1,5 +1,5 @@ -static const float zoom_step = 0.1; -static const float scroll_step = 40; +static const float ZOOM_STEP = 0.1; +static const float SCROLL_STEP = 40; static const char font[] = "monospace normal 9"; static const char default_bgcolor[] = "#141414"; @@ -17,15 +17,38 @@ static const char completion_hl_bgcolor[] = "#9FBC00"; Shortcut shortcuts[] = { // mask, key, function, argument + {GDK_CONTROL_MASK, GDK_f, sc_navigate, { NEXT } }, + {GDK_CONTROL_MASK, GDK_b, sc_navigate, { PREVIOUS } }, + {GDK_CONTROL_MASK, GDK_plus, sc_zoom, { ZOOM_IN } }, + {GDK_CONTROL_MASK, GDK_minus, sc_zoom, { ZOOM_OUT } }, + {GDK_CONTROL_MASK, GDK_0, sc_zoom, { ZOOM_ORIGINAL } }, + {GDK_CONTROL_MASK, GDK_r, sc_rotate, { RIGHT } }, + {GDK_CONTROL_MASK, GDK_e, sc_rotate, { LEFT } }, + {GDK_CONTROL_MASK, GDK_q, sc_quit, {0} }, + {0, GDK_h, sc_scroll, { LEFT } }, + {0, GDK_j, sc_scroll, { DOWN } }, + {0, GDK_k, sc_scroll, { UP } }, + {0, GDK_l, sc_scroll, { RIGHT } }, + {0, GDK_i, sc_adjust_window, { ADJUST_BESTFIT } }, + {0, GDK_u, sc_adjust_window, { ADJUST_WIDTH } }, {0, GDK_colon, sc_focus_inputbar, { .data = ":" } }, {0, GDK_o, sc_focus_inputbar, { .data = ":open " } }, - {GDK_CONTROL_MASK, GDK_q, sc_quit, {0} }, + {0, GDK_r, sc_focus_inputbar, { .data = ":rotate " } }, + {0, GDK_z, sc_focus_inputbar, { .data = ":zoom " } }, + {0, GDK_g, sc_focus_inputbar, { .data = ":goto " } }, + {0, GDK_slash, sc_focus_inputbar, { .data = "/" } }, }; Command commands[] = { // command, function + {"g", cmd_goto}, + {"goto", cmd_goto}, {"o", cmd_open}, {"open", cmd_open}, + {"r", cmd_rotate}, + {"rotate", cmd_rotate}, {"q", cmd_quit}, {"quit", cmd_quit}, + {"z", cmd_zoom}, + {"zoom", cmd_zoom}, }; diff --git a/zathura.c b/zathura.c index 3ac502a..56cdde3 100644 --- a/zathura.c +++ b/zathura.c @@ -1,6 +1,9 @@ #include #include +#include +#include + #include #include @@ -8,13 +11,16 @@ #define LENGTH(x) sizeof(x)/sizeof((x)[0]) /* enums */ -enum { NEXT, PREVIOUS, HIDE, WARNING, DEFAULT }; +enum { UP, DOWN, LEFT, RIGHT, ZOOM_IN, ZOOM_OUT, ZOOM_ORIGINAL, + NEXT, PREVIOUS, HIDE, ERROR, WARNING, DEFAULT, + ADJUST_BESTFIT, ADJUST_WIDTH }; struct { GtkWindow *window; GtkBox *box; GtkScrolledWindow *view; + GtkWidget *drawing_area; GtkEntry *inputbar; struct @@ -31,6 +37,18 @@ struct GdkColor completion_hl_bg; PangoFontDescription *font; } Settings; + + struct + { + PopplerDocument *document; + PopplerPage *page; + int page_number; + double scale; + int rotate; + cairo_surface_t *surface; + char *file; + } PDF; + } Zathura; typedef struct @@ -57,18 +75,30 @@ typedef struct void init(); void update_title(); void update_status(int, char*); +void set_page(int); +void draw(); gboolean complete(Argument*); /* shortcut declarations */ void sc_focus_inputbar(Argument*); +void sc_scroll(Argument*); +void sc_navigate(Argument*); +void sc_zoom(Argument*); +void sc_adjust_window(Argument*); +void sc_rotate(Argument*); void sc_quit(Argument*); /* command declarations */ +void cmd_goto(int, char**); void cmd_open(int, char**); +void cmd_rotate(int, char**); +void cmd_search(int, char**); void cmd_quit(int, char**); +void cmd_zoom(int, char**); /* callbacks declarations */ +void cb_draw(GtkWidget*, gpointer); void cb_destroy(GtkWidget*, gpointer); void cb_inputbar_activate(GtkEntry*, gpointer); void cb_inputbar_button_pressed(GtkWidget*, GdkEventButton*, gpointer); @@ -98,13 +128,17 @@ init() Zathura.Settings.font = pango_font_description_from_string(font); /* variables */ - Zathura.window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); - Zathura.box = GTK_BOX(gtk_vbox_new(FALSE, 0)); - Zathura.view = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); - Zathura.inputbar = GTK_ENTRY(gtk_entry_new()); + Zathura.window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + Zathura.box = GTK_BOX(gtk_vbox_new(FALSE, 0)); + Zathura.view = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); + Zathura.drawing_area = gtk_drawing_area_new(); + Zathura.inputbar = GTK_ENTRY(gtk_entry_new()); + Zathura.PDF.scale = 1.0; + Zathura.PDF.rotate = 0; /* window */ - gtk_window_set_title(Zathura.window, "title"); + gtk_window_set_title(Zathura.window, "zathura"); + gtk_window_set_default_size(Zathura.window, 800, 600); g_signal_connect(G_OBJECT(Zathura.window), "destroy", G_CALLBACK(cb_destroy), NULL); /* box */ @@ -112,8 +146,12 @@ init() gtk_container_add(GTK_CONTAINER(Zathura.window), GTK_WIDGET(Zathura.box)); /* view */ + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(Zathura.view), Zathura.drawing_area); g_signal_connect(G_OBJECT(Zathura.view), "key-press-event", G_CALLBACK(cb_view_key_pressed), NULL); + /* drawing area */ + g_signal_connect(G_OBJECT(Zathura.drawing_area), "expose-event", G_CALLBACK(cb_draw), NULL); + #ifdef SHOW_SCROLLBARS gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(Zathura.view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); #else @@ -142,7 +180,10 @@ init() void update_title() { - + if(Zathura.PDF.file != NULL) + gtk_window_set_title(GTK_WINDOW(Zathura.window), Zathura.PDF.file + strlen("file://")); + else + gtk_window_set_title(GTK_WINDOW(Zathura.window), "zathura"); } void @@ -159,9 +200,98 @@ update_status(int level, char* text) gtk_widget_modify_base(GTK_WIDGET(Zathura.inputbar), GTK_STATE_NORMAL, &(Zathura.Settings.inputbar_bg)); } + if(text == (char*) "") + { + if(Zathura.PDF.document && Zathura.PDF.page) + { + char* show_page = g_strdup_printf("[%i/%i]", Zathura.PDF.page_number + 1, + poppler_document_get_n_pages(Zathura.PDF.document)); + char* filename = Zathura.PDF.file + strlen("file://"); + text = g_strdup_printf("%s %s", show_page, filename); + } + } + gtk_entry_set_text(Zathura.inputbar, text); } +void set_page(int page_number) +{ + if(page_number > poppler_document_get_n_pages(Zathura.PDF.document)) + { + update_status(WARNING, "Error: Could not open page"); + return; + } + + Zathura.PDF.page_number = page_number; + Zathura.PDF.page = poppler_document_get_page(Zathura.PDF.document, page_number); +} + +void +draw() +{ + if(!Zathura.PDF.document || !Zathura.PDF.page) + return; + + double page_width, page_height; + double width, height; + + if(Zathura.PDF.surface) + cairo_surface_destroy(Zathura.PDF.surface); + Zathura.PDF.surface = NULL; + + poppler_page_get_size(Zathura.PDF.page, &page_width, &page_height); + + if(Zathura.PDF.rotate == 0 || Zathura.PDF.rotate == 180) + { + width = page_width * Zathura.PDF.scale; + height = page_height * Zathura.PDF.scale; + } + else + { + width = page_height * Zathura.PDF.scale; + height = page_width * Zathura.PDF.scale; + } + + cairo_t *cairo; + Zathura.PDF.surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + cairo = cairo_create(Zathura.PDF.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(Zathura.PDF.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(Zathura.PDF.scale != 1.0) + cairo_scale(cairo, Zathura.PDF.scale, Zathura.PDF.scale); + + if(Zathura.PDF.rotate != 0) + cairo_rotate(cairo, Zathura.PDF.rotate * G_PI / 180.0); + + poppler_page_render(Zathura.PDF.page, cairo); + cairo_restore(cairo); + cairo_destroy(cairo); + + gtk_widget_set_size_request(Zathura.drawing_area, width, height); + gtk_widget_queue_draw(Zathura.drawing_area); +} + gboolean complete(Argument* argument) { @@ -287,7 +417,93 @@ sc_focus_inputbar(Argument *argument) gtk_widget_grab_focus(GTK_WIDGET(Zathura.inputbar)); gtk_editable_set_position(GTK_EDITABLE(Zathura.inputbar), -1); } + +void +sc_scroll(Argument *argument) +{ + GtkAdjustment* adjustment; + + if( (argument->n == LEFT) || (argument->n == RIGHT) ) + adjustment = gtk_scrolled_window_get_hadjustment(Zathura.view); + else + adjustment = gtk_scrolled_window_get_vadjustment(Zathura.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 + gtk_adjustment_set_value(adjustment, (value + SCROLL_STEP) > max ? max : (value + SCROLL_STEP)); +} + +void +sc_navigate(Argument *argument) +{ + int number_of_pages = poppler_document_get_n_pages(Zathura.PDF.document); + int new_page = Zathura.PDF.page_number; + + if(argument->n == NEXT) + new_page = abs( (new_page + number_of_pages + 1) % number_of_pages); + else if(argument->n == PREVIOUS) + new_page = abs( (new_page + number_of_pages - 1) % number_of_pages); + + Zathura.PDF.page = poppler_document_get_page(Zathura.PDF.document, new_page); + Zathura.PDF.page_number = new_page; + + update_status(DEFAULT, ""); + draw(); +} + +void +sc_zoom(Argument *argument) +{ + if(argument->n == ZOOM_IN) + Zathura.PDF.scale += ZOOM_STEP; + else if(argument->n == ZOOM_OUT) + Zathura.PDF.scale -= ZOOM_STEP; + else if(argument->n == ZOOM_ORIGINAL) + Zathura.PDF.scale = 1.0; + + draw(); +} + +void +sc_adjust_window(Argument *argument) +{ + GtkAdjustment* adjustment; + double view_size; + double page_width; + double page_height; + + if(argument->n == ADJUST_WIDTH) + adjustment = gtk_scrolled_window_get_vadjustment(Zathura.view); + else + adjustment = gtk_scrolled_window_get_hadjustment(Zathura.view); + + view_size = gtk_adjustment_get_page_size(adjustment); + poppler_page_get_size(Zathura.PDF.page, &page_width, &page_height); + + if(argument->n == ADJUST_WIDTH) + Zathura.PDF.scale = view_size / page_height; + else + Zathura.PDF.scale = view_size / page_width; + draw(); +} + +void +sc_rotate(Argument *argument) +{ + if(argument->n == LEFT) + Zathura.PDF.rotate = abs((Zathura.PDF.rotate - 90) % 360); + else if(argument->n == RIGHT) + Zathura.PDF.rotate = abs((Zathura.PDF.rotate + 90) % 360); + + draw(); +} + void sc_quit(Argument *argument) { @@ -298,7 +514,90 @@ sc_quit(Argument *argument) void cmd_open(int argc, char** argv) { + if(argc == 0 || (int) strlen(argv[0]) == 0) + return; + gchar* file; + + if(g_ascii_strncasecmp(argv[0], "file://", strlen("file://")) == 0) + file = g_strdup(argv[0]); + else + file = g_filename_to_uri(argv[0], NULL, NULL); + + Zathura.PDF.document = poppler_document_new_from_file(file, (argc == 2) ? argv[1] : NULL, NULL); + if(!Zathura.PDF.document) + { + printf("Error: Could not open file %s\n", file); + return; + } + + Zathura.PDF.file = file; + set_page(0); + + draw(); + + update_status(DEFAULT, ""); + update_title(); +} + +void +cmd_goto(int argc, char** argv) +{ + if(argc == 0) + return; + + int page = atoi(argv[0]);; + set_page(page); +} + +void +cmd_rotate(int argc, char** argv) +{ + Argument argument; + + if(argc == 0) + argument.n = RIGHT; + else + { + if(!strncmp(argv[0], "left", strlen(argv[0]) - 1)) + argument.n = LEFT; + else + argument.n = RIGHT; + } + + sc_rotate(&argument); +} + +void +cmd_search(int argc, char** argv) +{ + if(!Zathura.PDF.document || !Zathura.PDF.page || !argv[0]) + return; + + GList* results = poppler_page_find_text(Zathura.PDF.page, argv[0]); + GList* list; + + if(results) + { + for(list = results; list && list->data; list = g_list_next(list)) + { + //PopplerRectangle *rectangle = (PopplerRectangle*) list->data; + } + } +} + +void +cmd_zoom(int argc, char** argv) +{ + if(argc == 0) + return; + + int zoom = atoi(argv[0]); + if(zoom < 1 || zoom >= 1000) + return; + + Zathura.PDF.scale = (float) zoom / 100; + draw(); } void @@ -308,6 +607,19 @@ cmd_quit(int argc, char** argv) } /* callback implementations */ +void +cb_draw(GtkWidget *widget, gpointer data) +{ + gdk_window_clear(widget->window); + + draw(); + + cairo_t *cairo = gdk_cairo_create(widget->window); + cairo_set_source_surface(cairo, Zathura.PDF.surface, 0, 0); + cairo_paint(cairo); + cairo_destroy(cairo); +} + void cb_destroy(GtkWidget *widget, gpointer data) { @@ -320,7 +632,8 @@ cb_view_key_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 == 0)) + if (event->keyval == shortcuts[i].key && + (((event->state & shortcuts[i].mask) == shortcuts[i].mask) || shortcuts[i].mask == 0)) { shortcuts[i].function(&(shortcuts[i].argument)); return TRUE; @@ -347,7 +660,14 @@ cb_inputbar_activate(GtkEntry* entry, gpointer data) // special command if(*tokens[0] == '/') + { + char *term = (char*) gtk_entry_get_text(entry) + 1; + cmd_search(1, &term); + g_strfreev(tokens); + gtk_widget_grab_focus(GTK_WIDGET(Zathura.view)); + update_status(DEFAULT, ""); return; + } // other for(i = 0; i < LENGTH(commands); i++) @@ -407,7 +727,8 @@ cb_inputbar_key_pressed(GtkEntry* entry, GdkEventKey *event, gpointer data) gboolean cb_inputbar_key_released(GtkEntry *entry, GdkEventKey *event, gpointer data) { - int length = gtk_entry_get_text_length(entry); + int length = gtk_entry_get_text_length(entry); + char* text = (char*) gtk_entry_get_text(entry); Argument argument; if(!length) @@ -415,6 +736,13 @@ cb_inputbar_key_released(GtkEntry *entry, GdkEventKey *event, gpointer data) argument.n = HIDE; complete(&argument); } + else if(length > 1 && text[0] == '/') + { + char *term = text + 1; + cmd_search(1, &term); + gtk_widget_grab_focus(GTK_WIDGET(Zathura.inputbar)); + gtk_editable_set_position(GTK_EDITABLE(Zathura.inputbar), -1); + } return FALSE; } @@ -426,6 +754,9 @@ main(int argc, char* argv[]) gtk_init(&argc, &argv); init(); + + if(argc >= 2) + cmd_open(2, &argv[1]); gtk_widget_show_all(GTK_WIDGET(Zathura.window)); gtk_main();