diff --git a/config.h b/config.h index 380bc6e..d8c4e3d 100644 --- a/config.h +++ b/config.h @@ -1,28 +1,34 @@ /* settings */ -static const int SHOW_NOTIFICATION = 5; static const float ZOOM_STEP = 0.1; static const float SCROLL_STEP = 40; +static const float HL_TRANSPARENCY = 0.4; +static const int SHOW_NOTIFICATION = 5; +static const int DEFAULT_WIDTH = 800; +static const int DEFAULT_HEIGHT = 600; +static const char BROWSER[] = "firefox %s"; /* look */ static const char font[] = "monospace normal 9"; -static const char default_bgcolor[] = "#141414"; -static const char default_fgcolor[] = "#CCCCCC"; +static const char default_bgcolor[] = "#000000"; +static const char default_fgcolor[] = "#DDDDDD"; static const char notification_fgcolor[] = "#0F0F0F"; static const char notification_bgcolor[] = "#F9F9F9"; -static const char notification_warning_fgcolor[] = "2222222"; -static const char notification_warning_bgcolor[] = "#E8E62E"; -static const char notification_error_fgcolor[] = "#CCCCCC"; -static const char notification_error_bgcolor[] = "#B82121"; +static const char notification_warning_fgcolor[] = "DDDDDDC"; +static const char notification_warning_bgcolor[] = "#D0C54D"; +static const char notification_error_fgcolor[] = "#FFFFFF"; +static const char notification_error_bgcolor[] = "#BC2800"; static const char inputbar_bgcolor[] = "#141414"; static const char inputbar_fgcolor[] = "#9FBC00"; -static const char completion_fgcolor[] = "#CCCCCC"; +static const char completion_fgcolor[] = "#DDDDDD"; static const char completion_bgcolor[] = "#232323"; static const char completion_hl_fgcolor[] = "#232323"; static const char completion_hl_bgcolor[] = "#9FBC00"; +static const char search_highlight[] = "#9FBC00"; + /* shortcuts */ Shortcut shortcuts[] = { // mask, key, function, argument @@ -56,14 +62,12 @@ Shortcut shortcuts[] = { /* commands */ Command commands[] = { // command, function - {"g", cmd_goto}, + {"export", cmd_export}, {"goto", cmd_goto}, - {"o", cmd_open}, + {"info", cmd_info}, + {"links", cmd_links}, {"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 a07f1ef..bdfa90a 100644 --- a/zathura.c +++ b/zathura.c @@ -41,6 +41,7 @@ struct GdkColor completion_bg; GdkColor completion_hl_fg; GdkColor completion_hl_bg; + GdkColor search_highlight; PangoFontDescription *font; } Settings; @@ -84,11 +85,14 @@ void update_title(); void update_status(); void set_page(int); void draw(); -void hilight_result(PopplerRectangle*); +void highlight_result(PopplerRectangle*); +void open_link(char*); gboolean complete(Argument*); gboolean delete_widget(gpointer); -GtkLabel* notify(int, char*); + +GtkWidget* notify(int, char*); +GtkWidget* update_notification(GtkWidget*, int, char*); /* shortcut declarations */ void sc_focus_inputbar(Argument*); @@ -102,7 +106,10 @@ void sc_print(Argument*); void sc_quit(Argument*); /* command declarations */ +void cmd_export(int, char**); void cmd_goto(int, char**); +void cmd_info(int, char**); +void cmd_links(int, char**); void cmd_open(int, char**); void cmd_rotate(int, char**); void cmd_quit(int, char**); @@ -113,7 +120,9 @@ void cb_draw(GtkWidget*, gpointer); void cb_destroy(GtkWidget*, gpointer); void cb_inputbar_activate(GtkEntry*, gpointer); void cb_inputbar_button_pressed(GtkWidget*, GdkEventButton*, gpointer); +void cb_drawing_area_button_pressed(GtkWidget*, GdkEventButton*, gpointer); +gboolean cb_label_open_link(GtkLabel*, gchar*, gpointer); gboolean cb_inputbar_key_pressed(GtkEntry*, GdkEventKey*, gpointer); gboolean cb_inputbar_key_released(GtkEntry*, GdkEventKey*, gpointer); gboolean cb_view_key_pressed(GtkWidget*, GdkEventKey*, gpointer); @@ -140,6 +149,7 @@ init() gdk_color_parse(completion_bgcolor, &(Zathura.Settings.completion_bg)); gdk_color_parse(completion_hl_fgcolor, &(Zathura.Settings.completion_hl_fg)); gdk_color_parse(completion_hl_bgcolor, &(Zathura.Settings.completion_hl_bg)); + gdk_color_parse(search_highlight, &(Zathura.Settings.search_highlight)); Zathura.Settings.font = pango_font_description_from_string(font); /* variables */ @@ -154,15 +164,17 @@ init() /* window */ 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); + gtk_window_set_default_size(Zathura.window, DEFAULT_WIDTH, DEFAULT_HEIGHT); + g_signal_connect(G_OBJECT(Zathura.window), "destroy", G_CALLBACK(cb_destroy), NULL); /* box */ gtk_box_set_spacing(Zathura.box, 0); 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); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(Zathura.view), + Zathura.drawing_area); + gtk_viewport_set_shadow_type((GtkViewport*) gtk_bin_get_child(GTK_BIN(Zathura.view)), GTK_SHADOW_NONE); g_signal_connect(G_OBJECT(Zathura.view), "key-press-event", G_CALLBACK(cb_view_key_pressed), NULL); #ifdef SHOW_SCROLLBARS @@ -172,7 +184,11 @@ init() #endif /* drawing area */ - g_signal_connect(G_OBJECT(Zathura.drawing_area), "expose-event", G_CALLBACK(cb_draw), NULL); + g_signal_connect(G_OBJECT(Zathura.drawing_area), "expose-event", G_CALLBACK(cb_draw), NULL); + g_signal_connect(G_OBJECT(Zathura.drawing_area), "button-press-event", G_CALLBACK(cb_drawing_area_button_pressed), NULL); + gtk_widget_set_events(Zathura.drawing_area, GDK_BUTTON_PRESS_MASK); + gtk_widget_modify_bg(GTK_WIDGET(Zathura.drawing_area), GTK_STATE_NORMAL, &(Zathura.Settings.default_bg)); + /* inputbar */ gtk_entry_set_inner_border(Zathura.inputbar, NULL); @@ -203,12 +219,28 @@ update_title() gtk_window_set_title(GTK_WINDOW(Zathura.window), "zathura"); } -GtkLabel* +GtkWidget* notify(int level, char* text) { - GtkLabel *message = GTK_LABEL(gtk_label_new(text)); GtkEventBox *view = GTK_EVENT_BOX(gtk_event_box_new()); + + update_notification(GTK_WIDGET(view), level, text); + + gtk_container_add(GTK_CONTAINER(Zathura.notification), GTK_WIDGET(view)); + gtk_widget_show_all(GTK_WIDGET(Zathura.window)); + g_timeout_add_seconds(SHOW_NOTIFICATION, delete_widget, GTK_WIDGET(GTK_WIDGET(view))); + + return GTK_WIDGET(view); +} + +GtkWidget* +update_notification(GtkWidget* view, int level, char* text) +{ + GtkLabel *message = GTK_LABEL(gtk_label_new("")); + gtk_label_set_markup(message, text); + g_signal_connect(G_OBJECT(message), "activate-link", G_CALLBACK(cb_label_open_link), NULL); + gtk_misc_set_alignment(GTK_MISC(message), 0, 0); gtk_widget_modify_font(GTK_WIDGET(message), Zathura.Settings.font); @@ -229,13 +261,12 @@ notify(int level, char* text) } + if(gtk_bin_get_child(GTK_BIN(view))) + gtk_container_remove(GTK_CONTAINER(view), gtk_bin_get_child(GTK_BIN(view))); + gtk_container_add(GTK_CONTAINER(view), GTK_WIDGET(message)); - gtk_container_add(GTK_CONTAINER(Zathura.notification), GTK_WIDGET(view)); - gtk_widget_show_all(GTK_WIDGET(Zathura.window)); - g_timeout_add_seconds(SHOW_NOTIFICATION, delete_widget, GTK_WIDGET(GTK_WIDGET(view))); - - return message; + return GTK_WIDGET(view); } void @@ -244,11 +275,7 @@ update_status() char* text = ""; if(Zathura.PDF.document && Zathura.PDF.page) - { - char* show_page = g_strdup_printf("[%i/%i]", Zathura.PDF.page_number + 1, - Zathura.PDF.number_of_pages); - text = g_strdup_printf("%s %s", show_page, Zathura.PDF.file); - } + text = g_strdup_printf("[%i/%i] %s", Zathura.PDF.page_number + 1, Zathura.PDF.number_of_pages, Zathura.PDF.file); gtk_entry_set_text(Zathura.inputbar, text); } @@ -332,9 +359,44 @@ draw() } void -hilight_result(PopplerRectangle* rectangle) +highlight_result(PopplerRectangle* rectangle) { + double page_height, page_width; + double width, height; + double scale; + poppler_page_get_size(Zathura.PDF.page, &page_width, &page_height); + scale = Zathura.PDF.scale; + width = (rectangle->x2 - rectangle->x1) * scale; + height = (rectangle->y2 - rectangle->y1) * scale; + + cairo_t *cairo = cairo_create(Zathura.PDF.surface); + cairo_set_source_rgba(cairo, Zathura.Settings.search_highlight.red, Zathura.Settings.search_highlight.green, + Zathura.Settings.search_highlight.blue, HL_TRANSPARENCY); + + switch(Zathura.PDF.rotate) + { + case 90: + cairo_rectangle(cairo, scale * rectangle->y1, scale * rectangle->x1, height, width); + break; + case 180: + cairo_rectangle(cairo, scale * (page_width - rectangle->x1) - width, scale * rectangle->y1, width, height); + break; + case 270: + cairo_rectangle(cairo, scale * (page_height - rectangle->y1) - height, scale * (page_width - rectangle->x1) - width, height, width); + break; + default: + cairo_rectangle(cairo, scale * rectangle->x1, scale * (page_height - rectangle->y1) - height, width, height); + } + + cairo_fill(cairo); +} + +void +open_link(char* link) +{ + char* start_browser = g_strdup_printf(BROWSER, link); + system(start_browser); } gboolean @@ -573,6 +635,7 @@ sc_search(Argument *argument) GList* results; GList* list; + if(argument->data) search_item = (char*) argument->data; @@ -580,7 +643,8 @@ sc_search(Argument *argument) return; /* search document */ - GtkLabel* search_status = notify(DEFAULT, "Searching..."); + GtkWidget* search_status = notify(DEFAULT, "Searching..."); + if(argument->n) direction = (argument->n == BACKWARD) ? -1 : 1; @@ -599,17 +663,16 @@ sc_search(Argument *argument) { draw(); - gtk_label_set_text(search_status, g_strdup_printf("%s found on page %i for %i time(s)", - search_item, Zathura.PDF.page_number, g_list_length(results))); + update_notification(GTK_WIDGET(search_status), DEFAULT, g_strdup_printf("%s found on page %i for %i time(s)", + search_item, Zathura.PDF.page_number, g_list_length(results))); for(list = results; list && list->data; list = g_list_next(list)) { - hilight_result((PopplerRectangle*) list->data); + highlight_result((PopplerRectangle*) list->data); } } else - gtk_label_set_text(search_status, g_strdup_printf("No match for %s", search_item)); - + update_notification(search_status, DEFAULT, g_strdup_printf("No match for %s", search_item)); } void @@ -653,14 +716,35 @@ cmd_open(int argc, char** argv) Zathura.PDF.number_of_pages = poppler_document_get_n_pages(Zathura.PDF.document); Zathura.PDF.file = file + strlen("file://"); - set_page(0); + set_page(0); draw(); update_status(); update_title(); } +void +cmd_export(int argc, char** argv) +{ + if(argc == 0) + return; + + if(strcmp(argv[0], "image") == 0) + { + + } + else if(strcmp(argv[0], "links") == 0) + { + + } + else if(strcmp(argv[0], "attachments") == 0) + { + + } + +} + void cmd_goto(int argc, char** argv) { @@ -679,6 +763,79 @@ cmd_goto(int argc, char** argv) update_status(); } +void +cmd_info(int argc, char** argv) +{ + if(!Zathura.PDF.document) + return; + + gchar *info; + 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); + + info = g_strconcat( + "Title: ", title ? title : "", "\n", + "Author: ", author ? author : "", "\n", + "Subject: ", subject ? subject : "", "\n", + "Keywords: ", keywords ? keywords : "", "\n", + "Creator: ", creator ? creator : "", "\n", + "Producer: ", producer ? producer : "", + NULL); + + notify(DEFAULT, info); +} + +void +cmd_links(int argc, char** argv) +{ + if(!Zathura.PDF.document && !Zathura.PDF.page) + return; + + GList *link_list; + GList *links; + int number_of_links; + + link_list = poppler_page_get_link_mapping(Zathura.PDF.page); + number_of_links = g_list_length(link_list); + + if(number_of_links > 0) + notify(DEFAULT, g_strdup_printf("%d links found", number_of_links)); + else + notify(WARNING, "No links found"); + + for(links = link_list; links; links = g_list_next(links)) + { + PopplerLinkMapping *link_mapping; + PopplerAction *action; + + link_mapping = (PopplerLinkMapping*) links->data; + action = poppler_action_copy(link_mapping->action); + + if(action->type == POPPLER_ACTION_URI) + { + char* link_name = poppler_page_get_text(Zathura.PDF.page, POPPLER_SELECTION_WORD, &link_mapping->area); + link_name = g_strdup_printf("%s", action->uri.uri, link_name); + + notify(DEFAULT, g_strdup_printf("%s: %s", link_name, action->uri.uri)); + } + } + + poppler_page_free_link_mapping(link_list); +} + void cmd_rotate(int argc, char** argv) { @@ -772,7 +929,6 @@ cb_inputbar_activate(GtkEntry* entry, gpointer data) { Argument argument; argument.data = (char*) gtk_entry_get_text(entry) + 1; - argument.n = -1; sc_search(&argument); g_strfreev(tokens); update_status(); @@ -812,6 +968,56 @@ cb_inputbar_button_pressed(GtkWidget* widget, GdkEventButton* event, gpointer da } } +void +cb_drawing_area_button_pressed(GtkWidget* widget, GdkEventButton* event, gpointer data) +{ + if(!Zathura.PDF.document && !Zathura.PDF.page) + return; + + // left click + if(event->button == 1) + { + // check for links + GList *link_list = poppler_page_get_link_mapping(Zathura.PDF.page); + GList *links; + int number_of_links = g_list_length(link_list); + + if(number_of_links <= 0) + return; + + for(links = link_list; links; links = g_list_next(links)) + { + PopplerLinkMapping *link_mapping = (PopplerLinkMapping*) links->data; + PopplerAction *action = poppler_action_copy(link_mapping->action); + + if(action->type == POPPLER_ACTION_URI) + { + double page_width, page_height; + poppler_page_get_size(Zathura.PDF.page, &page_width, &page_height); + + // check if click is in url area + if( (link_mapping->area.x1 <= event->x) + && (link_mapping->area.x2 >= event->x) + && (link_mapping->area.y1 <= (page_height - event->y)) + && (link_mapping->area.y2 >= (page_height - event->y)) + ) + { + open_link(action->uri.uri); + } + } + } + + poppler_page_free_link_mapping(link_list); + } +} + +gboolean +cb_label_open_link(GtkLabel* label, gchar* link, gpointer data) +{ + open_link(link); + return TRUE; +} + gboolean cb_inputbar_key_pressed(GtkEntry* entry, GdkEventKey *event, gpointer data) { @@ -821,6 +1027,7 @@ cb_inputbar_key_pressed(GtkEntry* entry, GdkEventKey *event, gpointer data) { case GDK_Escape: gtk_widget_grab_focus(GTK_WIDGET(Zathura.view)); + update_status(); argument.n = HIDE; return complete(&argument); case GDK_Tab: @@ -850,7 +1057,6 @@ cb_inputbar_key_released(GtkEntry *entry, GdkEventKey *event, gpointer data) { Argument argument; argument.data = (char*) text + 1; - argument.n = -1; sc_search(&argument); gtk_widget_grab_focus(GTK_WIDGET(Zathura.inputbar)); gtk_editable_set_position(GTK_EDITABLE(Zathura.inputbar), -1);