X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Farc-context.cpp;h=e689c93db186669b3768243e8d755cb40536aa6a;hb=07f872bd2a4fee3c346819cbaf5af6627e698499;hp=032d7febb7375f75f29cc257806bd02033e483ff;hpb=6926d8baf046c184bbcc8d0a62ee00128d6415a0;p=inkscape.git diff --git a/src/arc-context.cpp b/src/arc-context.cpp index 032d7febb..e689c93db 100644 --- a/src/arc-context.cpp +++ b/src/arc-context.cpp @@ -1,20 +1,20 @@ -#define __SP_ARC_CONTEXT_C__ - -/** \file Ellipse drawing context. */ - -/* - * Authors: +/** @file + * @brief Ellipse drawing context + */ +/* Authors: * Mitsuru Oka * Lauris Kaplinski * bulia byak + * Johan Engelen * - * Copyright (C) 2000-2002 Lauris Kaplinski + * Copyright (C) 2000-2006 Authors * Copyright (C) 2000-2001 Ximian, Inc. - * Copyright (C) 2002 Mitsuru Oka * * Released under GNU GPL, read the file 'COPYING' for more information */ +#define __SP_ARC_CONTEXT_C__ + #ifdef HAVE_CONFIG_H # include #endif @@ -30,17 +30,18 @@ #include "desktop-handles.h" #include "snap.h" #include "pixmaps/cursor-ellipse.xpm" -#include "pixmaps/cursor-ellipse.pixbuf" #include "sp-metrics.h" #include "xml/repr.h" #include "xml/node-event-vector.h" #include "object-edit.h" -#include "prefs-utils.h" +#include "preferences.h" #include "message-context.h" #include "desktop.h" #include "desktop-style.h" #include "context-fns.h" #include "verbs.h" +#include "shape-editor.h" +#include "event-context.h" #include "arc-context.h" @@ -49,11 +50,13 @@ static void sp_arc_context_init(SPArcContext *arc_context); static void sp_arc_context_dispose(GObject *object); static void sp_arc_context_setup(SPEventContext *ec); +static void sp_arc_context_finish(SPEventContext *ec); static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent *event); static gint sp_arc_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); -static void sp_arc_drag(SPArcContext *ec, NR::Point pt, guint state); +static void sp_arc_drag(SPArcContext *ec, Geom::Point pt, guint state); static void sp_arc_finish(SPArcContext *ec); +static void sp_arc_cancel(SPArcContext *ec); static SPEventContextClass *parent_class; @@ -87,6 +90,7 @@ static void sp_arc_context_class_init(SPArcContextClass *klass) object_class->dispose = sp_arc_context_dispose; event_context_class->setup = sp_arc_context_setup; + event_context_class->finish = sp_arc_context_finish; event_context_class->root_handler = sp_arc_context_root_handler; event_context_class->item_handler = sp_arc_context_item_handler; } @@ -96,11 +100,6 @@ static void sp_arc_context_init(SPArcContext *arc_context) SPEventContext *event_context = SP_EVENT_CONTEXT(arc_context); event_context->cursor_shape = cursor_ellipse_xpm; - event_context->cursor_pixbuf = gdk_pixbuf_new_from_inline( - -1, - cursor_ellipse_pixbuf, - FALSE, - NULL); event_context->hot_x = 4; event_context->hot_y = 4; event_context->xp = 0; @@ -109,14 +108,25 @@ static void sp_arc_context_init(SPArcContext *arc_context) event_context->within_tolerance = false; event_context->item_to_select = NULL; - event_context->shape_repr = NULL; - event_context->shape_knot_holder = NULL; - arc_context->item = NULL; new (&arc_context->sel_changed_connection) sigc::connection(); } +static void sp_arc_context_finish(SPEventContext *ec) +{ + SPArcContext *ac = SP_ARC_CONTEXT(ec); + SPDesktop *desktop = ec->desktop; + + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); + sp_arc_finish(ac); + ac->sel_changed_connection.disconnect(); + + if (((SPEventContextClass *) parent_class)->finish) { + ((SPEventContextClass *) parent_class)->finish(ec); + } +} + static void sp_arc_context_dispose(GObject *object) { SPEventContext *ec = SP_EVENT_CONTEXT(object); @@ -127,16 +137,8 @@ static void sp_arc_context_dispose(GObject *object) ac->sel_changed_connection.disconnect(); ac->sel_changed_connection.~connection(); - if (ec->shape_knot_holder) { - sp_knot_holder_destroy(ec->shape_knot_holder); - ec->shape_knot_holder = NULL; - } - - if (ec->shape_repr) { // remove old listener - sp_repr_remove_listener_by_data(ec->shape_repr, ec); - Inkscape::GC::release(ec->shape_repr); - ec->shape_repr = 0; - } + delete ec->shape_editor; + ec->shape_editor = NULL; /* fixme: This is necessary because we do not grab */ if (ac->item) { @@ -148,14 +150,6 @@ static void sp_arc_context_dispose(GObject *object) G_OBJECT_CLASS(parent_class)->dispose(object); } -static Inkscape::XML::NodeEventVector ec_shape_repr_events = { - NULL, /* child_added */ - NULL, /* child_removed */ - ec_shape_event_attr_changed, - NULL, /* content_changed */ - NULL /* order_changed */ -}; - /** \brief Callback that processes the "changed" signal on the selection; destroys old and creates new knotholder. @@ -165,27 +159,9 @@ void sp_arc_context_selection_changed(Inkscape::Selection * selection, gpointer SPArcContext *ac = SP_ARC_CONTEXT(data); SPEventContext *ec = SP_EVENT_CONTEXT(ac); - if (ec->shape_knot_holder) { // desktroy knotholder - sp_knot_holder_destroy(ec->shape_knot_holder); - ec->shape_knot_holder = NULL; - } - - if (ec->shape_repr) { // remove old listener - sp_repr_remove_listener_by_data(ec->shape_repr, ec); - Inkscape::GC::release(ec->shape_repr); - ec->shape_repr = 0; - } - - SPItem *item = selection ? selection->singleItem() : NULL; - if (item) { - ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop); - Inkscape::XML::Node *shape_repr = SP_OBJECT_REPR(item); - if (shape_repr) { - ec->shape_repr = shape_repr; - Inkscape::GC::anchor(shape_repr); - sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec); - } - } + ec->shape_editor->unset_item(SH_KNOTHOLDER); + SPItem *item = selection->singleItem(); + ec->shape_editor->set_item(item, SH_KNOTHOLDER); } static void sp_arc_context_setup(SPEventContext *ec) @@ -197,16 +173,11 @@ static void sp_arc_context_setup(SPEventContext *ec) ((SPEventContextClass *) parent_class)->setup(ec); } - SPItem *item = selection->singleItem(); + ec->shape_editor = new ShapeEditor(ec->desktop); + SPItem *item = sp_desktop_selection(ec->desktop)->singleItem(); if (item) { - ec->shape_knot_holder = sp_item_knot_holder(item, ec->desktop); - Inkscape::XML::Node *shape_repr = SP_OBJECT_REPR(item); - if (shape_repr) { - ec->shape_repr = shape_repr; - Inkscape::GC::anchor(shape_repr); - sp_repr_add_listener(shape_repr, &ec_shape_repr_events, ec); - } + ec->shape_editor->set_item(item, SH_KNOTHOLDER); } ac->sel_changed_connection.disconnect(); @@ -214,11 +185,12 @@ static void sp_arc_context_setup(SPEventContext *ec) sigc::bind(sigc::ptr_fun(&sp_arc_context_selection_changed), (gpointer) ac) ); - if (prefs_get_int_attribute("tools.shapes", "selcue", 0) != 0) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/tools/shapes/selcue")) { ec->enableSelectionCue(); } - if (prefs_get_int_attribute("tools.shapes", "gradientdrag", 0) != 0) { + if (prefs->getBool("/tools/shapes/gradientdrag")) { ec->enableGrDrag(); } @@ -232,7 +204,7 @@ static gint sp_arc_context_item_handler(SPEventContext *event_context, SPItem *i switch (event->type) { case GDK_BUTTON_PRESS: - if (event->button.button == 1) { + if (event->button.button == 1 && !event_context->space_panning) { Inkscape::setup_for_drag_start(desktop, event_context, event); ret = TRUE; } @@ -256,29 +228,35 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent SPDesktop *desktop = event_context->desktop; Inkscape::Selection *selection = sp_desktop_selection(desktop); SPArcContext *ac = SP_ARC_CONTEXT(event_context); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - event_context->tolerance = prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); + event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); gint ret = FALSE; switch (event->type) { case GDK_BUTTON_PRESS: - if (event->button.button == 1) { + if (event->button.button == 1 && !event_context->space_panning) { dragging = true; ac->center = Inkscape::setup_for_drag_start(desktop, event_context, event); - SnapManager const &m = desktop->namedview->snap_manager; - ac->center = m.freeSnap(Inkscape::Snapper::SNAP_POINT, ac->center, ac->item).getPoint(); + /* Snap center */ + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + Geom::Point pt2g = to_2geom(ac->center); + m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); + ac->center = from_2geom(pt2g); + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK, + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK, NULL, event->button.time); ret = TRUE; } break; case GDK_MOTION_NOTIFY: - if (dragging && event->motion.state && GDK_BUTTON1_MASK) { + if (dragging && (event->motion.state & GDK_BUTTON1_MASK) && !event_context->space_panning) { if ( event_context->within_tolerance && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance ) @@ -290,19 +268,24 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent // motion notify coordinates as given (no snapping back to origin) event_context->within_tolerance = false; - NR::Point const motion_w(event->motion.x, event->motion.y); - NR::Point const motion_dt(desktop->w2d(motion_w)); + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + sp_arc_drag(ac, motion_dt, event->motion.state); + + gobble_motion_events(GDK_BUTTON1_MASK); + ret = TRUE; } break; case GDK_BUTTON_RELEASE: event_context->xp = event_context->yp = 0; - if (event->button.button == 1) { + if (event->button.button == 1 && !event_context->space_panning) { dragging = false; + sp_event_context_discard_delayed_snap_event(event_context); if (!event_context->within_tolerance) { // we've been dragging, finish the arc - sp_arc_finish(ac); + sp_arc_finish(ac); } else if (event_context->item_to_select) { // no dragging, select clicked item if any if (event->button.state & GDK_SHIFT_MASK) { @@ -331,10 +314,12 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent case GDK_Shift_R: case GDK_Meta_L: // Meta is when you press Shift+Alt (at least on my machine) case GDK_Meta_R: - sp_event_show_modifier_tip(event_context->defaultMessageContext(), event, - _("Ctrl: make circle or integer-ratio ellipse, snap arc/segment angle"), - _("Shift: draw around the starting point"), - NULL); + if (!dragging) { + sp_event_show_modifier_tip(event_context->defaultMessageContext(), event, + _("Ctrl: make circle or integer-ratio ellipse, snap arc/segment angle"), + _("Shift: draw around the starting point"), + NULL); + } break; case GDK_Up: case GDK_Down: @@ -352,8 +337,28 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent } break; case GDK_Escape: - sp_desktop_selection(desktop)->clear(); - //TODO: make dragging escapable by Esc + if (dragging) { + dragging = false; + sp_event_context_discard_delayed_snap_event(event_context); + // if drawing, cancel, otherwise pass it up for deselecting + sp_arc_cancel(ac); + ret = TRUE; + } + break; + case GDK_space: + if (dragging) { + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); + dragging = false; + sp_event_context_discard_delayed_snap_event(event_context); + if (!event_context->within_tolerance) { + // we've been dragging, finish the arc + sp_arc_finish(ac); + } + // do not return true, so that space would work switching to selector + } + break; + default: break; } @@ -387,7 +392,7 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent return ret; } -static void sp_arc_drag(SPArcContext *ac, NR::Point pt, guint state) +static void sp_arc_drag(SPArcContext *ac, Geom::Point pt, guint state) { SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; @@ -398,29 +403,75 @@ static void sp_arc_drag(SPArcContext *ac, NR::Point pt, guint state) } /* Create object */ - Inkscape::XML::Node *repr = sp_repr_new("svg:path"); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); repr->setAttribute("sodipodi:type", "arc"); /* Set style */ - sp_desktop_apply_style_tool(desktop, repr, "tools.shapes.arc", false); + sp_desktop_apply_style_tool(desktop, repr, "/tools/shapes/arc", false); ac->item = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr)); Inkscape::GC::release(repr); - ac->item->transform = SP_ITEM(desktop->currentRoot())->getRelativeTransform(desktop->currentLayer()); + ac->item->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse(); ac->item->updateRepr(); + + sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5); } - NR::Rect const r = Inkscape::snap_rectangular_box(desktop, ac->item, pt, ac->center, state); + bool ctrl_save = false; + if ((state & GDK_MOD1_MASK) && (state & GDK_CONTROL_MASK) && !(state & GDK_SHIFT_MASK)) { + // if Alt is pressed without Shift in addition to Control, temporarily drop the CONTROL mask + // so that the ellipse is not constrained to integer ratios + ctrl_save = true; + state = state ^ GDK_CONTROL_MASK; + } + Geom::Rect r = Inkscape::snap_rectangular_box(desktop, ac->item, pt, ac->center, state); + if (ctrl_save) { + state = state ^ GDK_CONTROL_MASK; + } - sp_canvas_force_full_redraws(desktop->canvas, 1); + Geom::Point dir = r.dimensions() / 2; + if (state & GDK_MOD1_MASK) { + /* With Alt let the ellipse pass through the mouse pointer */ + Geom::Point c = r.midpoint(); + if (!ctrl_save) { + if (fabs(dir[Geom::X]) > 1E-6 && fabs(dir[Geom::Y]) > 1E-6) { + Geom::Matrix const i2d (sp_item_i2d_affine (ac->item)); + Geom::Point new_dir = pt * i2d - c; + new_dir[Geom::X] *= dir[Geom::Y] / dir[Geom::X]; + double lambda = new_dir.length() / dir[Geom::Y]; + r = Geom::Rect (c - lambda*dir, c + lambda*dir); + } + } else { + /* with Alt+Ctrl (without Shift) we generate a perfect circle + with diameter click point <--> mouse pointer */ + double l = dir.length(); + Geom::Point d (l, l); + r = Geom::Rect (c - d, c + d); + } + } sp_arc_position_set(SP_ARC(ac->item), - r.midpoint()[NR::X], r.midpoint()[NR::Y], - r.dimensions()[NR::X] / 2, r.dimensions()[NR::Y] / 2); - - GString *xs = SP_PX_TO_METRIC_STRING(r.dimensions()[NR::X], desktop->namedview->getDefaultMetric()); - GString *ys = SP_PX_TO_METRIC_STRING(r.dimensions()[NR::Y], desktop->namedview->getDefaultMetric()); - ac->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("Ellipse: %s × %s; with Ctrl to make circle or integer-ratio ellipse; with Shift to draw around the starting point"), xs->str, ys->str); + r.midpoint()[Geom::X], r.midpoint()[Geom::Y], + r.dimensions()[Geom::X] / 2, r.dimensions()[Geom::Y] / 2); + + double rdimx = r.dimensions()[Geom::X]; + double rdimy = r.dimensions()[Geom::Y]; + GString *xs = SP_PX_TO_METRIC_STRING(rdimx, desktop->namedview->getDefaultMetric()); + GString *ys = SP_PX_TO_METRIC_STRING(rdimy, desktop->namedview->getDefaultMetric()); + if (state & GDK_CONTROL_MASK) { + int ratio_x, ratio_y; + if (fabs (rdimx) > fabs (rdimy)) { + ratio_x = (int) rint (rdimx / rdimy); + ratio_y = 1; + } else { + ratio_x = 1; + ratio_y = (int) rint (rdimy / rdimx); + } + ac->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Ellipse: %s × %s (constrained to ratio %d:%d); with Shift to draw around the starting point"), xs->str, ys->str, ratio_x, ratio_y); + } else { + ac->_message_context->setF(Inkscape::IMMEDIATE_MESSAGE, _("Ellipse: %s × %s; with Ctrl to make square or integer-ratio ellipse; with Shift to draw around the starting point"), xs->str, ys->str); + } g_string_free(xs, FALSE); g_string_free(ys, FALSE); } @@ -430,18 +481,47 @@ static void sp_arc_finish(SPArcContext *ac) ac->_message_context->clear(); if (ac->item != NULL) { - SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; + + SPGenericEllipse *ge = SP_GENERICELLIPSE(SP_ARC(ac->item)); + if (ge->rx.computed == 0 || ge->ry.computed == 0) { + sp_arc_cancel(ac); // Don't allow the creating of zero sized arc, for example when the start and and point snap to the snap grid point + return; + } + + SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; SP_OBJECT(ac->item)->updateRepr(); + sp_canvas_end_forced_full_redraws(desktop->canvas); + sp_desktop_selection(desktop)->set(ac->item); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC, + sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC, _("Create ellipse")); ac->item = NULL; + } +} - sp_canvas_clear_forced_full_redraws(desktop->canvas); +static void sp_arc_cancel(SPArcContext *ac) +{ + SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; + + sp_desktop_selection(desktop)->clear(); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); + + if (ac->item != NULL) { + SP_OBJECT(ac->item)->deleteObject(); + ac->item = NULL; } + + ac->within_tolerance = false; + ac->xp = 0; + ac->yp = 0; + ac->item_to_select = NULL; + + sp_canvas_end_forced_full_redraws(desktop->canvas); + + sp_document_cancel(sp_desktop_document(desktop)); }