/** * position within the sheet in pixel coordinates * * coordinates are clamped to grid if grid is enabled * see snap_to_grid * zero point : top left corner of the window (not widget!) * * @param x horizontal, left to right * @param y vertical, top to bottom * @returns wether the position could be detected properly * * @attention never call in event handlers! */ gboolean sheet_get_pointer_pixel (Sheet *sheet, gdouble *x, gdouble *y) { GtkAdjustment *hadj = NULL, *vadj = NULL; gdouble x1, y1; gint _x, _y; GdkDeviceManager *device_manager; GdkDevice *device_pointer; GdkRectangle allocation; GdkDisplay *display; GdkWindow *window; // deprecated gtk_widget_get_pointer (GTK_WIDGET (sheet), &_x, &_y); // replaced by a code copied from evince if (G_UNLIKELY (!sheet || !gtk_widget_get_realized (GTK_WIDGET (sheet)))) { NG_DEBUG ("Widget is not realized."); return FALSE; } display = gtk_widget_get_display (GTK_WIDGET (sheet)); device_manager = gdk_display_get_device_manager (display); // gdk_device_manager_get_client_pointer // shall not be used within events device_pointer = gdk_device_manager_get_client_pointer (device_manager); window = gtk_widget_get_window (GTK_WIDGET (sheet)); if (!window) { NG_DEBUG ("Window is not realized."); return FALSE; } // even though above is all defined the below will always return NUL for // unknown reason and _x and _y are populated as expected gdk_window_get_device_position (window, device_pointer, &_x, &_y, NULL); #if 0 if (!window) { //fails always NG_DEBUG ("Window does not seem to be realized yet?"); return FALSE; } #else NG_DEBUG ("\n%p %p %p %p %i %i\n\n", display, device_manager, device_pointer, window, _x, _y); #endif gtk_widget_get_allocation (GTK_WIDGET (sheet), &allocation); _x -= allocation.x; _y -= allocation.y; x1 = (gdouble)_x; y1 = (gdouble)_y; if (!sheet_get_adjustments (sheet, &hadj, &vadj)) return FALSE; x1 += gtk_adjustment_get_value (hadj); y1 += gtk_adjustment_get_value (vadj); *x = x1; *y = y1; return TRUE; }
/* * position within the sheet in pixel coordinates * coordinates are clamped to grid if grid is enabled * see snap_to_grid * zero point : top left corner of the window (not widget!) * x : horizontal, left to right * y : vertical, top to bottom * returns wether the position could be detected properly */ gboolean sheet_get_pointer_pixel (Sheet *sheet, gdouble *x, gdouble *y) { GtkAdjustment *hadj, *vadj; gdouble x1, y1; gint _x, _y; GdkDeviceManager *device_manager; GdkDevice *device_pointer; GdkRectangle allocation; // deprecated gtk_widget_get_pointer (GTK_WIDGET (sheet), &_x, &_y); // replaced by a code copied from evince if (G_UNLIKELY (!sheet || !gtk_widget_get_realized (GTK_WIDGET (sheet)))) { NG_DEBUG ("widget not realized"); return FALSE; } device_manager = gdk_display_get_device_manager ( gtk_widget_get_display (GTK_WIDGET (sheet))); device_pointer = gdk_device_manager_get_client_pointer (device_manager); //FIXME add another check based on the following functions return val //FIXME gtkdoc says this shall not be used in event handlers gdk_window_get_device_position (gtk_widget_get_window (GTK_WIDGET (sheet)), device_pointer, &_x, &_y, NULL); if (!gtk_widget_get_has_window (GTK_WIDGET (sheet))) { NG_DEBUG ("some weird gtk window shit failed"); return FALSE; } gtk_widget_get_allocation (GTK_WIDGET (sheet), &allocation); _x -= allocation.x; _y -= allocation.y; x1 = (gdouble) _x; y1 = (gdouble) _y; if (!sheet_get_adjustments (sheet, &hadj, &vadj)) return FALSE; x1 += gtk_adjustment_get_value (hadj); y1 += gtk_adjustment_get_value (vadj); *x = x1; *y = y1; return TRUE; }
/* * scroll to <dx,dy> in pixels relative to the current coords * note that pixels are _not_ affected by zoom */ void sheet_scroll_pixel (const Sheet *sheet, int delta_x, int delta_y) { GtkAdjustment *hadj, *vadj; GtkAllocation allocation; gfloat vnew, hnew; gfloat hmax, vmax; gfloat x1, y1; const SheetPriv *priv = sheet->priv; if (sheet_get_adjustments (sheet, &hadj, &vadj)) { x1 = gtk_adjustment_get_value (hadj); y1 = gtk_adjustment_get_value (vadj); } else { x1 = y1 = 0.f; } gtk_widget_get_allocation (GTK_WIDGET (sheet), &allocation); if (priv->width > allocation.width) hmax = (gfloat) (priv->width - allocation.width); else hmax = 0.f; if (priv->height > allocation.height) vmax = (gfloat) (priv->height - allocation.height); else vmax = 0.f; hnew = CLAMP (x1 + (gfloat) delta_x, 0.f, hmax); vnew = CLAMP (y1 + (gfloat) delta_y, 0.f, vmax); if (hnew != x1) { gtk_adjustment_set_value (hadj, hnew); g_signal_emit_by_name (G_OBJECT (hadj), "value_changed"); } if (vnew != y1) { gtk_adjustment_set_value (vadj, vnew); g_signal_emit_by_name (G_OBJECT (vadj), "value_changed"); } }
/* * change the zoom by factor <rate> * zoom origin when zooming in is the cursor * zoom origin when zooming out is the center of the current viewport * sane <rate> values are in range of [0.5 .. 2] */ void sheet_change_zoom (Sheet *sheet, gdouble rate) { g_return_if_fail (sheet); g_return_if_fail (IS_SHEET (sheet)); //////////////////////////////////////////////7 gdouble x, y; gdouble rx, ry; gdouble px, py; gdouble dx, dy; gdouble cx, cy; gdouble dcx, dcy; GtkAdjustment *hadj, *vadj; GooCanvas *canvas; canvas = GOO_CANVAS (sheet); // if we scroll out, just scroll to the center if (rate < 1.) { goo_canvas_set_scale (canvas, rate * goo_canvas_get_scale (canvas)); return; } // top left corner in pixels if (sheet_get_adjustments (sheet, &hadj, &vadj)) { x = gtk_adjustment_get_value (hadj); y = gtk_adjustment_get_value (vadj); } else { x = y = 0.; } // get pointer position in pixels sheet_get_pointer_pixel (sheet, &px, &py); // get the page size in pixels dx = gtk_adjustment_get_page_size (hadj); dy = gtk_adjustment_get_page_size (vadj); // calculate the center of the widget in pixels cx = x + dx/2; cy = y + dy/2; // calculate the delta between the center and the pointer in pixels // this is required as the center is the zoom target dcx = px - cx; dcy = py - cy; // increase the top left position in pixels by our calculated delta x += dcx; y += dcy; //convert to canvas coords goo_canvas_convert_from_pixels (canvas, &x, &y); //the center of the canvas is now our cursor position goo_canvas_scroll_to (canvas, x, y); //calculate a correction term //for the case that we can not scroll the pane far enough to //compensate the whole off-due-to-wrong-center-error rx = gtk_adjustment_get_value (hadj); ry = gtk_adjustment_get_value (vadj); goo_canvas_convert_from_pixels (canvas, &rx, &ry); //the correction term in goo coordinates, to be subtracted from the backscroll distance rx -= x; ry -= y; // no the center is our cursor position and we can safely call scale goo_canvas_set_scale (canvas, rate * goo_canvas_get_scale (canvas)); // top left corner in pixels after scaling if (sheet_get_adjustments (sheet, &hadj, &vadj)) { x = gtk_adjustment_get_value (hadj); y = gtk_adjustment_get_value (vadj); } else { x = y = 0.; } // not sure if the below part is required, could be zer0 NG_DEBUG ("rx %lf\n", rx); NG_DEBUG ("ry %lf\n", ry); NG_DEBUG ("dcx %lf\n", dcx); NG_DEBUG ("dcy %lf\n", dcy); NG_DEBUG ("\n\n"); // gtk_adjustment_get_page_size is constant x -= (dcx) / sheet->priv->zoom; y -= (dcy) / sheet->priv->zoom; goo_canvas_convert_from_pixels (canvas, &x, &y); goo_canvas_scroll_to (canvas, x-rx, y-ry); gtk_widget_queue_draw (GTK_WIDGET (canvas)); }
/** * change the zoom by factor * * zoom origin when zooming in is the cursor * zoom origin when zooming out is the center of the current viewport * * @param sheet * @param factor values should be in the range of [0.5 .. 2] */ void sheet_change_zoom (Sheet *sheet, gdouble factor) { g_return_if_fail (sheet); g_return_if_fail (IS_SHEET (sheet)); Coords adju, r, pointer, delta, center, pagesize; GtkAdjustment *hadj = NULL, *vadj = NULL; GooCanvas *canvas; gboolean b = FALSE; canvas = GOO_CANVAS (sheet); // if we scroll out, just use the center as focus // mouse curser centered scroll out "feels" awkward if (factor < 1.) { goo_canvas_set_scale (canvas, factor * goo_canvas_get_scale (canvas)); return; } // get pointer position in pixels // just skip the correction if we can not get the pointer if (!sheet_get_pointer_pixel (sheet, &pointer.x, &pointer.y)) { goo_canvas_set_scale (canvas, factor * goo_canvas_get_scale (canvas)); g_warning ("Failed to get cursor position."); return; } // top left corner in pixels b = sheet_get_adjustments (sheet, &hadj, &vadj); if (b) { adju.x = gtk_adjustment_get_value (hadj); adju.y = gtk_adjustment_get_value (vadj); // get the page size in pixels pagesize.x = gtk_adjustment_get_page_size (hadj); pagesize.y = gtk_adjustment_get_page_size (vadj); } else { //FIXME untested codepath, check for variable space conversion //FIXME Pixel vs GooUnits gdouble left, right, top, bottom; goo_canvas_get_bounds (canvas, &left, &top, &right, &bottom); pagesize.x = bottom - top; pagesize.y = right - left; adju.x = adju.y = 0.; } // calculate the center of the widget in pixels center.x = adju.x + pagesize.x/2.; center.y = adju.y + pagesize.y/2.; // calculate the delta between the center and the pointer in pixels // this is required as the center is the zoom target delta.x = pointer.x - center.x; delta.y = pointer.y - center.y; // increase the top left position in pixels by our calculated delta adju.x += delta.x; adju.y += delta.y; //convert to canvas coords goo_canvas_convert_from_pixels (canvas, &adju.x, &adju.y); //the center of the canvas is now our cursor position goo_canvas_scroll_to (canvas, adju.x, adju.y); //calculate a correction term //for the case that we can not scroll the pane far enough to //compensate the whole off-due-to-wrong-center-error if (b) { r.x = gtk_adjustment_get_value (hadj); r.y = gtk_adjustment_get_value (vadj); goo_canvas_convert_from_pixels (canvas, &r.x, &r.y); //the correction term in goo coordinates, to be subtracted from the backscroll distance r.x -= adju.x; r.y -= adju.y; } else { r.x = r.y = 0.; } // no the center is our cursor position and we can safely call scale goo_canvas_set_scale (canvas, factor * goo_canvas_get_scale (canvas)); // top left corner in pixels after scaling if (b) { adju.x = gtk_adjustment_get_value (hadj); adju.y = gtk_adjustment_get_value (vadj); } else { adju.x = adju.y = 0.; } // gtk_adjustment_get_page_size is constant before and after scale adju.x -= (delta.x) / sheet->priv->zoom; adju.y -= (delta.y) / sheet->priv->zoom; goo_canvas_convert_from_pixels (canvas, &adju.x, &adju.y); goo_canvas_scroll_to (canvas, adju.x-r.x, adju.y-r.y); gtk_widget_queue_draw (GTK_WIDGET (canvas)); }