#include "ev-marshal.h"
#include "ev-view.h"
+#include "ev-selection.h"
#include "ev-document-find.h"
#include "ev-document-misc.h"
#include "ev-debug.h"
};
enum {
- SIGNAL_SCROLL_VIEW,
+ SIGNAL_BINDING_ACTIVATED,
SIGNAL_ZOOM_INVALID,
N_SIGNALS,
};
typedef enum {
EV_VIEW_CURSOR_NORMAL,
+ EV_VIEW_CURSOR_IBEAM,
EV_VIEW_CURSOR_LINK,
EV_VIEW_CURSOR_WAIT,
EV_VIEW_CURSOR_HIDDEN,
#define MIN_SCALE 0.05409
#define MAX_SCALE 4.0
+/* Information for middle clicking and moving around the doc */
typedef struct {
- EvRectangle rect;
- int page;
-} EvViewSelection;
-
-typedef struct {
- gboolean dragging;
+ gboolean in_drag;
GdkPoint start;
gdouble hadj;
gdouble vadj;
} DragInfo;
+/* Information for handling selection */
+typedef struct {
+ gboolean in_selection;
+ GdkPoint start;
+ GList *selections;
+} SelectionInfo;
+
typedef enum {
SCROLL_TO_KEEP_POSITION,
SCROLL_TO_CURRENT_PAGE,
gint scroll_x;
gint scroll_y;
+ /* Information for middle clicking and dragging around. */
DragInfo drag_info;
+
+ /* Selection */
+ EvViewSelectionMode selection_mode;
+ SelectionInfo selection_info;
+
gboolean pressed_button;
- GdkPoint selection_start;
- GList *selections;
EvViewCursor cursor;
GtkAdjustment *hadjustment;
int spacing;
double scale;
- GtkBorder border;
gboolean continuous;
gboolean dual_page;
gboolean fullscreen;
gboolean presentation;
EvSizingMode sizing_mode;
-
+
PendingScroll pending_scroll;
gboolean pending_resize;
};
void (*set_scroll_adjustments) (EvView *view,
GtkAdjustment *hadjustment,
GtkAdjustment *vadjustment);
- void (*scroll_view) (EvView *view,
+ void (*binding_activated) (EvView *view,
GtkScrollType scroll,
gboolean horizontal);
void (*zoom_invalid) (EvView *view);
};
/*** Scrolling ***/
+static void scroll_to_current_page (EvView *view,
+ GtkOrientation orientation);
static void ev_view_set_scroll_adjustments (EvView *view,
GtkAdjustment *hadjustment,
GtkAdjustment *vadjustment);
guint keyval,
GtkScrollType scroll,
gboolean horizontal);
-static void ev_view_scroll_view (EvView *view,
+static void ev_view_binding_activated (EvView *view,
GtkScrollType scroll,
gboolean horizontal);
static void ensure_rectangle_is_visible (EvView *view,
int width,
int height,
GtkBorder *border);
+static void get_page_y_offset (EvView *view,
+ int page,
+ double zoom,
+ int *y_offset);
static gboolean get_page_extents (EvView *view,
gint page,
GdkRectangle *page_area,
int page,
EvRectangle *doc_rect,
GdkRectangle *view_rect);
-static void get_bounding_box_size (EvView *view,
- int *max_width,
- int *max_height);
static void find_page_at_location (EvView *view,
gdouble x,
gdouble y,
/*** Selection ***/
static void compute_selections (EvView *view,
- GdkRectangle *view_rect);
+ GdkPoint *start,
+ GdkPoint *stop);
static void clear_selection (EvView *view);
static char* get_selected_text (EvView *ev_view);
static void ev_view_primary_get_cb (GtkClipboard *clipboard,
gpointer data);
static void ev_view_update_primary_selection (EvView *ev_view);
-
+
G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_WIDGET)
static void
scroll_to_current_page (EvView *view, GtkOrientation orientation)
{
- int max_width, max_height, n_rows;
-
- get_bounding_box_size (view, &max_width, &max_height);
-
- if (orientation == GTK_ORIENTATION_VERTICAL) {
- if (view->continuous) {
- n_rows = view->dual_page ? view->current_page / 2 : view->current_page;
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ if (view->document == NULL) {
+ return;
+ }
+
+ get_page_extents (view, view->current_page, &page_area, &border);
+ if (orientation == GTK_ORIENTATION_VERTICAL) {
+ if (view->continuous) {
gtk_adjustment_clamp_page (view->vadjustment,
- (max_height + view->spacing) * n_rows,
- (max_height + view->spacing) * n_rows +
- view->vadjustment->page_size);
+ page_area.y - view->spacing,
+ page_area.y + view->vadjustment->page_size);
} else {
gtk_adjustment_set_value (view->vadjustment,
view->vadjustment->lower);
}
} else {
if (view->dual_page) {
- if (view->current_page % 2 == 0) {
- gtk_adjustment_set_value (view->hadjustment,
- view->hadjustment->lower);
- } else {
- gtk_adjustment_clamp_page (view->hadjustment,
- view->hadjustment->lower +
- max_width + view->spacing,
- view->hadjustment->lower +
- max_width + view->spacing +
- view->hadjustment->page_size);
- }
+ gtk_adjustment_clamp_page (view->hadjustment,
+ page_area.x,
+ page_area.x + view->hadjustment->page_size);
} else {
gtk_adjustment_set_value (view->hadjustment,
- CLAMP (view->hadjustment->value,
+ CLAMP (view->hadjustment->value,
view->hadjustment->lower,
- view->hadjustment->upper -
+ view->hadjustment->upper -
view->hadjustment->page_size));
}
}
if (!adjustment)
return;
-
+
factor = 1.0;
switch (view->pending_scroll) {
- case SCROLL_TO_KEEP_POSITION:
- factor = adjustment->value / adjustment->upper;
+ case SCROLL_TO_KEEP_POSITION:
+ factor = (adjustment->value) / adjustment->upper;
break;
- case SCROLL_TO_CURRENT_PAGE:
+ case SCROLL_TO_CURRENT_PAGE:
break;
- case SCROLL_TO_CENTER:
+ case SCROLL_TO_CENTER:
factor = (adjustment->value + adjustment->page_size * 0.5) / adjustment->upper;
break;
}
adjustment->lower = 0;
adjustment->upper = MAX (allocation, requisition);
+ /*
+ * We add 0.5 to the values before to average out our rounding errors.
+ */
switch (view->pending_scroll) {
- case SCROLL_TO_KEEP_POSITION:
- new_value = CLAMP (adjustment->upper * factor, 0, adjustment->upper - adjustment->page_size);
+ case SCROLL_TO_KEEP_POSITION:
+ new_value = CLAMP (adjustment->upper * factor + 0.5, 0, adjustment->upper - adjustment->page_size);
gtk_adjustment_set_value (adjustment, (int)new_value);
break;
- case SCROLL_TO_CURRENT_PAGE:
+ case SCROLL_TO_CURRENT_PAGE:
scroll_to_current_page (view, orientation);
break;
- case SCROLL_TO_CENTER:
- new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5,
+ case SCROLL_TO_CENTER:
+ new_value = CLAMP (adjustment->upper * factor - adjustment->page_size * 0.5 + 0.5,
0, adjustment->upper - adjustment->page_size);
gtk_adjustment_set_value (adjustment, (int)new_value);
break;
view->end_page = view->current_page;
} else if (view->continuous) {
GdkRectangle current_area, unused, page_area;
+ GtkBorder border;
gint current_page;
gboolean found = FALSE;
int i;
-
- get_bounding_box_size (view, &(page_area.width), &(page_area.height));
- page_area.x = view->spacing;
- page_area.y = view->spacing;
- if (view->hadjustment) {
- current_area.x = view->hadjustment->value;
- current_area.width = view->hadjustment->page_size;
- } else {
- current_area.x = page_area.x;
- current_area.width = page_area.width;
- }
+ if (!(view->vadjustment && view->hadjustment))
+ return;
- if (view->vadjustment) {
- current_area.y = view->vadjustment->value;
- current_area.height = view->vadjustment->page_size;
- } else {
- current_area.y = page_area.y;
- current_area.height = page_area.height;
- }
+ current_area.x = view->hadjustment->value;
+ current_area.width = view->hadjustment->upper;
+ current_area.y = view->vadjustment->value;
+ current_area.height = view->vadjustment->page_size;
for (i = 0; i < ev_page_cache_get_n_pages (view->page_cache); i++) {
+
+ get_page_extents (view, i, &page_area, &border);
+
if (gdk_rectangle_intersect (¤t_area, &page_area, &unused)) {
if (! found) {
view->start_page = i;
found = TRUE;
-
+
}
view->end_page = i;
} else if (found) {
break;
}
- if (view->dual_page) {
- if (i % 2 == 0) {
- page_area.x += page_area.width + view->spacing;
- } else {
- page_area.x = view->spacing;
- page_area.y += page_area.height + view->spacing;
- }
- } else {
- page_area.y += page_area.height + view->spacing;
- }
}
current_page = ev_page_cache_get_current_page (view->page_cache);
if (current_page < view->start_page || current_page > view->end_page) {
view->current_page = view->start_page;
ev_page_cache_set_current_page (view->page_cache, view->start_page);
- }
+ }
} else {
if (view->dual_page) {
if (view->current_page % 2 == 0) {
ev_pixbuf_cache_set_page_range (view->pixbuf_cache,
view->start_page,
view->end_page,
- view->scale);
+ view->scale,
+ view->selection_info.selections);
}
static void
guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
gtk_binding_entry_add_signal (binding_set, keyval, 0,
- "scroll_view", 2,
+ "binding_activated", 2,
GTK_TYPE_SCROLL_TYPE, scroll,
G_TYPE_BOOLEAN, horizontal);
gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
- "scroll_view", 2,
+ "binding_activated", 2,
GTK_TYPE_SCROLL_TYPE, scroll,
G_TYPE_BOOLEAN, horizontal);
}
}
static void
-ev_view_scroll_view (EvView *view,
- GtkScrollType scroll,
- gboolean horizontal)
+ev_view_binding_activated (EvView *view,
+ GtkScrollType scroll,
+ gboolean horizontal)
{
GtkAdjustment *adjustment;
double value;
+ if (view->presentation) {
+ switch (scroll) {
+ case GTK_SCROLL_STEP_BACKWARD:
+ ev_page_cache_prev_page (view->page_cache);
+ break;
+ case GTK_SCROLL_STEP_FORWARD:
+ ev_page_cache_next_page (view->page_cache);
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
if (horizontal) {
adjustment = view->hadjustment;
} else {
}
}
+static void get_page_y_offset (EvView *view,
+ int page,
+ double zoom,
+ int *y_offset)
+{
+ int max_width, offset;
+ GtkBorder border;
+
+ g_return_if_fail (y_offset != NULL);
+
+ ev_page_cache_get_max_width (view->page_cache, zoom, &max_width);
+
+ compute_border (view, max_width, max_width, &border);
+
+ if (view->dual_page) {
+ ev_page_cache_get_height_to_page (view->page_cache, page, zoom, NULL, &offset);
+ offset += (page / 2 + 1) * view->spacing + (page / 2) * (border.top + border.bottom);
+ } else {
+ ev_page_cache_get_height_to_page (view->page_cache, page, zoom, &offset, NULL);
+ offset += (page + 1) * view->spacing + page * (border.top + border.bottom);
+ }
+
+ *y_offset = offset;
+ return;
+}
+
static gboolean
get_page_extents (EvView *view,
gint page,
widget = GTK_WIDGET (view);
- /* Quick sanity check */
- if (view->presentation) {
- if (view->current_page != page)
- return FALSE;
- } else if (view->continuous) {
- if (page < view->start_page ||
- page > view->end_page)
- return FALSE;
- } else if (view->dual_page) {
- if (ABS (page - view->current_page) > 1)
- return FALSE;
- } else {
- if (view->current_page != page)
- return FALSE;
- }
-
/* Get the size of the page */
ev_page_cache_get_size (view->page_cache, page,
view->scale,
page_area->x = (MAX (0, widget->allocation.width - width))/2;
page_area->y = (MAX (0, widget->allocation.height - height))/2;
} else if (view->continuous) {
- gint max_width, max_height;
+ gint max_width;
gint x, y;
- get_bounding_box_size (view, &max_width, &max_height);
+ ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
+ max_width = max_width + border->left + border->right;
/* Get the location of the bounding box */
if (view->dual_page) {
x = view->spacing + (page % 2) * (max_width + view->spacing);
- y = view->spacing + (page / 2) * (max_height + view->spacing);
- x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3))/2;
+ x = x + MAX (0, widget->allocation.width - (max_width * 2 + view->spacing * 3)) / 2;
+ if (page % 2 == 0)
+ x = x + (max_width - width - border->left - border->right);
} else {
x = view->spacing;
- y = view->spacing + page * (max_height + view->spacing);
- x = x + MAX (0, widget->allocation.width - (max_width + view->spacing * 2))/2;
+ x = x + MAX (0, widget->allocation.width - (width + view->spacing * 2)) / 2;
}
+
+ get_page_y_offset (view, page, view->scale, &y);
+
page_area->x = x;
page_area->y = y;
} else {
/* First, we get the bounding box of the two pages */
if (other_page < ev_page_cache_get_n_pages (view->page_cache)) {
ev_page_cache_get_size (view->page_cache,
- page + 1,
+ other_page,
view->scale,
&width_2, &height_2);
if (width_2 > width)
return TRUE;
}
+static void
+view_point_to_doc_point (EvView *view,
+ GdkPoint *view_point,
+ GdkRectangle *page_area,
+ double *doc_point_x,
+ double *doc_point_y)
+{
+ *doc_point_x = (double) (view_point->x - page_area->x) / view->scale;
+ *doc_point_y = (double) (view_point->y - page_area->y) / view->scale;
+}
+
static void
view_rect_to_doc_rect (EvView *view,
GdkRectangle *view_rect,
GdkRectangle *page_area,
EvRectangle *doc_rect)
{
- doc_rect->x1 = floor ((view_rect->x - page_area->x) / view->scale);
- doc_rect->y1 = floor ((view_rect->y - page_area->y) / view->scale);
- doc_rect->x2 = doc_rect->x1 + ceil (view_rect->width / view->scale);
- doc_rect->y2 = doc_rect->y1 + ceil (view_rect->height / view->scale);
+ doc_rect->x1 = (double) (view_rect->x - page_area->x) / view->scale;
+ doc_rect->y1 = (double) (view_rect->y - page_area->y) / view->scale;
+ doc_rect->x2 = doc_rect->x1 + (double) view_rect->width / view->scale;
+ doc_rect->y2 = doc_rect->y1 + (double) view_rect->height / view->scale;
}
static void
view_rect->height = ceil (height * view->scale);
}
-static void
-get_bounding_box_size (EvView *view, int *max_width, int *max_height)
-{
- GtkBorder border;
- int width, height;
-
- if (max_width) {
- ev_page_cache_get_max_width_size (view->page_cache,
- view->scale,
- &width, &height);
- compute_border (view, width, height, &border);
- *max_width = width + border.left + border.right;
- }
-
-
- if (max_height) {
- ev_page_cache_get_max_height_size (view->page_cache,
- view->scale,
- &width, &height);
- compute_border (view, width, height, &border);
- *max_height = height + border.top + border.bottom;
- }
-}
-
static void
find_page_at_location (EvView *view,
gdouble x,
*page = -1;
}
+static gboolean
+location_in_text (EvView *view,
+ gdouble x,
+ gdouble y)
+{
+ GdkRegion *region;
+ gint page = -1;
+ gint x_offset = 0, y_offset = 0;
+
+ find_page_at_location (view, x, y, &page, &x_offset, &y_offset);
+
+ if (page == -1)
+ return FALSE;
+
+ region = ev_pixbuf_cache_get_text_mapping (view->pixbuf_cache, page);
+
+ if (region)
+ return gdk_region_point_in (region, x_offset / view->scale, y_offset / view->scale);
+ else
+ return FALSE;
+}
+
/*** Hyperref ***/
static EvLink *
get_link_at_location (EvView *view,
static void
ev_view_size_request_continuous_dual_page (EvView *view,
- GtkRequisition *requisition)
+ GtkRequisition *requisition)
{
- int max_width, max_height;
- int n_rows;
+ int max_width;
+ gint n_pages;
+ GtkBorder border;
- get_bounding_box_size (view, &max_width, &max_height);
+ ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
+ compute_border (view, max_width, max_width, &border);
- n_rows = (1 + ev_page_cache_get_n_pages (view->page_cache)) / 2;
+ n_pages = ev_page_cache_get_n_pages (view->page_cache) + 1;
- requisition->width = (max_width * 2) + (view->spacing * 3);
- requisition->height = max_height * n_rows + (view->spacing * (n_rows + 1));
+ requisition->width = (max_width + border.left + border.right) * 2 + (view->spacing * 3);
+ get_page_y_offset (view, n_pages, view->scale, &requisition->height);
if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
requisition->width = 1;
ev_view_size_request_continuous (EvView *view,
GtkRequisition *requisition)
{
- int max_width, max_height;
+ int max_width;
int n_pages;
+ GtkBorder border;
- get_bounding_box_size (view, &max_width, &max_height);
+ ev_page_cache_get_max_width (view->page_cache, view->scale, &max_width);
n_pages = ev_page_cache_get_n_pages (view->page_cache);
+ compute_border (view, max_width, max_width, &border);
- requisition->width = max_width + (view->spacing * 2);
- requisition->height = max_height * n_pages + (view->spacing * (n_pages + 1));
+ requisition->width = max_width + (view->spacing * 2) + border.left + border.right;
+ get_page_y_offset (view, n_pages, view->scale, &requisition->height);
if (view->sizing_mode == EV_SIZING_FIT_WIDTH) {
requisition->width = 1;
GtkAllocation *allocation)
{
EvView *view = EV_VIEW (widget);
-
+
if (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
view->sizing_mode == EV_SIZING_BEST_FIT) {
g_signal_emit (view, signals[SIGNAL_ZOOM_INVALID], 0);
-
+
ev_view_size_request (widget, &widget->requisition);
}
-
+
view_set_adjustment_values (view, GTK_ORIENTATION_HORIZONTAL);
view_set_adjustment_values (view, GTK_ORIENTATION_VERTICAL);
view->pending_resize = FALSE;
if (view->document)
- view_update_range_and_current_page (view);
+ view_update_range_and_current_page (view);
GTK_WIDGET_CLASS (ev_view_parent_class)->size_allocate (widget, allocation);
}
static gboolean
ev_view_scroll_event (GtkWidget *widget, GdkEventScroll *event)
{
- EvView *view = EV_VIEW (widget);
+ EvView *view = EV_VIEW (widget);
- if ((event->state & GDK_CONTROL_MASK) != 0) {
+ if ((event->state & GDK_CONTROL_MASK) != 0) {
- ev_view_set_sizing_mode (view, EV_SIZING_FREE);
+ ev_view_set_sizing_mode (view, EV_SIZING_FREE);
- if (event->direction == GDK_SCROLL_UP ||
+ if (event->direction == GDK_SCROLL_UP ||
event->direction == GDK_SCROLL_LEFT) {
if (ev_view_can_zoom_in (view)) {
ev_view_zoom_in (view);
} else {
if (ev_view_can_zoom_out (view)) {
ev_view_zoom_out (view);
- }
+ }
}
return TRUE;
}
-
- if ((event->state & GDK_SHIFT_MASK) != 0) {
+
+ if ((event->state & GDK_SHIFT_MASK) != 0) {
if (event->direction == GDK_SCROLL_UP)
event->direction = GDK_SCROLL_LEFT;
if (event->direction == GDK_SCROLL_DOWN)
return FALSE;
}
+static EvViewSelection *
+find_selection_for_page (EvView *view,
+ gint page)
+{
+ GList *list;
+
+ for (list = view->selection_info.selections; list != NULL; list = list->next) {
+ EvViewSelection *selection;
+
+ selection = (EvViewSelection *) list->data;
+
+ if (selection->page == page)
+ return selection;
+ }
+
+ return NULL;
+}
+
static gboolean
ev_view_expose_event (GtkWidget *widget,
GdkEventExpose *event)
{
EvView *view = EV_VIEW (widget);
- GdkRectangle rubberband;
- GList *l;
int i;
if (view->document == NULL)
if (!get_page_extents (view, i, &page_area, &border))
continue;
-
+
page_area.x -= view->scroll_x;
page_area.y -= view->scroll_y;
-
+
draw_one_page (view, i, &page_area, &border, &(event->area));
- if (EV_IS_DOCUMENT_FIND (view->document)) {
+ if (EV_IS_DOCUMENT_FIND (view->document))
highlight_find_results (view, i);
- }
- }
-
- for (l = view->selections; l != NULL; l = l->next) {
- EvViewSelection *selection = (EvViewSelection *)l->data;
-
- doc_rect_to_view_rect (view, selection->page,
- &selection->rect, &rubberband);
- if (rubberband.width > 0 && rubberband.height > 0) {
- draw_rubberband (GTK_WIDGET (view), GTK_WIDGET(view)->window,
- &rubberband, 0x40);
- }
}
return FALSE;
switch (event->button) {
case 1:
- if (view->selections) {
+ if (view->selection_info.selections) {
clear_selection (view);
gtk_widget_queue_draw (widget);
}
- view->selection_start.x = event->x;
- view->selection_start.y = event->y;
+ view->selection_info.start.x = event->x + view->scroll_x;
+ view->selection_info.start.y = event->y + view->scroll_y;
return TRUE;
case 2:
/* use root coordinates as reference point because
{
EvView *view = EV_VIEW (widget);
- if (view->pressed_button == 1) {
- GdkRectangle selection;
-
- selection.x = MIN (view->selection_start.x, event->x) + view->scroll_x;
- selection.y = MIN (view->selection_start.y, event->y) + view->scroll_y;
- selection.width = ABS (view->selection_start.x - event->x) + 1;
- selection.height = ABS (view->selection_start.y - event->y) + 1;
+ if (!view->document)
+ return FALSE;
- compute_selections (view, &selection);
+ if (view->pressed_button == 1) {
+ GdkPoint point;
- gtk_widget_queue_draw (widget);
+ view->selection_info.in_selection = TRUE;
+ point.x = event->x + view->scroll_x;
+ point.y = event->y + view->scroll_y;
+ compute_selections (view, &view->selection_info.start, &point);
return TRUE;
} else if (view->pressed_button == 2) {
- if (!view->drag_info.dragging) {
+ if (!view->drag_info.in_drag) {
gboolean start;
start = gtk_drag_check_threshold (widget,
view->drag_info.start.y,
event->x_root,
event->y_root);
- view->drag_info.dragging = start;
+ view->drag_info.in_drag = start;
}
- if (view->drag_info.dragging) {
+ if (view->drag_info.in_drag) {
int dx, dy;
gdouble dhadj_value, dvadj_value;
return TRUE;
}
- } else if (view->pressed_button <= 0 && view->document) {
+ } else if (view->pressed_button <= 0) {
EvLink *link;
link = get_link_at_location (view, event->x + view->scroll_x, event->y + view->scroll_y);
ev_view_set_status (view, msg);
ev_view_set_cursor (view, EV_VIEW_CURSOR_LINK);
g_free (msg);
+ } else if (location_in_text (view, event->x + view->scroll_x, event->y + view->scroll_y)) {
+ ev_view_set_cursor (view, EV_VIEW_CURSOR_IBEAM);
} else {
ev_view_set_status (view, NULL);
- if (view->cursor == EV_VIEW_CURSOR_LINK) {
+ if (view->cursor == EV_VIEW_CURSOR_LINK ||
+ view->cursor == EV_VIEW_CURSOR_IBEAM)
ev_view_set_cursor (view, EV_VIEW_CURSOR_NORMAL);
- }
}
return TRUE;
}
}
view->pressed_button = -1;
- view->drag_info.dragging = FALSE;
+ view->drag_info.in_drag = FALSE;
- if (view->selections) {
+ if (view->selection_info.selections) {
ev_view_update_primary_selection (view);
} else if (view->document) {
EvLink *link;
GdkPixbuf *pixbuf;
GdkColor *fill_color_gdk;
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 | alpha;
}
static void
-draw_one_page (EvView *view,
- gint page,
- GdkRectangle *page_area,
- GtkBorder *border,
- GdkRectangle *expose_area)
+draw_one_page (EvView *view,
+ gint page,
+ GdkRectangle *page_area,
+ GtkBorder *border,
+ GdkRectangle *expose_area)
{
gint width, height;
- GdkPixbuf *scaled_image;
GdkPixbuf *current_pixbuf;
GdkRectangle overlap;
GdkRectangle real_page_area;
+ EvViewSelection *selection;
g_assert (view->document);
if (! gdk_rectangle_intersect (page_area, expose_area, &overlap))
return;
+ selection = find_selection_for_page (view, page);
ev_page_cache_get_size (view->page_cache,
page, view->scale,
&width, &height);
page_area, border);
if (gdk_rectangle_intersect (&real_page_area, expose_area, &overlap)) {
+ GdkPixbuf *selection_pixbuf = NULL;
+ GdkPixbuf *scaled_image;
+ GdkPixbuf *scaled_selection;
+
current_pixbuf = ev_pixbuf_cache_get_pixbuf (view->pixbuf_cache, page);
+ /* Get the selection pixbuf iff we have something to draw */
+ if (current_pixbuf && view->selection_mode == EV_VIEW_SELECTION_TEXT && selection)
+ selection_pixbuf = ev_pixbuf_cache_get_selection_pixbuf (view->pixbuf_cache,
+ page,
+ view->scale);
+
if (current_pixbuf == NULL)
scaled_image = NULL;
else if (width == gdk_pixbuf_get_width (current_pixbuf) &&
width, height,
GDK_INTERP_NEAREST);
+ if (selection_pixbuf == NULL)
+ scaled_selection = NULL;
+ else if (width == gdk_pixbuf_get_width (selection_pixbuf) &&
+ height == gdk_pixbuf_get_height (selection_pixbuf))
+ scaled_selection = g_object_ref (selection_pixbuf);
+ else
+ /* FIXME: We don't want to scale the whole area, just the right
+ * area of it */
+ scaled_selection = gdk_pixbuf_scale_simple (selection_pixbuf,
+ width, height,
+ GDK_INTERP_NEAREST);
+
if (scaled_image) {
gdk_draw_pixbuf (GTK_WIDGET(view)->window,
GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
0, 0);
g_object_unref (scaled_image);
}
+
+ if (scaled_selection) {
+ gdk_draw_pixbuf (GTK_WIDGET(view)->window,
+ GTK_WIDGET (view)->style->fg_gc[GTK_STATE_NORMAL],
+ scaled_selection,
+ overlap.x - real_page_area.x,
+ overlap.y - real_page_area.y,
+ overlap.x, overlap.y,
+ overlap.width, overlap.height,
+ GDK_RGB_DITHER_NORMAL,
+ 0, 0);
+ g_object_unref (scaled_selection);
+ }
}
}
gtk_object_class->destroy = ev_view_destroy;
class->set_scroll_adjustments = ev_view_set_scroll_adjustments;
- class->scroll_view = ev_view_scroll_view;
+ class->binding_activated = ev_view_binding_activated;
- widget_class->set_scroll_adjustments_signal =
+ widget_class->set_scroll_adjustments_signal =
g_signal_new ("set-scroll-adjustments",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
GTK_TYPE_ADJUSTMENT,
GTK_TYPE_ADJUSTMENT);
- signals[SIGNAL_SCROLL_VIEW] = g_signal_new ("scroll-view",
+ signals[SIGNAL_BINDING_ACTIVATED] = g_signal_new ("binding_activated",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (EvViewClass, scroll_view),
+ G_STRUCT_OFFSET (EvViewClass, binding_activated),
NULL, NULL,
ev_marshal_VOID__ENUM_BOOLEAN,
G_TYPE_NONE, 2,
g_param_spec_double ("zoom",
"Zoom factor",
"Zoom factor",
- MIN_SCALE,
+ MIN_SCALE,
MAX_SCALE,
1.0,
G_PARAM_READWRITE));
view->current_page = 0;
view->pressed_button = -1;
view->cursor = EV_VIEW_CURSOR_NORMAL;
- view->drag_info.dragging = FALSE;
+ view->drag_info.in_drag = FALSE;
+ view->selection_info.in_selection = FALSE;
+ view->selection_mode = EV_VIEW_SELECTION_TEXT;
view->continuous = TRUE;
view->dual_page = FALSE;
view->presentation = FALSE;
EvView *view)
{
if (view->current_page != new_page) {
-
+
view->current_page = new_page;
view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
gtk_widget_queue_resize (GTK_WIDGET (view));
} else {
view->scroll_y = 0;
}
-
-
- if (view->pending_resize)
+
+
+ if (view->pending_resize)
gtk_widget_queue_draw (GTK_WIDGET (view));
else
gdk_window_scroll (GTK_WIDGET (view)->window, dx, dy);
if (view->document)
- view_update_range_and_current_page (view);}
+ view_update_range_and_current_page (view);
+}
GtkWidget*
ev_view_new (void)
return view;
}
+static void
+setup_caches (EvView *view)
+{
+ view->page_cache = ev_page_cache_get (view->document);
+ g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
+ view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
+ g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
+}
+
+static void
+clear_caches (EvView *view)
+{
+ if (view->pixbuf_cache) {
+ g_object_unref (view->pixbuf_cache);
+ view->pixbuf_cache = NULL;
+ }
+
+ if (view->document) {
+ ev_page_cache_clear (view->document);
+ }
+}
+
void
ev_view_set_document (EvView *view,
EvDocument *document)
g_return_if_fail (EV_IS_VIEW (view));
if (document != view->document) {
+ clear_caches (view);
+
if (view->document) {
g_signal_handlers_disconnect_by_func (view->document,
find_changed_cb,
}
- if (view->pixbuf_cache) {
- g_object_unref (view->pixbuf_cache);
- view->pixbuf_cache = NULL;
- }
-
view->document = document;
view->find_page = 0;
view->find_result = 0;
G_CALLBACK (find_changed_cb),
view);
}
- view->page_cache = ev_document_get_page_cache (view->document);
- g_signal_connect (view->page_cache, "page-changed", G_CALLBACK (page_changed_cb), view);
- view->pixbuf_cache = ev_pixbuf_cache_new (view->document);
- g_signal_connect (view->pixbuf_cache, "job-finished", G_CALLBACK (job_finished_cb), view);
+
+ setup_caches (view);
}
gtk_widget_queue_resize (GTK_WIDGET (view));
view->scale = scale;
view->pending_resize = TRUE;
-
+
gtk_widget_queue_resize (GTK_WIDGET (view));
g_object_notify (G_OBJECT (view), "zoom");
return view->scale;
}
+gboolean
+ev_view_get_continuous (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
+
+ return view->continuous;
+}
+
void
ev_view_set_continuous (EvView *view,
gboolean continuous)
g_object_notify (G_OBJECT (view), "continuous");
}
+gboolean
+ev_view_get_dual_page (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), FALSE);
+
+ return view->dual_page;
+}
+
void
ev_view_set_dual_page (EvView *view,
gboolean dual_page)
if (view->dual_page == dual_page)
return;
-
+
view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
view->dual_page = dual_page;
/* FIXME: if we're keeping the pixbuf cache around, we should extend the
fullscreen = fullscreen != FALSE;
- if (view->fullscreen != fullscreen) {
- view->fullscreen = fullscreen;
- gtk_widget_queue_resize (GTK_WIDGET (view));
- }
-
+ if (view->fullscreen == fullscreen)
+ return;
+
+ view->fullscreen = fullscreen;
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+
g_object_notify (G_OBJECT (view), "fullscreen");
}
return;
view->presentation = presentation;
+ view->pending_scroll = SCROLL_TO_CURRENT_PAGE;
gtk_widget_queue_resize (GTK_WIDGET (view));
+
if (GTK_WIDGET_REALIZED (view)) {
if (view->presentation)
gdk_window_set_background (GTK_WIDGET(view)->window,
view->sizing_mode = sizing_mode;
gtk_widget_queue_resize (GTK_WIDGET (view));
-
+
g_object_notify (G_OBJECT (view), "sizing-mode");
}
ev_view_zoom_in (EvView *view)
{
g_return_if_fail (view->sizing_mode == EV_SIZING_FREE);
-
+
view->pending_scroll = SCROLL_TO_CENTER;
ev_view_set_zoom (view, ZOOM_IN_FACTOR, TRUE);
}
ev_view_set_zoom (view, ZOOM_OUT_FACTOR, TRUE);
}
+static void
+ev_view_set_orientation (EvView *view,
+ EvOrientation orientation)
+{
+ ev_document_set_orientation (view->document, orientation);
+
+ clear_caches (view);
+ setup_caches (view);
+
+ gtk_widget_queue_resize (GTK_WIDGET (view));
+}
+
+void
+ev_view_rotate_right (EvView *view)
+{
+ EvOrientation orientation, new_orientation;
+
+ orientation = ev_document_get_orientation (view->document);
+ if (orientation == EV_ORIENTATION_PORTRAIT) {
+ new_orientation = EV_ORIENTATION_LANDSCAPE;
+ } else if (orientation == EV_ORIENTATION_LANDSCAPE) {
+ new_orientation = EV_ORIENTATION_UPSIDEDOWN;
+ } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) {
+ new_orientation = EV_ORIENTATION_SEASCAPE;
+ } else {
+ new_orientation = EV_ORIENTATION_PORTRAIT;
+ }
+ ev_view_set_orientation (view, new_orientation);
+}
+
+void
+ev_view_rotate_left (EvView *view)
+{
+ EvOrientation orientation, new_orientation;
+
+ orientation = ev_document_get_orientation (view->document);
+ if (orientation == EV_ORIENTATION_PORTRAIT) {
+ new_orientation = EV_ORIENTATION_SEASCAPE;
+ } else if (orientation == EV_ORIENTATION_SEASCAPE) {
+ new_orientation = EV_ORIENTATION_UPSIDEDOWN;
+ } else if (orientation == EV_ORIENTATION_UPSIDEDOWN) {
+ new_orientation = EV_ORIENTATION_LANDSCAPE;
+ } else {
+ new_orientation = EV_ORIENTATION_PORTRAIT;
+ }
+ ev_view_set_orientation (view, new_orientation);
+}
+
static double
zoom_for_size_fit_width (int doc_width,
int doc_height,
GtkBorder border;
gdouble scale;
- ev_page_cache_get_max_width_size (view->page_cache,
- 1.0,
- &doc_width, NULL);
- ev_page_cache_get_max_height_size (view->page_cache,
- 1.0,
- NULL, &doc_height);
+ ev_page_cache_get_max_width (view->page_cache,
+ 1.0,
+ &doc_width);
+ ev_page_cache_get_max_height (view->page_cache,
+ 1.0,
+ &doc_height);
compute_border (view, doc_width, doc_height, &border);
doc_width = doc_width * 2;
GtkBorder border;
gdouble scale;
- ev_page_cache_get_max_width_size (view->page_cache,
- 1.0,
- &doc_width, NULL);
- ev_page_cache_get_max_height_size (view->page_cache,
- 1.0,
- NULL, &doc_height);
+ ev_page_cache_get_max_width (view->page_cache,
+ 1.0,
+ &doc_width);
+ ev_page_cache_get_max_height (view->page_cache,
+ 1.0,
+ &doc_height);
compute_border (view, doc_width, doc_height, &border);
width -= (border.left + border.right + 2 * view->spacing);
int height,
int vsb_width,
int hsb_height)
-{
+{
g_return_if_fail (view->sizing_mode == EV_SIZING_FIT_WIDTH ||
view->sizing_mode == EV_SIZING_BEST_FIT);
g_return_if_fail (width >= 0);
}
ev_view_set_find_status (view, message);
+ g_free (message);
}
const char *
int n_results, n_pages;
EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
- page_cache = ev_document_get_page_cache (view->document);
+ page_cache = ev_page_cache_get (view->document);
n_results = ev_document_find_get_n_results (find, view->current_page);
n_pages = ev_page_cache_get_n_pages (page_cache);
EvDocumentFind *find = EV_DOCUMENT_FIND (view->document);
EvPageCache *page_cache;
- page_cache = ev_document_get_page_cache (view->document);
+ page_cache = ev_page_cache_get (view->document);
n_results = ev_document_find_get_n_results (find, view->current_page);
/*** Selections ***/
-static void
-compute_selections (EvView *view, GdkRectangle *view_rect)
+/* compute_new_selection_rect/text calculates the area currently selected by
+ * view_rect. each handles a different mode;
+ */
+static GList *
+compute_new_selection_rect (EvView *view,
+ GdkPoint *start,
+ GdkPoint *stop)
{
+ GdkRectangle view_rect;
int n_pages, i;
+ GList *list = NULL;
- clear_selection (view);
+ g_assert (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE);
+
+ view_rect.x = MIN (start->x, stop->x);
+ view_rect.y = MIN (start->y, stop->y);
+ view_rect.width = MAX (start->x, stop->x) - view_rect.x;
+ view_rect.width = MAX (start->y, stop->y) - view_rect.y;
n_pages = ev_page_cache_get_n_pages (view->page_cache);
+
for (i = 0; i < n_pages; i++) {
GdkRectangle page_area;
GtkBorder border;
-
+
if (get_page_extents (view, i, &page_area, &border)) {
GdkRectangle overlap;
- if (gdk_rectangle_intersect (&page_area, view_rect, &overlap)) {
+ if (gdk_rectangle_intersect (&page_area, &view_rect, &overlap)) {
EvViewSelection *selection;
selection = g_new0 (EvViewSelection, 1);
view_rect_to_doc_rect (view, &overlap, &page_area,
&(selection->rect));
- view->selections = g_list_append
- (view->selections, selection);
+ list = g_list_append (list, selection);
}
}
}
+
+ return list;
+}
+
+static gboolean
+gdk_rectangle_point_in (GdkRectangle *rectangle,
+ GdkPoint *point)
+{
+ return rectangle->x <= point->x &&
+ rectangle->y <= point->y &&
+ point->x < rectangle->x + rectangle->width &&
+ point->y < rectangle->y + rectangle->height;
+}
+
+static GList *
+compute_new_selection_text (EvView *view,
+ GdkPoint *start,
+ GdkPoint *stop)
+{
+ int n_pages, i, first, last;
+ GList *list = NULL;
+ EvViewSelection *selection;
+ gint width, height;
+
+ g_assert (view->selection_mode == EV_VIEW_SELECTION_TEXT);
+
+ n_pages = ev_page_cache_get_n_pages (view->page_cache);
+
+ /* First figure out the range of pages the selection
+ * affects. */
+ first = n_pages;
+ last = 0;
+ for (i = 0; i < n_pages; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ get_page_extents (view, i, &page_area, &border);
+ if (gdk_rectangle_point_in (&page_area, start) ||
+ gdk_rectangle_point_in (&page_area, stop)) {
+ if (first == n_pages)
+ first = i;
+ last = i;
+ }
+
+ }
+
+
+
+ /* Now create a list of EvViewSelection's for the affected
+ * pages. This could be an empty list, a list of just one
+ * page or a number of pages.*/
+ for (i = first; i < last + 1; i++) {
+ GdkRectangle page_area;
+ GtkBorder border;
+ GdkPoint *point;
+
+ ev_page_cache_get_size (view->page_cache, i,
+ 1.0, &width, &height);
+
+ selection = g_new0 (EvViewSelection, 1);
+ selection->page = i;
+ selection->rect.x1 = selection->rect.y1 = 0;
+ selection->rect.x2 = width;
+ selection->rect.y2 = height;
+
+ get_page_extents (view, i, &page_area, &border);
+
+ if (gdk_rectangle_point_in (&page_area, start))
+ point = start;
+ else
+ point = stop;
+
+ if (i == first)
+ view_point_to_doc_point (view, point, &page_area,
+ &selection->rect.x1,
+ &selection->rect.y1);
+
+ /* If the selection is contained within just one page,
+ * make sure we don't write 'start' into both points
+ * in selection->rect. */
+ if (first == last)
+ point = stop;
+
+ if (i == last)
+ view_point_to_doc_point (view, point, &page_area,
+ &selection->rect.x2,
+ &selection->rect.y2);
+
+ list = g_list_append (list, selection);
+ }
+
+ return list;
+}
+
+/* This function takes the newly calculated list, and figures out which regions
+ * have changed. It then queues a redraw approporiately.
+ */
+static void
+merge_selection_region (EvView *view,
+ GList *list)
+{
+
+ /* FIXME: actually write... */
+ clear_selection (view);
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+
+ view->selection_info.selections = list;
+ ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, list);
+}
+
+static void
+compute_selections (EvView *view,
+ GdkPoint *start,
+ GdkPoint *stop)
+{
+ GList *list;
+
+ if (view->selection_mode == EV_VIEW_SELECTION_RECTANGLE)
+ list = compute_new_selection_rect (view, start, stop);
+ else
+ list = compute_new_selection_text (view, start, stop);
+ merge_selection_region (view, list);
+}
+
+/* Free's the selection. It's up to the caller to queue redraws if needed.
+ */
+static void
+selection_free (EvViewSelection *selection)
+{
+ g_free (selection);
}
static void
clear_selection (EvView *view)
{
- g_list_foreach (view->selections, (GFunc)g_free, NULL);
- view->selections = NULL;
+ g_list_foreach (view->selection_info.selections, (GFunc)selection_free, NULL);
+ view->selection_info.selections = NULL;
+ view->selection_info.in_selection = FALSE;
}
selection->rect.x2 = width;
selection->rect.y2 = height;
- view->selections = g_list_append (view->selections, selection);
+ view->selection_info.selections = g_list_append (view->selection_info.selections, selection);
}
+ ev_pixbuf_cache_set_selection_list (view->pixbuf_cache, view->selection_info.selections);
gtk_widget_queue_draw (GTK_WIDGET (view));
}
ev_document_doc_mutex_lock ();
- for (l = ev_view->selections; l != NULL; l = l->next) {
+ for (l = ev_view->selection_info.selections; l != NULL; l = l->next) {
EvViewSelection *selection = (EvViewSelection *)l->data;
char *tmp;
clipboard = gtk_widget_get_clipboard (GTK_WIDGET (ev_view),
GDK_SELECTION_PRIMARY);
- if (ev_view->selections) {
+ if (ev_view->selection_info.selections) {
if (!gtk_clipboard_set_with_owner (clipboard,
targets,
G_N_ELEMENTS (targets),
case EV_VIEW_CURSOR_NORMAL:
gdk_window_set_cursor (widget->window, NULL);
break;
+ case EV_VIEW_CURSOR_IBEAM:
+ cursor = gdk_cursor_new_for_display (display, GDK_XTERM);
+ break;
case EV_VIEW_CURSOR_LINK:
cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
break;