X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;ds=sidebyside;f=shell%2Fev-view.c;h=589311ad85956e56333a004ea06ff65172f5ced3;hb=75eac88174e3a7843b702ffd38e06b0e4d3d62ac;hp=4e5db8b18b11ec5b4808e8c581c7abafb503fa1c;hpb=a521d16eb7f59cbda6881b66e0e7a00125377cd8;p=evince.git diff --git a/shell/ev-view.c b/shell/ev-view.c index 4e5db8b1..589311ad 100644 --- a/shell/ev-view.c +++ b/shell/ev-view.c @@ -29,6 +29,8 @@ #include "ev-marshal.h" #include "ev-view.h" #include "ev-document-find.h" +#include "ev-document-misc.h" +#include "ev-debug.h" #define EV_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_VIEW, EvViewClass)) #define EV_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_VIEW)) @@ -58,9 +60,22 @@ static const GtkTargetEntry targets[] = { typedef enum { EV_VIEW_CURSOR_NORMAL, EV_VIEW_CURSOR_LINK, - EV_VIEW_CURSOR_WAIT + EV_VIEW_CURSOR_WAIT, + EV_VIEW_CURSOR_HIDDEN } EvViewCursor; +#define ZOOM_IN_FACTOR 1.2 +#define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR) + +#define MIN_SCALE 0.05409 +#define MAX_SCALE 18.4884 + +/* FIXME: temporarily setting the epsilon very high until we figure out how to + * constrain the size of the window to a pixel width, instead of to the zoom + * level */ +#define ZOOM_EPSILON 1e-2 + + struct _EvView { GtkWidget parent_instance; @@ -85,8 +100,10 @@ struct _EvView { int find_page; int find_result; + int spacing; double scale; + EvSizingMode sizing_mode; }; struct _EvViewClass { @@ -182,6 +199,8 @@ ev_view_finalize (GObject *object) { EvView *view = EV_VIEW (object); + LOG ("Finalize"); + if (view->document) g_object_unref (view->document); @@ -205,21 +224,39 @@ ev_view_size_request (GtkWidget *widget, GtkRequisition *requisition) { EvView *view = EV_VIEW (widget); + GtkBorder border; + gint width, height; - if (GTK_WIDGET_REALIZED (widget)) { - if (view->document) { - ev_document_get_page_size (view->document, - &requisition->width, - &requisition->height); - } else { - requisition->width = 10; - requisition->height = 10; - } + if (! GTK_WIDGET_REALIZED (widget)) + return; - requisition->width += 2; - requisition->height += 2; + if (! view->document) { + requisition->width = 1; + requisition->height = 1; + return; + } + + ev_document_get_page_size (view->document, -1, + &width, &height); + ev_document_misc_get_page_border_size (width, height, &border); + + switch (view->sizing_mode) { + case EV_SIZING_BEST_FIT: + requisition->width = MIN_SCALE * ((float) width) / view->scale; + requisition->height = MIN_SCALE * ((float) height) / view->scale; + break; + case EV_SIZING_FIT_WIDTH: + requisition->width = MIN_SCALE * ((float) width) / view->scale; + requisition->height = height + border.top + border.bottom; + requisition->height += view->spacing * 2; + break; + case EV_SIZING_FREE: + requisition->width = width + border.left + border.right; + requisition->height = height + border.top + border.bottom; + requisition->width += view->spacing * 2; + requisition->height += view->spacing * 2; + break; } - } static void @@ -248,6 +285,7 @@ ev_view_realize (GtkWidget *widget) GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + attributes.window_type = GDK_WINDOW_CHILD; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); @@ -266,6 +304,7 @@ ev_view_realize (GtkWidget *widget) GDK_WA_VISUAL); gdk_window_set_user_data (widget->window, widget); widget->style = gtk_style_attach (widget->style, widget->window); + gdk_window_set_background (widget->window, &widget->style->mid[widget->state]); attributes.x = 0; attributes.y = 0; @@ -276,7 +315,8 @@ ev_view_realize (GtkWidget *widget) GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK | - GDK_POINTER_MOTION_MASK; + GDK_POINTER_MOTION_MASK | + GDK_LEAVE_NOTIFY_MASK; view->bin_window = gdk_window_new (widget->window, &attributes, @@ -328,7 +368,7 @@ ev_gdk_color_to_rgb (const GdkColor *color) static void draw_rubberband (GtkWidget *widget, GdkWindow *window, - const GdkRectangle *rect, gboolean dark) + const GdkRectangle *rect, guchar alpha) { GdkGC *gc; GdkPixbuf *pixbuf; @@ -336,8 +376,7 @@ draw_rubberband (GtkWidget *widget, GdkWindow *window, guint fill_color; fill_color_gdk = gdk_color_copy (>K_WIDGET (widget)->style->base[GTK_STATE_SELECTED]); - fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | - (dark ? 0x90 : 0x40); + fill_color = ev_gdk_color_to_rgb (fill_color_gdk) << 8 | alpha; pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, rect->width, rect->height); @@ -366,57 +405,79 @@ draw_rubberband (GtkWidget *widget, GdkWindow *window, static void highlight_find_results (EvView *view) { - EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); + EvDocumentFind *find; int i, results; + g_return_if_fail (EV_IS_DOCUMENT_FIND (view->document)); + + find = EV_DOCUMENT_FIND (view->document); + results = ev_document_find_get_n_results (find); for (i = 0; i < results; i++) { GdkRectangle rectangle; - gboolean current; + guchar alpha; - current = (i == view->find_result); + alpha = (i == view->find_result) ? 0x90 : 0x20; ev_document_find_get_result (find, i, &rectangle); draw_rubberband (GTK_WIDGET (view), view->bin_window, - &rectangle, current); + &rectangle, alpha); } } + static void expose_bin_window (GtkWidget *widget, GdkEventExpose *event) { EvView *view = EV_VIEW (widget); int x_offset, y_offset; - + GtkBorder border; + gint width, height; + GdkRectangle area; + int target_width, target_height; + if (view->document == NULL) return; - x_offset = MAX (0, (widget->allocation.width - - widget->requisition.width) / 2); - y_offset = MAX (0, (widget->allocation.height - - widget->requisition.height) / 2); - gdk_draw_rectangle (view->bin_window, - widget->style->black_gc, - FALSE, - x_offset, - y_offset, - widget->requisition.width - 1, - widget->requisition.height - 1); - + ev_document_get_page_size (view->document, -1, + &width, &height); + ev_document_misc_get_page_border_size (width, height, &border); + + x_offset = view->spacing; + y_offset = view->spacing; + target_width = width + border.left + border.right + view->spacing * 2; + target_height = height + border.top + border.bottom + view->spacing * 2; + + x_offset += MAX (0, (widget->allocation.width - target_width) / 2); + y_offset += MAX (0, (widget->allocation.height - target_height) / 2); + + /* Paint the frame */ + area.x = x_offset; + area.y = y_offset; + area.width = width + border.left + border.right; + area.height = height + border.top + border.bottom; + ev_document_misc_paint_one_page (view->bin_window, widget, &area, &border); + + /* Render the document itself */ ev_document_set_page_offset (view->document, - x_offset + 1, - y_offset + 1); + x_offset + border.left, + y_offset + border.top); + + LOG ("Render area %d %d %d %d", event->area.x, event->area.y, + event->area.width, event->area.height); ev_document_render (view->document, event->area.x, event->area.y, event->area.width, event->area.height); - highlight_find_results (view); + if (EV_IS_DOCUMENT_FIND (view->document)) { + highlight_find_results (view); + } if (view->has_selection) { draw_rubberband (widget, view->bin_window, - &view->selection, FALSE); + &view->selection, 0x40); } } @@ -582,6 +643,17 @@ ev_view_set_find_status (EvView *view, const char *message) g_object_notify (G_OBJECT (view), "find-status"); } +static GdkCursor * +ev_view_create_invisible_cursor(void) +{ + GdkBitmap *empty; + GdkColor black = { 0, 0, 0, 0 }; + static unsigned char bits[] = { 0x00 }; + + empty = gdk_bitmap_create_from_data (NULL, bits, 1, 1); + + return gdk_cursor_new_from_pixmap (empty, empty, &black, &black, 0, 0); +} static void ev_view_set_cursor (EvView *view, EvViewCursor new_cursor) @@ -608,6 +680,10 @@ ev_view_set_cursor (EvView *view, EvViewCursor new_cursor) case EV_VIEW_CURSOR_WAIT: cursor = gdk_cursor_new_for_display (display, GDK_WATCH); break; + case EV_VIEW_CURSOR_HIDDEN: + cursor = ev_view_create_invisible_cursor (); + break; + } if (cursor) { @@ -629,6 +705,8 @@ ev_view_motion_notify_event (GtkWidget *widget, view->selection.y = MIN (view->selection_start.y, event->y); view->selection.width = ABS (view->selection_start.x - event->x) + 1; view->selection.height = ABS (view->selection_start.y - event->y) + 1; + + gtk_widget_queue_draw (widget); } else if (view->document) { EvLink *link; @@ -650,8 +728,6 @@ ev_view_motion_notify_event (GtkWidget *widget, } } - gtk_widget_queue_draw (widget); - return TRUE; } @@ -904,45 +980,66 @@ ev_view_init (EvView *view) { GTK_WIDGET_SET_FLAGS (view, GTK_CAN_FOCUS); + view->spacing = 10; view->scale = 1.0; view->pressed_button = -1; view->cursor = EV_VIEW_CURSOR_NORMAL; + view->sizing_mode = EV_SIZING_BEST_FIT; } -static char * -ev_view_get_find_status_message (EvView *view) +static void +update_find_status_message (EvView *view) { -/* - if (view->find_results->len == 0) { - if (view->find_percent_complete >= (1.0 - 1e-10)) { - return g_strdup (_("Not found")); + char *message; + + if (ev_document_get_page (view->document) == view->find_page) { + int results; + + results = ev_document_find_get_n_results + (EV_DOCUMENT_FIND (view->document)); + + message = g_strdup_printf (_("%d found on this page"), + results); + } else { + double percent; + + percent = ev_document_find_get_progress + (EV_DOCUMENT_FIND (view->document)); + + if (percent >= (1.0 - 1e-10)) { + message = g_strdup (_("Not found")); } else { - return g_strdup_printf (_("%3d%% remaining to search"), - (int) ((1.0 - view->find_percent_complete) * 100)); + message = g_strdup_printf (_("%3d%% remaining to search"), + (int) ((1.0 - percent) * 100)); } - } else if (view->results_on_this_page == 0) { - g_assert (view->next_page_with_result != 0); - return g_strdup_printf (_("Found on page %d"), - view->next_page_with_result); - } else { - return g_strdup_printf (_("%d found on this page"), - view->results_on_this_page); + } -*/ + + ev_view_set_find_status (view, message); + g_free (message); } static void -set_document_page (EvView *view, int page) +set_document_page (EvView *view, int new_page) { + int page; + int pages; + + pages = ev_document_get_n_pages (view->document); + page = CLAMP (new_page, 1, pages); + if (view->document) { int old_page = ev_document_get_page (view->document); int old_width, old_height; ev_document_get_page_size (view->document, + -1, &old_width, &old_height); if (old_page != page) { - ev_view_set_cursor (view, EV_VIEW_CURSOR_WAIT); + if (view->cursor != EV_VIEW_CURSOR_HIDDEN) { + ev_view_set_cursor (view, EV_VIEW_CURSOR_WAIT); + } ev_document_set_page (view->document, page); } @@ -953,13 +1050,20 @@ set_document_page (EvView *view, int page) view->has_selection = FALSE; ev_document_get_page_size (view->document, + -1, &width, &height); if (width != old_width || height != old_height) gtk_widget_queue_resize (GTK_WIDGET (view)); + + gtk_adjustment_set_value (view->vadjustment, + view->vadjustment->lower); } - view->find_page = page; - view->find_result = 0; + if (EV_IS_DOCUMENT_FIND (view->document)) { + view->find_page = page; + view->find_result = 0; + update_find_status_message (view); + } } } @@ -1000,11 +1104,17 @@ ensure_rectangle_is_visible (EvView *view, GdkRectangle *rect) static void jump_to_find_result (EvView *view) { + EvDocumentFind *find = EV_DOCUMENT_FIND (view->document); GdkRectangle rect; + int n_results; - ev_document_find_get_result (EV_DOCUMENT_FIND (view->document), - view->find_result, &rect); - ensure_rectangle_is_visible (view, &rect); + n_results = ev_document_find_get_n_results (find); + + if (n_results > view->find_result) { + ev_document_find_get_result + (find, view->find_result, &rect); + ensure_rectangle_is_visible (view, &rect); + } } static void @@ -1041,8 +1151,7 @@ find_changed_cb (EvDocument *document, int page, EvView *view) { jump_to_find_page (view); jump_to_find_result (view); - - g_print ("Update for page %d\n", page); + update_find_status_message (view); if (ev_document_get_page (document) == page) { gtk_widget_queue_draw (GTK_WIDGET (view)); @@ -1054,7 +1163,10 @@ document_changed_callback (EvDocument *document, EvView *view) { gtk_widget_queue_draw (GTK_WIDGET (view)); - ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); + + if (view->cursor != EV_VIEW_CURSOR_HIDDEN) { + ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL); + } } /*** Public API ***/ @@ -1085,11 +1197,12 @@ ev_view_set_document (EvView *view, if (view->document) { g_object_ref (view->document); - if (EV_IS_DOCUMENT_FIND (view->document)) + if (EV_IS_DOCUMENT_FIND (view->document)) { g_signal_connect (view->document, "find_changed", G_CALLBACK (find_changed_cb), view); + } g_signal_connect (view->document, "changed", G_CALLBACK (document_changed_callback), @@ -1105,6 +1218,17 @@ ev_view_set_document (EvView *view, } } +void +ev_view_set_mode (EvView *view, + EvSizingMode sizing_mode) +{ + if (view->sizing_mode == sizing_mode) + return; + + view->sizing_mode = sizing_mode; + gtk_widget_queue_resize (GTK_WIDGET (view)); +} + static void go_to_link (EvView *view, EvLink *link) { @@ -1152,12 +1276,6 @@ ev_view_get_page (EvView *view) return 1; } -#define ZOOM_IN_FACTOR 1.2 -#define ZOOM_OUT_FACTOR (1.0/ZOOM_IN_FACTOR) - -#define MIN_SCALE 0.05409 -#define MAX_SCALE 18.4884 - static void ev_view_zoom (EvView *view, double factor, @@ -1170,7 +1288,12 @@ ev_view_zoom (EvView *view, else scale = factor; - view->scale = CLAMP (scale, MIN_SCALE, MAX_SCALE); + scale = CLAMP (scale, MIN_SCALE, MAX_SCALE); + + if (ABS (scale - view->scale) < ZOOM_EPSILON) + return; + + view->scale = scale; ev_document_set_scale (view->document, view->scale); @@ -1195,42 +1318,73 @@ ev_view_normal_size (EvView *view) ev_view_zoom (view, 1.0, FALSE); } +/* Unfortunately this is not idempotent (!) (numerical stability + * issues because width and height are rounded) */ void -ev_view_best_fit (EvView *view) +ev_view_best_fit (EvView *view, int allocation_width, int allocation_height) { - double scale; + int target_width, target_height; int width, height; + GtkBorder border; + + if (view->document == NULL) + return; width = height = 0; - ev_document_get_page_size (view->document, &width, &height); + /* This is the bad part. You could make it stable by doing + * ev_document_set_scale 1.0. But at least with pdf this means + * redrawing the whole page */ + ev_document_get_page_size (view->document, -1, &width, &height); + /* FIXME: The border size isn't constant. Ugh. Still, if we have extra + * space, we just cut it from the border */ + ev_document_misc_get_page_border_size (width, height, &border); + + target_width = allocation_width - (view->spacing * 2 + border.left + border.right); + target_height = allocation_height - (view->spacing * 2 + border.top + border.bottom); + + LOG ("Best fit %d %d", allocation_width, allocation_height); - scale = 1.0; if (width != 0 && height != 0) { + double scale; double scale_w, scale_h; - scale_w = (double)GTK_WIDGET (view)->allocation.width * view->scale / width; - scale_h = (double)GTK_WIDGET (view)->allocation.height * view->scale / height; + scale_w = (double)target_width * view->scale / width; + scale_h = (double)target_height * view->scale / height; scale = (scale_w < scale_h) ? scale_w : scale_h; + + ev_view_zoom (view, scale, FALSE); } - ev_view_zoom (view, scale, FALSE); } void -ev_view_fit_width (EvView *view) +ev_view_fit_width (EvView *view, int allocation_width, int allocation_height, + int vsb_width) { - double scale = 1.0; - int width; + int target_width, target_height; + int width, height; + GtkBorder border; - width = 0; - ev_document_get_page_size (view->document, &width, NULL); + if (view->document == NULL) + return; - scale = 1.0; - if (width != 0) - scale = (double)GTK_WIDGET (view)->allocation.width * view->scale / width; + width = height = 0; + ev_document_get_page_size (view->document, -1, &width, &height); + ev_document_misc_get_page_border_size (width, height, &border); + + target_width = allocation_width - (view->spacing * 2 + border.left + border.right); + target_height = allocation_height - (view->spacing * 2 + border.top + border.bottom); + + if (width) { + double scale; + scale = (double)target_width * view->scale / width; - ev_view_zoom (view, scale, FALSE); + if (height * scale / view->scale > target_height) + scale = ((double)(target_width - vsb_width) * view->scale / width); + + ev_view_zoom (view, scale, FALSE); + } } const char * @@ -1300,3 +1454,14 @@ ev_view_find_previous (EvView *view) gtk_widget_queue_draw (GTK_WIDGET (view)); } } +void +ev_view_hide_cursor (EvView *view) +{ + ev_view_set_cursor (view, EV_VIEW_CURSOR_HIDDEN); +} + +void +ev_view_show_cursor (EvView *view) +{ + ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK); +}