X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Farc-context.cpp;h=1c480ecbc6efaeb971230fc2bce45977edf624a8;hb=9dc68827cbd515262ecb8d5ae8547d9e82c72e00;hp=10e38ee9a64473cf023854c062e125220851439f;hpb=7d1f87e5fe8f470b9e734264d803a5c3a51f05d4;p=inkscape.git diff --git a/src/arc-context.cpp b/src/arc-context.cpp index 10e38ee9a..1c480ecbc 100644 --- a/src/arc-context.cpp +++ b/src/arc-context.cpp @@ -1,20 +1,21 @@ -#define __SP_ARC_CONTEXT_C__ - -/** \file Ellipse drawing context. */ - -/* - * Authors: +/** @file + * @brief Ellipse drawing context + */ +/* Authors: * Mitsuru Oka * Lauris Kaplinski * bulia byak + * Johan Engelen + * Abhishek Sharma * - * 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 @@ -34,24 +35,31 @@ #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" +using Inkscape::DocumentUndo; + static void sp_arc_context_class_init(SPArcContextClass *klass); 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; @@ -85,6 +93,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; } @@ -101,15 +110,27 @@ static void sp_arc_context_init(SPArcContext *arc_context) event_context->tolerance = 0; event_context->within_tolerance = false; event_context->item_to_select = NULL; - - event_context->shape_repr = NULL; - event_context->shape_knot_holder = NULL; + event_context->tool_url = "/tools/shapes/arc"; 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); @@ -120,16 +141,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) { @@ -141,14 +154,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. @@ -158,48 +163,25 @@ 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) { SPArcContext *ac = SP_ARC_CONTEXT(ec); - Inkscape::Selection *selection = SP_DT_SELECTION(ec->desktop); + Inkscape::Selection *selection = sp_desktop_selection(ec->desktop); if (((SPEventContextClass *) parent_class)->setup) { ((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(); @@ -207,11 +189,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(); } @@ -225,7 +208,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; } @@ -247,31 +230,36 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent static bool dragging; SPDesktop *desktop = event_context->desktop; - Inkscape::Selection *selection = SP_DT_SELECTION(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); - ac->center = m.freeSnap(Inkscape::Snapper::SNAP_POINT, ac->center, ac->item).getPoint(); + /* Snap center */ + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + m.freeSnapReturnByRef(ac->center, Inkscape::SNAPSOURCE_NODE_HANDLE); + 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; + m.unSetup(); } 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 ) @@ -283,19 +271,32 @@ 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; + } else if (!sp_event_context_knot_mouseover(ac)){ + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); + m.unSetup(); } 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) { @@ -324,10 +325,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: @@ -345,8 +348,28 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent } break; case GDK_Escape: - SP_DT_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; } @@ -380,7 +403,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; @@ -390,28 +413,76 @@ static void sp_arc_drag(SPArcContext *ac, NR::Point pt, guint state) return; } - /* Create object */ - Inkscape::XML::Node *repr = sp_repr_new("svg:path"); + // Create object + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + 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); + // Set style + 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(desktop->currentLayer())->i2doc_affine().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_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); + 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 ((ac->item)->i2d_affine ()); + 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); + } + } - 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); + sp_arc_position_set(SP_ARC(ac->item), + 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); } @@ -421,17 +492,49 @@ static void sp_arc_finish(SPArcContext *ac) ac->_message_context->clear(); if (ac->item != NULL) { + + 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_DT_SELECTION(desktop)->set(ac->item); - sp_document_done(SP_DT_DOCUMENT(desktop)); + sp_canvas_end_forced_full_redraws(desktop->canvas); + + sp_desktop_selection(desktop)->set(ac->item); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC, + _("Create ellipse")); ac->item = NULL; } } +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); + + DocumentUndo::cancel(sp_desktop_document(desktop)); +} + /* Local Variables: @@ -442,4 +545,4 @@ static void sp_arc_finish(SPArcContext *ac) fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :