diff options
| author | Liam P. White <inkscapebrony@gmail.com> | 2014-08-08 15:04:56 +0000 |
|---|---|---|
| committer | Liam P. White <inkscapebrony@gmail.com> | 2014-08-08 15:04:56 +0000 |
| commit | ca8023872ae0d9f7b99688502b9bdba232ec5627 (patch) | |
| tree | 73092b0aa6ea3e697da66d1d6316d3a2d78a4f3e /src | |
| parent | Update to experimental r13440 (diff) | |
| parent | Small tweak to bbox calculation (diff) | |
| download | inkscape-ca8023872ae0d9f7b99688502b9bdba232ec5627.tar.gz inkscape-ca8023872ae0d9f7b99688502b9bdba232ec5627.zip | |
Update to experimental r13464
(bzr r13341.5.14)
Diffstat (limited to 'src')
92 files changed, 1408 insertions, 654 deletions
diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp index fc4d72028..3558af3b3 100644 --- a/src/2geom/path.cpp +++ b/src/2geom/path.cpp @@ -110,7 +110,7 @@ Path &Path::operator*=(Translate const &m) { Sequence::iterator it; Point prev; for (it = get_curves().begin() ; it != last ; ++it) { - //*(const_cast<Curve*>(&**it)) *= m; + // *(const_cast<Curve*>(&**it)) *= m; const_cast<Curve*>(it->get())->operator*=(m); if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) { THROW_CONTINUITYERROR(); diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp index ccc383920..932f95829 100644 --- a/src/2geom/svg-path-parser.cpp +++ b/src/2geom/svg-path-parser.cpp @@ -1,5 +1,3 @@ - -#line 1 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" /** * \file * \brief parse SVG path specifications @@ -130,6 +128,9 @@ private: void _arcTo(double rx, double ry, double angle, bool large_arc, bool sweep, Point p) { + if (are_near(_current, p)) { + return; + } _quad_tangent = _cubic_tangent = _current = p; _sink.arcTo(rx, ry, angle, large_arc, sweep, p); } @@ -141,7 +142,6 @@ private: }; -#line 145 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" static const char _svg_path_actions[] = { 0, 1, 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 15, 1, @@ -1147,9 +1147,6 @@ static const int svg_path_first_final = 270; //static const int svg_path_en_main = 1; -#line 144 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" - - void Parser::parse(char const *str) throw(SVGPathParseError) { @@ -1159,13 +1156,11 @@ throw(SVGPathParseError) _reset(); - -#line 1164 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" { cs = svg_path_start; } -#line 1169 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" + { int _klen; unsigned int _trans; @@ -1238,13 +1233,12 @@ _match: switch ( *_acts++ ) { case 0: -#line 156 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { start = p; } break; case 1: -#line 160 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { char const *end=p; std::string buf(start, end); @@ -1253,55 +1247,49 @@ _match: } break; case 2: -#line 167 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { _push(1.0); } break; case 3: -#line 171 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { _push(0.0); } break; case 4: -#line 175 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" + { _absolute = true; } break; case 5: -#line 179 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _absolute = false; } break; case 6: -#line 183 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _moveTo(_pop_point()); } break; case 7: -#line 187 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _lineTo(_pop_point()); } break; case 8: -#line 191 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _hlineTo(Point(_pop_coord(X), _current[Y])); } break; case 9: -#line 195 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _vlineTo(Point(_current[X], _pop_coord(Y))); } break; case 10: -#line 199 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); Point c1 = _pop_point(); @@ -1310,7 +1298,6 @@ _match: } break; case 11: -#line 206 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); Point c1 = _pop_point(); @@ -1318,7 +1305,6 @@ _match: } break; case 12: -#line 212 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); Point c = _pop_point(); @@ -1326,14 +1312,12 @@ _match: } break; case 13: -#line 218 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point p = _pop_point(); _quadTo(_quad_tangent, p); } break; case 14: -#line 223 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { Point point = _pop_point(); bool sweep = _pop_flag(); @@ -1346,16 +1330,13 @@ _match: } break; case 15: -#line 234 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" { _closePath(); } break; case 16: -#line 370 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" {{p++; goto _out; }} break; -#line 1359 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp" } } @@ -1367,8 +1348,6 @@ _again: _out: {} } -#line 380 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl" - if ( cs < svg_path_first_final ) { throw SVGPathParseError(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3dcbb8e94..2d293020e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -218,6 +218,7 @@ set(inkscape_SRC knot-holder-entity.cpp knot.cpp knotholder.cpp + knot-ptr.cpp layer-fns.cpp layer-manager.cpp layer-model.cpp @@ -355,6 +356,7 @@ set(inkscape_SRC knot-holder-entity.h knot.h knotholder.h + knot-ptr.h layer-fns.h layer-manager.h layer-model.h diff --git a/src/Makefile_insert b/src/Makefile_insert index 2f28233d5..f1185e96d 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -80,6 +80,7 @@ ink_common_sources += \ knot-enums.h \ knotholder.cpp knotholder.h \ knot-holder-entity.h knot-holder-entity.cpp \ + knot-ptr.h knot-ptr.cpp \ layer-fns.cpp layer-fns.h \ layer-manager.cpp layer-manager.h \ layer-model.cpp layer-model.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index da7b25f03..2474e4abe 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -413,10 +413,6 @@ static SPStyleProp const props[] = { /* Text */ {SP_PROP_TEXT_INDENT, "text-indent"}, {SP_PROP_TEXT_ALIGN, "text-align"}, - {SP_PROP_TEXT_DECORATION, "text-decoration"}, - {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, - {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"}, - {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"}, {SP_PROP_LINE_HEIGHT, "line-height"}, {SP_PROP_LETTER_SPACING, "letter-spacing"}, {SP_PROP_WORD_SPACING, "word-spacing"}, @@ -433,6 +429,12 @@ static SPStyleProp const props[] = { {SP_PROP_GLYPH_ORIENTATION_VERTICAL, "glyph-orientation-vertical"}, {SP_PROP_KERNING, "kerning"}, {SP_PROP_TEXT_ANCHOR, "text-anchor"}, + {SP_PROP_WHITE_SPACE, "white-space"}, + /* Text Decoration */ + {SP_PROP_TEXT_DECORATION, "text-decoration"}, + {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, + {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"}, + {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"}, /* Misc */ {SP_PROP_CLIP, "clip"}, {SP_PROP_COLOR, "color"}, @@ -440,7 +442,7 @@ static SPStyleProp const props[] = { {SP_PROP_DISPLAY, "display"}, {SP_PROP_OVERFLOW, "overflow"}, {SP_PROP_VISIBILITY, "visibility"}, - {SP_PROP_BLEND_MODE, "mix-blend-mode"}, // CSS Blending and Compositing + {SP_PROP_MIX_BLEND_MODE, "mix-blend-mode"}, // CSS Blending and Compositing {SP_PROP_ISOLATION, "isolation"}, /* SVG */ /* Clip/Mask */ diff --git a/src/attributes.h b/src/attributes.h index 82e7ca8a6..7f18cb5ea 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -399,9 +399,11 @@ enum SPAttributeEnum { SP_ATTR_TEXT_EXCLUDE, SP_ATTR_LAYOUT_OPTIONS, - /* CSS2 */ - /* Custom full font name because Font stuff below is inadequate */ + /* CSS & SVG Properties */ + + /* Custom full font name because Font stuff below is inadequate REMOVE ME */ SP_PROP_INKSCAPE_FONT_SPEC, + /* Font */ SP_PROP_FONT, SP_PROP_FONT_FAMILY, @@ -411,18 +413,16 @@ enum SPAttributeEnum { SP_PROP_FONT_STYLE, SP_PROP_FONT_VARIANT, SP_PROP_FONT_WEIGHT, - /* Text */ + + /* Text Layout */ SP_PROP_TEXT_INDENT, SP_PROP_TEXT_ALIGN, - SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */ - SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */ - SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/ - SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/ + SP_PROP_LINE_HEIGHT, SP_PROP_LETTER_SPACING, SP_PROP_WORD_SPACING, SP_PROP_TEXT_TRANSFORM, - /* text (css3) */ + SP_PROP_DIRECTION, SP_PROP_BLOCK_PROGRESSION, SP_PROP_WRITING_MODE, @@ -434,6 +434,14 @@ enum SPAttributeEnum { SP_PROP_GLYPH_ORIENTATION_VERTICAL, SP_PROP_KERNING, SP_PROP_TEXT_ANCHOR, + SP_PROP_WHITE_SPACE, + + /* Text Decoration */ + SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */ + SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */ + SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/ + SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/ + /* Misc */ SP_PROP_CLIP, SP_PROP_COLOR, @@ -441,26 +449,31 @@ enum SPAttributeEnum { SP_PROP_DISPLAY, SP_PROP_OVERFLOW, SP_PROP_VISIBILITY, - SP_PROP_BLEND_MODE, + SP_PROP_MIX_BLEND_MODE, SP_PROP_ISOLATION, + /* SVG */ /* Clip/Mask */ SP_PROP_CLIP_PATH, SP_PROP_CLIP_RULE, SP_PROP_MASK, SP_PROP_OPACITY, + /* Filter */ SP_PROP_ENABLE_BACKGROUND, SP_PROP_FILTER, SP_PROP_FLOOD_COLOR, SP_PROP_FLOOD_OPACITY, SP_PROP_LIGHTING_COLOR, + /* Gradient */ SP_PROP_STOP_COLOR, SP_PROP_STOP_OPACITY, SP_PROP_STOP_PATH, + /* Interactivity */ SP_PROP_POINTER_EVENTS, + /* Paint */ SP_PROP_COLOR_INTERPOLATION, SP_PROP_COLOR_INTERPOLATION_FILTERS, @@ -487,10 +500,12 @@ enum SPAttributeEnum { SP_PROP_STROKE_OPACITY, SP_PROP_STROKE_WIDTH, SP_PROP_TEXT_RENDERING, + /* Conditional */ SP_PROP_SYSTEM_LANGUAGE, SP_PROP_REQUIRED_FEATURES, SP_PROP_REQUIRED_EXTENSIONS, + /* LivePathEffect */ SP_PROP_PATH_EFFECT, }; diff --git a/src/box3d.cpp b/src/box3d.cpp index a47dce1b3..5598bc83d 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -672,15 +672,6 @@ box3d_aux_set_z_orders (int z_orders[6], int a, int b, int c, int d, int e, int z_orders[5] = f; } -static inline void -box3d_swap_z_orders (int z_orders[6]) { - int tmp; - for (int i = 0; i < 3; ++i) { - tmp = z_orders[i]; - z_orders[i] = z_orders[5-i]; - z_orders[5-i] = tmp; - } -} /* * In standard perspective we have: @@ -695,11 +686,6 @@ box3d_swap_z_orders (int z_orders[6]) { /* All VPs infinite */ static void box3d_set_new_z_orders_case0 (SPBox3D *box, int z_orders[6], Box3D::Axis central_axis) { - Persp3D *persp = box3d_get_perspective(box); - Geom::Point xdir(persp3d_get_infinite_dir(persp, Proj::X)); - Geom::Point ydir(persp3d_get_infinite_dir(persp, Proj::Y)); - Geom::Point zdir(persp3d_get_infinite_dir(persp, Proj::Z)); - bool swapped = box3d_XY_axes_are_swapped(box); switch(central_axis) { @@ -811,12 +797,7 @@ box3d_set_new_z_orders_case1 (SPBox3D *box, int z_orders[6], Box3D::Axis central /* Precisely 2 finite VPs */ static void box3d_set_new_z_orders_case2 (SPBox3D *box, int z_orders[6], Box3D::Axis central_axis, Box3D::Axis /*infinite_axis*/) { - Persp3D *persp = box3d_get_perspective(box); - Geom::Point c3(box3d_get_corner_screen(box, 3, false)); - Geom::Point xdir(persp3d_get_PL_dir_from_pt(persp, c3, Proj::X)); - Geom::Point ydir(persp3d_get_PL_dir_from_pt(persp, c3, Proj::Y)); - Geom::Point zdir(persp3d_get_PL_dir_from_pt(persp, c3, Proj::Z)); bool swapped = box3d_XY_axes_are_swapped(box); diff --git a/src/connection-pool.h b/src/connection-pool.h index b1ac6f07a..4637a3cc1 100644 --- a/src/connection-pool.h +++ b/src/connection-pool.h @@ -1,84 +1,100 @@ +/* + * Author: + * mderezynski <mderezynski@users.sourceforge.net> + * + * Copyright (C) 2006 Author + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + #ifndef CONNECTION_POOL_H #define CONNECTION_POOL_H -#include <map> #include <glib-object.h> -#include <stddef.h> -#include <string> #include <sigc++/sigc++.h> -namespace Inkscape -{ -class ConnectionPool -{ - public: +#include <map> +#include <stddef.h> +#include <string> +#include <exception> + +namespace Inkscape { + +/** + * @class ConnectionPool + * an auxilliary class to manage sigc::connections as if the referred object + * were a GObject; in that way, the class that holds a ConnectionPool does not + * need to also hold several sigc::connection objects + */ +class ConnectionPool { +public: + typedef std::map<std::string, sigc::connection*> ConnectionMap; - enum Exception - { - NAME_EXISTS, - NAME_DOES_NOT_EXIST + struct NameExistsException : public std::exception { + virtual const char* what() const throw() { return "Inkscape::ConnectionPool: name exists"; } + }; + struct NameDoesNotExistException : public std::exception { + virtual const char* what() const throw() { return "Inkscape::ConnectionPool: name doesn't exist"; } }; - typedef std::map<std::string, sigc::connection*> ConnectionMap; - - virtual ~ConnectionPool () - { - for (ConnectionMap::iterator iter = map.begin (), end = map.end (); iter != end; ++iter) { - sigc::connection* connection = (*iter).second; - connection->disconnect (); - delete connection; + void add_connection(std::string name, sigc::connection* connection) { + if (_map.find(name) != _map.end()) { + throw NameExistsException(); } + _map.insert(std::make_pair(name, connection)); } - void - add_connection (std::string name, sigc::connection* connection) - { - if (map.find (name) != map.end ()) throw NAME_EXISTS; - map.insert (std::make_pair (name, connection)); - } - - void - del_connection (std::string name) - { - ConnectionMap::iterator iter = map.find (name); - if (iter == map.end ()) throw NAME_DOES_NOT_EXIST; - sigc::connection* connection = (*iter).second; - connection->disconnect (); - delete connection; + void del_connection(std::string name) { + ConnectionMap::iterator iter = _map.find(name); + if (iter == _map.end()) { + throw NameDoesNotExistException(); + } + sigc::connection* connection = (*iter).second; + connection->disconnect(); + delete connection; } - static Inkscape::ConnectionPool* - new_connection_pool (std::string name) - { - return new ConnectionPool (name); + static Inkscape::ConnectionPool* new_connection_pool(std::string name) { + return new ConnectionPool(name); } - static void - del_connection_pool (Inkscape::ConnectionPool* pool) - { - delete pool; + static void del_connection_pool(ConnectionPool* pool) { + delete pool; } - static void - connect_destroy (GObject *obj, Inkscape::ConnectionPool *pool) - { - g_object_connect (obj, "swapped-signal::destroy", G_CALLBACK (del_connection_pool), pool, NULL); + static void connect_destroy(GObject *obj, ConnectionPool *pool) { + g_object_connect (obj, "swapped-signal::destroy", G_CALLBACK(del_connection_pool), pool, NULL); } - operator std::string () - { - return name; + operator std::string() { + return _name; } - private: - - ConnectionPool (std::string name) : name (name) - {} +private: + ConnectionPool(std::string name) : _name(name) {} + virtual ~ConnectionPool() { + for (ConnectionMap::iterator iter = _map.begin(), end = _map.end(); iter != end; ++iter) { + sigc::connection* connection = (*iter).second; + connection->disconnect(); + delete connection; + } + } - ConnectionMap map; - std::string name; + ConnectionMap _map; + std::string _name; }; } #endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index 0129a382d..8be5e001b 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -365,7 +365,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) if (event->motion.state & GDK_CONTROL_MASK) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); - bool const relative_snaps = abs(prefs->getBool("/options/relativeguiderotationsnap/value", false)); + bool const relative_snaps = prefs->getBool("/options/relativeguiderotationsnap/value", false); if (snaps) { if (relative_snaps) { Geom::Angle orig_angle(guide->normal_to_line); @@ -442,7 +442,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) if (event->motion.state & GDK_CONTROL_MASK) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); - bool const relative_snaps = abs(prefs->getBool("/options/relativeguiderotationsnap/value", false)); + bool const relative_snaps = prefs->getBool("/options/relativeguiderotationsnap/value", false); if (snaps) { if (relative_snaps) { Geom::Angle orig_angle(guide->normal_to_line); @@ -491,7 +491,6 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) // set move or rotate cursor Geom::Point const event_w(event->crossing.x, event->crossing.y); - Geom::Point const event_dt(desktop->w2d(event_w)); if ((event->crossing.state & GDK_SHIFT_MASK) && (drag_type != SP_DRAG_MOVE_ORIGIN)) { GdkCursor *guide_cursor; diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index 5b358ade7..d6ff7b2f0 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -19,6 +19,7 @@ #include <glibmm/fileutils.h> #include <2geom/pathvector.h> #include <2geom/bezier-curve.h> +#include <2geom/elliptical-arc.h> #include <2geom/hvlinesegment.h> #include <2geom/affine.h> #include <2geom/point.h> @@ -502,7 +503,17 @@ void Pixbuf::ensurePixelFormat(PixelFormat fmt) static void feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & trans, Geom::Rect view, bool optimize_stroke) { - if( is_straight_curve(c) ) + using Geom::X; + using Geom::Y; + + unsigned order = 0; + if (Geom::BezierCurve const* b = dynamic_cast<Geom::BezierCurve const*>(&c)) { + order = b->order(); + } + + // handle the three typical curve cases + switch (order) { + case 1: { Geom::Point end_tr = c.finalPoint() * trans; if (!optimize_stroke) { @@ -516,57 +527,97 @@ feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & tran } } } - else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const*>(&c)) { + break; + case 2: + { + Geom::QuadraticBezier const *quadratic_bezier = static_cast<Geom::QuadraticBezier const*>(&c); std::vector<Geom::Point> points = quadratic_bezier->points(); points[0] *= trans; points[1] *= trans; points[2] *= trans; + // degree-elevate to cubic Bezier, since Cairo doesn't do quadratic Beziers Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]); Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]); if (!optimize_stroke) { - cairo_curve_to(cr, b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); + cairo_curve_to(cr, b1[X], b1[Y], b2[X], b2[Y], points[2][X], points[2][Y]); } else { Geom::Rect swept(points[0], points[2]); swept.expandTo(points[1]); if (swept.intersects(view)) { - cairo_curve_to(cr, b1[0], b1[1], b2[0], b2[1], points[2][0], points[2][1]); + cairo_curve_to(cr, b1[X], b1[Y], b2[X], b2[Y], points[2][X], points[2][Y]); } else { - cairo_move_to(cr, points[2][0], points[2][1]); + cairo_move_to(cr, points[2][X], points[2][Y]); } } } - else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) { + break; + case 3: + { + Geom::CubicBezier const *cubic_bezier = static_cast<Geom::CubicBezier const*>(&c); std::vector<Geom::Point> points = cubic_bezier->points(); //points[0] *= trans; // don't do this one here for fun: it is only needed for optimized strokes points[1] *= trans; points[2] *= trans; points[3] *= trans; if (!optimize_stroke) { - cairo_curve_to(cr, points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); + cairo_curve_to(cr, points[1][X], points[1][Y], points[2][X], points[2][Y], points[3][X], points[3][Y]); } else { points[0] *= trans; // didn't transform this point yet Geom::Rect swept(points[0], points[3]); swept.expandTo(points[1]); swept.expandTo(points[2]); if (swept.intersects(view)) { - cairo_curve_to(cr, points[1][0], points[1][1], points[2][0], points[2][1], points[3][0], points[3][1]); + cairo_curve_to(cr, points[1][X], points[1][Y], points[2][X], points[2][Y], points[3][X], points[3][Y]); } else { - cairo_move_to(cr, points[3][0], points[3][1]); + cairo_move_to(cr, points[3][X], points[3][Y]); } } } -// else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc *>(c)) { -// //TODO: get at the innards and spit them out to cairo -// } - else { - //this case handles sbasis as well as all other curve types - Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); - - //recurse to convert the new path resulting from the sbasis to svgd - for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { - feed_curve_to_cairo(cr, *iter, trans, view, optimize_stroke); + break; + default: + { + if (Geom::EllipticalArc const *a = dynamic_cast<Geom::EllipticalArc const*>(&c)) { + //if (!optimize_stroke || a->boundsFast().intersects(view)) { + Geom::Affine xform = a->unitCircleTransform() * trans; + Geom::Point ang(a->initialAngle().radians(), a->finalAngle().radians()); + + // Apply the transformation to the current context + cairo_matrix_t cm; + cm.xx = xform[0]; + cm.xy = xform[2]; + cm.x0 = xform[4]; + cm.yx = xform[1]; + cm.yy = xform[3]; + cm.y0 = xform[5]; + + cairo_save(cr); + cairo_transform(cr, &cm); + + // Draw the circle + if (a->sweep()) { + cairo_arc(cr, 0, 0, 1, ang[0], ang[1]); + } else { + cairo_arc_negative(cr, 0, 0, 1, ang[0], ang[1]); + } + // Revert the current context + cairo_restore(cr); + //} else { + // Geom::Point f = a->finalPoint() * trans; + // cairo_move_to(cr, f[X], f[Y]); + //} + } else { + // handles sbasis as well as all other curve types + // this is very slow + Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); + + // recurse to convert the new path resulting from the sbasis to svgd + for (Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { + feed_curve_to_cairo(cr, *iter, trans, view, optimize_stroke); + } } } + break; + } } @@ -579,7 +630,7 @@ feed_path_to_cairo (cairo_t *ct, Geom::Path const &path) cairo_move_to(ct, path.initialPoint()[0], path.initialPoint()[1] ); - for(Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) { + for (Geom::Path::const_iterator cit = path.begin(); cit != path.end_open(); ++cit) { feed_curve_to_cairo(ct, *cit, Geom::identity(), Geom::Rect(), false); // optimize_stroke is false, so the view rect is not used } diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 0bfb00b62..80eb69546 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -127,7 +127,7 @@ DrawingItem::DrawingItem(Drawing &drawing) , _pick_children(0) , _antialias(1) , _isolation(SP_CSS_ISOLATION_AUTO) - , _blend_mode(SP_CSS_BLEND_NORMAL) + , _mix_blend_mode(SP_CSS_BLEND_NORMAL) {} DrawingItem::~DrawingItem() @@ -291,10 +291,10 @@ DrawingItem::setIsolation(unsigned isolation) } void -DrawingItem::setBlendMode(unsigned blend_mode) +DrawingItem::setBlendMode(unsigned mix_blend_mode) { - _blend_mode = blend_mode; - //if( blend_mode != 0 ) std::cout << "setBlendMode: " << blend_mode << std::endl; + _mix_blend_mode = mix_blend_mode; + //if( mix_blend_mode != 0 ) std::cout << "setBlendMode: " << mix_blend_mode << std::endl; _markForRendering(); } @@ -592,7 +592,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag if (_cached) { if (_cache) { _cache->prepare(); - set_cairo_blend_operator( dc, _blend_mode ); + set_cairo_blend_operator( dc, _mix_blend_mode ); _cache->paintFromCache(dc, carea); if (!carea) return RENDER_OK; @@ -622,7 +622,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag nir |= (_filter != NULL && render_filters); // 3. it has a filter nir |= needs_opacity; // 4. it is non-opaque nir |= (_cache != NULL); // 5. it is cached - nir |= (_blend_mode != SP_CSS_BLEND_NORMAL); // 6. Blend mode not normal + nir |= (_mix_blend_mode != SP_CSS_BLEND_NORMAL); // 6. Blend mode not normal nir |= (_isolation == SP_CSS_ISOLATION_ISOLATE); // 7. Explicit isolatiom /* How the rendering is done. @@ -735,7 +735,7 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag } dc.rectangle(*carea); dc.setSource(&intermediate); - set_cairo_blend_operator( dc, _blend_mode ); + set_cairo_blend_operator( dc, _mix_blend_mode ); dc.fill(); dc.setSource(0,0,0,0); // the call above is to clear a ref on the intermediate surface held by dc diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index d89299eeb..925bcbddb 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -209,7 +209,7 @@ protected: unsigned _antialias : 1; ///< Whether to use antialiasing unsigned _isolation : 1; - unsigned _blend_mode : 4; + unsigned _mix_blend_mode : 4; friend class Drawing; }; diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 05a2c3c2a..9f3b447df 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -150,25 +150,26 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext return STATE_ALL; } -DrawingItem * -DrawingGlyphs::_pickItem(Geom::Point const &p, double delta, unsigned /*flags*/) +DrawingItem *DrawingGlyphs::_pickItem(Geom::Point const &p, double /*delta*/, unsigned /*flags*/) { DrawingText *ggroup = dynamic_cast<DrawingText *>(_parent); if (!ggroup) { throw InvalidItemException(); } + DrawingItem *result = NULL; bool invisible = (ggroup->_nrstyle.fill.type == NRStyle::PAINT_NONE) && (ggroup->_nrstyle.stroke.type == NRStyle::PAINT_NONE); - if (!_font || !_bbox || (!_drawing.outline() && invisible) ) { - return NULL; - } - // With text we take a simple approach: pick if the point is in a character bbox - Geom::Rect expanded(_pick_bbox); - // FIXME, why expand by delta? When is the next line needed? - // expanded.expandBy(delta); - if (expanded.contains(p)) return this; - return NULL; + if (_font && _bbox && (_drawing.outline() || !invisible) ) { + // With text we take a simple approach: pick if the point is in a character bbox + Geom::Rect expanded(_pick_bbox); + // FIXME, why expand by delta? When is the next line needed? + // expanded.expandBy(delta); + if (expanded.contains(p)) { + result = this; + } + } + return result; } diff --git a/src/document-undo.cpp b/src/document-undo.cpp index 0bce4420a..679223da6 100644 --- a/src/document-undo.cpp +++ b/src/document-undo.cpp @@ -254,6 +254,7 @@ gboolean Inkscape::DocumentUndo::undo(SPDocument *doc) Inkscape::Event *log=(Inkscape::Event *)doc->priv->undo->data; doc->priv->undo = g_slist_remove (doc->priv->undo, log); sp_repr_undo_log (log->event); + doc->_updateDocument(); doc->priv->redo = g_slist_prepend (doc->priv->redo, log); doc->setModifiedSinceSave(); diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 863d1e006..084fbcd58 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -1641,11 +1641,19 @@ int Emf::myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DA // next record is valid type and forces pending text to be drawn immediately if ((d->dc[d->level].dirty & DIRTY_TEXT) || ((emr_mask != U_EMR_INVALID) && (emr_mask & U_DRAW_TEXT) && d->tri->dirty)){ TR_layout_analyze(d->tri); + if (d->dc[d->level].clip_id){ + SVGOStringStream tmp_clip; + tmp_clip << "\n<g\n\tclip-path=\"url(#clipEmfPath" << d->dc[d->level].clip_id << ")\"\n>"; + d->outsvg += tmp_clip.str().c_str(); + } TR_layout_2_svg(d->tri); SVGOStringStream ts; ts << d->tri->out; d->outsvg += ts.str().c_str(); d->tri = trinfo_clear(d->tri); + if (d->dc[d->level].clip_id){ + d->outsvg += "\n</g>\n"; + } } if(d->dc[d->level].dirty){ //Apply the delayed background changes, clear the flag d->dc[d->level].bkMode = tbkMode; @@ -2524,8 +2532,8 @@ std::cout << "BEFORE DRAW" double cx = pix_to_x_point( d, (rclBox.left + rclBox.right)/2.0, (rclBox.bottom + rclBox.top)/2.0 ); double cy = pix_to_y_point( d, (rclBox.left + rclBox.right)/2.0, (rclBox.bottom + rclBox.top)/2.0 ); - double rx = pix_to_abs_size( d, fabs(rclBox.right - rclBox.left )/2.0 ); - double ry = pix_to_abs_size( d, fabs(rclBox.top - rclBox.bottom)/2.0 ); + double rx = pix_to_abs_size( d, std::abs(rclBox.right - rclBox.left )/2.0 ); + double ry = pix_to_abs_size( d, std::abs(rclBox.top - rclBox.bottom)/2.0 ); SVGOStringStream tmp_ellipse; tmp_ellipse << "cx=\"" << cx << "\" "; @@ -3141,11 +3149,19 @@ std::cout << "BEFORE DRAW" int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement if(status==-1){ // change of escapement, emit what we have and reset TR_layout_analyze(d->tri); + if (d->dc[d->level].clip_id){ + SVGOStringStream tmp_clip; + tmp_clip << "\n<g\n\tclip-path=\"url(#clipEmfPath" << d->dc[d->level].clip_id << ")\"\n>"; + d->outsvg += tmp_clip.str().c_str(); + } TR_layout_2_svg(d->tri); ts << d->tri->out; d->outsvg += ts.str().c_str(); d->tri = trinfo_clear(d->tri); (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work + if (d->dc[d->level].clip_id){ + d->outsvg += "\n</g>\n"; + } } g_free(escaped_text); @@ -3479,7 +3495,7 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) if (d.pDesc){ free( d.pDesc ); } -// std::cout << "SVG Output: " << std::endl << *(d.outsvg) << std::endl; +// std::cout << "SVG Output: " << std::endl << d.outsvg << std::endl; SPDocument *doc = SPDocument::createNewDocFromMem(d.outsvg.c_str(), strlen(d.outsvg.c_str()), TRUE); diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index 9c68e40a4..e054829b5 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -977,8 +977,8 @@ unsigned int PrintEmf::fill( using Geom::X; using Geom::Y; - SPItem *item = SP_ITEM(style->object); - SPClipPath *scp = (item->clip_ref ? item->clip_ref->getObject() : NULL); + //SPItem *item = SP_ITEM(style->object); + //SPClipPath *scp = (item->clip_ref ? item->clip_ref->getObject() : NULL); Geom::Affine tf = m_tr_stack.top(); @@ -1882,7 +1882,7 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te fix90n = 1; //assume vertical rot = (double)(((int) round(rot)) - irem); rotb = rot * M_PI / 1800.0; - if (abs(rot) == 900.0) { + if (std::abs(rot) == 900.0) { fix90n = 2; } } diff --git a/src/extension/internal/emf-print.h b/src/extension/internal/emf-print.h index 1e4970a46..9a1251b38 100644 --- a/src/extension/internal/emf-print.h +++ b/src/extension/internal/emf-print.h @@ -29,7 +29,7 @@ namespace Internal { class PrintEmf : public PrintMetafile { - uint32_t hbrush, hbrushOld, hpen, hpenOld; + uint32_t hbrush, hbrushOld, hpen; unsigned int print_pathv (Geom::PathVector const &pathv, const Geom::Affine &transform); bool print_simple_shape (Geom::PathVector const &pathv, const Geom::Affine &transform); diff --git a/src/extension/internal/text_reassemble.c b/src/extension/internal/text_reassemble.c index 810e3f8cc..4dfc49420 100644 --- a/src/extension/internal/text_reassemble.c +++ b/src/extension/internal/text_reassemble.c @@ -67,8 +67,8 @@ Optional compiler switches for development: File: text_reassemble.c -Version: 0.0.14 -Date: 25-MAR-2014 +Version: 0.0.15 +Date: 24-JUL-2014 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2014 David Mathog and California Institute of Technology (Caltech) @@ -550,7 +550,7 @@ int TR_check_set_vadvance(TR_INFO *tri, int src, int lines){ See if the line to be added is compatible. All text fields in a complex have the same advance, so just set/check the first one. vadvance must be within 1% or do not add a new line */ - if(fabs(1.0 - (tpi->chunks[trec].vadvance/newV) > 0.01)){ + if(fabs(1.0 - (tpi->chunks[trec].vadvance/newV)) > 0.01){ status = 1; } else { /* recalculate the weighted vadvance */ @@ -1820,7 +1820,9 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,% fasc = ((double) (fsp->face->ascender) )/64.0; fdsc = ((double) (fsp->face->descender))/64.0; - if(tri->load_flags & FT_LOAD_NO_SCALE) xe *= tsp->fs/32.0; + /* originally the denominator was just 32.0, but it broke when units_per_EM wasn't 2048 */ + double fixscale = tsp->fs/(((double) fsp->face->units_per_EM)/64.0); + if(tri->load_flags & FT_LOAD_NO_SCALE) xe *= fixscale; /* now place the rectangle using ALN information */ if( taln & ALIHORI & ALILEFT ){ @@ -1837,11 +1839,11 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,% } tpi->chunks[current].ldir = tsp->ldir; - if(tri->load_flags & FT_LOAD_NO_SCALE){ - asc *= tsp->fs/32.0; - dsc *= tsp->fs/32.0; - fasc *= tsp->fs/32.0; - fdsc *= tsp->fs/32.0; + if(tri->load_flags & FT_LOAD_NO_SCALE){ + asc *= fixscale; + dsc *= fixscale; + fasc *= fixscale; + fdsc *= fixscale; } diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index 85060470b..5ccad678a 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -63,7 +63,6 @@ namespace Extension { namespace Internal { -static U_RECT16 rc_old; static bool clipset = false; static uint32_t BLTmode=0; @@ -671,7 +670,7 @@ void Wmf::add_clips(PWMF_CALLBACK_DATA d, const char *clippath, unsigned int log SVGOStringStream tmp_clippath; tmp_clippath << "\n<clipPath"; tmp_clippath << "\n\tclipPathUnits=\"userSpaceOnUse\" "; - tmp_clippath << "\n\tid=\"clipEmfPath" << d->dc[d->level].clip_id << "\""; + tmp_clippath << "\n\tid=\"clipWmfPath" << d->dc[d->level].clip_id << "\""; tmp_clippath << " >"; tmp_clippath << "\n\t<path d=\""; tmp_clippath << combined; @@ -896,7 +895,7 @@ Wmf::output_style(PWMF_CALLBACK_DATA d) } tmp_style << "\" "; if (d->dc[d->level].clip_id) - tmp_style << "\n\tclip-path=\"url(#clipEmfPath" << d->dc[d->level].clip_id << ")\" "; + tmp_style << "\n\tclip-path=\"url(#clipWmfPath" << d->dc[d->level].clip_id << ")\" "; d->outsvg += tmp_style.str().c_str(); } @@ -1689,11 +1688,19 @@ int Wmf::myMetaFileProc(const char *contents, unsigned int length, PWMF_CALLBACK // next record is valid type and forces pending text to be drawn immediately if ((d->dc[d->level].dirty & DIRTY_TEXT) || ((wmr_mask != U_WMR_INVALID) && (wmr_mask & U_DRAW_TEXT) && d->tri->dirty)){ TR_layout_analyze(d->tri); + if (d->dc[d->level].clip_id){ + SVGOStringStream tmp_clip; + tmp_clip << "\n<g\n\tclip-path=\"url(#clipWmfPath" << d->dc[d->level].clip_id << ")\"\n>"; + d->outsvg += tmp_clip.str().c_str(); + } TR_layout_2_svg(d->tri); SVGOStringStream ts; ts << d->tri->out; d->outsvg += ts.str().c_str(); d->tri = trinfo_clear(d->tri); + if (d->dc[d->level].clip_id){ + d->outsvg += "\n</g>\n"; + } } if(d->dc[d->level].dirty){ //Apply the delayed background changes, clear the flag d->dc[d->level].bkMode = tbkMode; @@ -2054,8 +2061,8 @@ std::cout << "BEFORE DRAW" double cx = pix_to_x_point( d, (rc.left + rc.right)/2.0, (rc.bottom + rc.top)/2.0 ); double cy = pix_to_y_point( d, (rc.left + rc.right)/2.0, (rc.bottom + rc.top)/2.0 ); - double rx = pix_to_abs_size( d, fabs(rc.right - rc.left )/2.0 ); - double ry = pix_to_abs_size( d, fabs(rc.top - rc.bottom)/2.0 ); + double rx = pix_to_abs_size( d, std::abs(rc.right - rc.left )/2.0 ); + double ry = pix_to_abs_size( d, std::abs(rc.top - rc.bottom)/2.0 ); SVGOStringStream tmp_ellipse; tmp_ellipse << "cx=\"" << cx << "\" "; @@ -2591,11 +2598,19 @@ std::cout << "BEFORE DRAW" status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement if(status==-1){ // change of escapement, emit what we have and reset TR_layout_analyze(d->tri); + if (d->dc[d->level].clip_id){ + SVGOStringStream tmp_clip; + tmp_clip << "\n<g\n\tclip-path=\"url(#clipWmfPath" << d->dc[d->level].clip_id << ")\"\n>"; + d->outsvg += tmp_clip.str().c_str(); + } TR_layout_2_svg(d->tri); ts << d->tri->out; d->outsvg += ts.str().c_str(); d->tri = trinfo_clear(d->tri); (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work + if (d->dc[d->level].clip_id){ + d->outsvg += "\n</g>\n"; + } } g_free(escaped_text); @@ -3072,7 +3087,7 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) (void) myMetaFileProc(contents,length, &d); free(contents); -// std::cout << "SVG Output: " << std::endl << *(d.outsvg) << std::endl; +// std::cout << "SVG Output: " << std::endl << d.outsvg << std::endl; SPDocument *doc = SPDocument::createNewDocFromMem(d.outsvg.c_str(), strlen(d.outsvg.c_str()), TRUE); diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp index 55ad5da5f..e5ff34009 100644 --- a/src/extension/internal/wmf-print.cpp +++ b/src/extension/internal/wmf-print.cpp @@ -1396,7 +1396,7 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te fix90n = 1; //assume vertical rot = (double)(((int) round(rot)) - irem); rotb = rot * M_PI / 1800.0; - if (abs(rot) == 900.0) { + if (std::abs(rot) == 900.0) { fix90n = 2; } } diff --git a/src/extension/internal/wmf-print.h b/src/extension/internal/wmf-print.h index 1e5d4c323..e4cf19184 100644 --- a/src/extension/internal/wmf-print.h +++ b/src/extension/internal/wmf-print.h @@ -28,7 +28,7 @@ namespace Internal { class PrintWmf : public PrintMetafile { - uint32_t hbrush, hpen, hpenOld, hbrush_null, hpen_null; + uint32_t hbrush, hpen, hbrush_null, hpen_null; uint32_t hmiterlimit; // used to minimize redundant records that set this unsigned int print_pathv (Geom::PathVector const &pathv, const Geom::Affine &transform); diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index 89b7968fc..27f4d7a98 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -1296,6 +1296,10 @@ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_ break; case POINT_LG_MID: { + if (lg->vector.stops.size() < point_i) { + g_message("POINT_LG_MID bug trigger, see LP bug #453067"); + break; + } gdouble offset = lg->vector.stops.at(point_i).offset; p = (1-offset) * Geom::Point(lg->x1.computed, lg->y1.computed) + offset * Geom::Point(lg->x2.computed, lg->y2.computed); } @@ -1321,12 +1325,20 @@ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_ break; case POINT_RG_MID1: { + if (rg->vector.stops.size() < point_i) { + g_message("POINT_RG_MID1 bug trigger, see LP bug #453067"); + break; + } gdouble offset = rg->vector.stops.at(point_i).offset; p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed + rg->r.computed, rg->cy.computed); } break; case POINT_RG_MID2: { + if (rg->vector.stops.size() < point_i) { + g_message("POINT_RG_MID2 bug trigger, see LP bug #453067"); + break; + } gdouble offset = rg->vector.stops.at(point_i).offset; p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed, rg->cy.computed - rg->r.computed); } diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index ee4c1bc8c..a14220cfa 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -21,7 +21,6 @@ #include <glibmm/i18n.h> #include <cstring> #include <string> -//#include <2geom/bezier-curve.h> #include "desktop-handles.h" #include "selection.h" @@ -38,6 +37,7 @@ #include "svg/css-ostringstream.h" #include "svg/svg.h" #include "preferences.h" +#include "inkscape.h" #include "sp-item.h" #include "style.h" #include "knot.h" @@ -55,6 +55,7 @@ #include "verbs.h" #include "display/sp-canvas.h" #include "ui/control-manager.h" +#include "ui/tools/tool-base.h" using Inkscape::ControlManager; using Inkscape::CtrlLineType; @@ -783,6 +784,9 @@ static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, gui dragger->parent->draggers = g_list_remove (dragger->parent->draggers, dragger); delete dragger; + // throw out delayed snap context + Inkscape::UI::Tools::sp_event_context_discard_delayed_snap_event(SP_ACTIVE_DESKTOP->event_context); + // update the new merged dragger d_new->fireDraggables(true, false, true); d_new->parent->updateLines(); @@ -790,8 +794,7 @@ static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, gui d_new->updateKnotShape (); d_new->updateTip (); d_new->updateDependencies(true); - DocumentUndo::done(sp_desktop_document (d_new->parent->desktop), SP_VERB_CONTEXT_GRADIENT, - _("Merge gradient handles")); + DocumentUndo::done(sp_desktop_document (d_new->parent->desktop), SP_VERB_CONTEXT_GRADIENT, _("Merge gradient handles")); return; } } diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp index c9148a634..6eba6e949 100644 --- a/src/helper/geom.cpp +++ b/src/helper/geom.cpp @@ -168,24 +168,25 @@ bounds_exact_transformed(Geom::PathVector const & pv, Geom::Affine const & t) for (Geom::Path::const_iterator cit = it->begin(); cit != it->end_open(); ++cit) { Geom::Curve const &c = *cit; - if( is_straight_curve(c) ) - { - bbox.expandTo( c.finalPoint() * t ); - } - else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(&c)) - { - Geom::Point c0 = (*cubic_bezier)[0] * t; - Geom::Point c1 = (*cubic_bezier)[1] * t; - Geom::Point c2 = (*cubic_bezier)[2] * t; - Geom::Point c3 = (*cubic_bezier)[3] * t; - cubic_bbox( c0[0], c0[1], - c1[0], c1[1], - c2[0], c2[1], - c3[0], c3[1], - bbox ); + unsigned order = 0; + if (Geom::BezierCurve const* b = dynamic_cast<Geom::BezierCurve const*>(&c)) { + order = b->order(); } - else - { + + if (order == 1) { // line segment + bbox.expandTo(c.finalPoint() * t); + + // TODO: we can make the case for quadratics faster by degree elevating them to + // cubic and then taking the bbox of that. + + } else if (order == 3) { // cubic bezier + Geom::CubicBezier const &cubic_bezier = static_cast<Geom::CubicBezier const&>(c); + Geom::Point c0 = cubic_bezier[0] * t; + Geom::Point c1 = cubic_bezier[1] * t; + Geom::Point c2 = cubic_bezier[2] * t; + Geom::Point c3 = cubic_bezier[3] * t; + cubic_bbox(c0[0], c0[1], c1[0], c1[1], c2[0], c2[1], c3[0], c3[1], bbox); + } else { // should handle all not-so-easy curves: Geom::Curve *ctemp = cit->transformed(t); bbox.unionWith( ctemp->boundsExact()); @@ -356,8 +357,11 @@ geom_curve_bbox_wind_distance(Geom::Curve const & c, Geom::Affine const &m, Geom::Coord tolerance, Geom::Rect const *viewbox, Geom::Point &p0) // pass p0 through as it represents the last endpoint added (the finalPoint of last curve) { - if( is_straight_curve(c) ) - { + unsigned order = 0; + if (Geom::BezierCurve const* b = dynamic_cast<Geom::BezierCurve const*>(&c)) { + order = b->order(); + } + if (order == 1) { Geom::Point pe = c.finalPoint() * m; if (bbox) { bbox->expandTo(pe); @@ -373,10 +377,11 @@ geom_curve_bbox_wind_distance(Geom::Curve const & c, Geom::Affine const &m, } p0 = pe; } - else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(&c)) { - Geom::Point p1 = (*cubic_bezier)[1] * m; - Geom::Point p2 = (*cubic_bezier)[2] * m; - Geom::Point p3 = (*cubic_bezier)[3] * m; + else if (order == 3) { + Geom::CubicBezier const& cubic_bezier = static_cast<Geom::CubicBezier const&>(c); + Geom::Point p1 = cubic_bezier[1] * m; + Geom::Point p2 = cubic_bezier[2] * m; + Geom::Point p3 = cubic_bezier[3] * m; // get approximate bbox from handles (convex hull property of beziers): Geom::Rect swept(p0, p3); @@ -402,7 +407,7 @@ geom_curve_bbox_wind_distance(Geom::Curve const & c, Geom::Affine const &m, Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); //recurse to convert the new path resulting from the sbasis to svgd - for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { + for (Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { geom_curve_bbox_wind_distance(*iter, m, pt, bbox, wind, dist, tolerance, viewbox, p0); } } diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index 6af5c6a56..b66156b09 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -158,7 +158,7 @@ static Geom::Point sp_pattern_extract_trans(SPPattern const *pat) void PatternKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) { - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); + SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); // FIXME: this snapping should be done together with knowing whether control was pressed. If GDK_CONTROL_MASK, then constrained snapping should be used. Geom::Point p_snapped = snap_knot_position(p, state); @@ -173,7 +173,7 @@ PatternKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &ori if (state) { Geom::Point const q = p_snapped - sp_pattern_extract_trans(pat); - item->adjust_pattern(Geom::Affine(Geom::Translate(q))); + item->adjust_pattern(Geom::Translate(q), false, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE); } item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); @@ -182,14 +182,14 @@ PatternKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &ori Geom::Point PatternKnotHolderEntityXY::knot_get() const { - SPPattern const *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); + SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); return sp_pattern_extract_trans(pat); } Geom::Point PatternKnotHolderEntityAngle::knot_get() const { - SPPattern const *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); + SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); gdouble x = pattern_width(pat); gdouble y = 0; @@ -207,7 +207,7 @@ PatternKnotHolderEntityAngle::knot_set(Geom::Point const &p, Geom::Point const & Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); + SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); // get the angle from pattern 0,0 to the cursor pos Geom::Point delta = p - sp_pattern_extract_trans(pat); @@ -223,14 +223,14 @@ PatternKnotHolderEntityAngle::knot_set(Geom::Point const &p, Geom::Point const & Geom::Point const t = sp_pattern_extract_trans(pat); rot[4] = t[Geom::X]; rot[5] = t[Geom::Y]; - item->adjust_pattern(rot, true); + item->adjust_pattern(rot, true, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE); item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } void PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) { - SPPattern *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); + SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); // FIXME: this snapping should be done together with knowing whether control was pressed. If GDK_CONTROL_MASK, then constrained snapping should be used. Geom::Point p_snapped = snap_knot_position(p, state); @@ -257,7 +257,7 @@ PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const & Geom::Point const t = sp_pattern_extract_trans(pat); rot[4] = t[Geom::X]; rot[5] = t[Geom::Y]; - item->adjust_pattern(rot, true); + item->adjust_pattern(rot, true, _fill ? TRANSFORM_FILL : TRANSFORM_STROKE); item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -265,7 +265,7 @@ PatternKnotHolderEntityScale::knot_set(Geom::Point const &p, Geom::Point const & Geom::Point PatternKnotHolderEntityScale::knot_get() const { - SPPattern const *pat = SP_PATTERN(SP_STYLE_FILL_SERVER(SP_OBJECT(item)->style)); + SPPattern *pat = _fill ? SP_PATTERN(item->style->getFillPaintServer()) : SP_PATTERN(item->style->getStrokePaintServer()); gdouble x = pattern_width(pat); gdouble y = pattern_height(pat); diff --git a/src/knot-holder-entity.h b/src/knot-holder-entity.h index a9268d396..dde60f515 100644 --- a/src/knot-holder-entity.h +++ b/src/knot-holder-entity.h @@ -101,20 +101,30 @@ protected: class PatternKnotHolderEntityXY : public KnotHolderEntity { public: + PatternKnotHolderEntityXY(bool fill) : KnotHolderEntity(), _fill(fill) {} virtual Geom::Point knot_get() const; virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +private: + // true if the entity tracks fill, false for stroke + bool _fill; }; class PatternKnotHolderEntityAngle : public KnotHolderEntity { public: + PatternKnotHolderEntityAngle(bool fill) : KnotHolderEntity(), _fill(fill) {} virtual Geom::Point knot_get() const; virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +private: + bool _fill; }; class PatternKnotHolderEntityScale : public KnotHolderEntity { public: + PatternKnotHolderEntityScale(bool fill) : KnotHolderEntity(), _fill(fill) {} virtual Geom::Point knot_get() const; virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); +private: + bool _fill; }; #endif /* !SEEN_KNOT_HOLDER_ENTITY_H */ diff --git a/src/knot-ptr.cpp b/src/knot-ptr.cpp new file mode 100644 index 000000000..de8118ba7 --- /dev/null +++ b/src/knot-ptr.cpp @@ -0,0 +1,25 @@ +#include <algorithm> +#include <glib.h> +#include <list> +#include "knot-ptr.h" + +static std::list<void*> deleted_knots; + +void knot_deleted_callback(void* knot) { + if (std::find(deleted_knots.begin(), deleted_knots.end(), knot) == deleted_knots.end()) { + deleted_knots.push_back(knot); + } +} + +void knot_created_callback(void* knot) { + std::list<void*>::iterator it = std::find(deleted_knots.begin(), deleted_knots.end(), knot); + if (it != deleted_knots.end()) { + deleted_knots.erase(it); + } +} + +void check_if_knot_deleted(void* knot) { + if (std::find(deleted_knots.begin(), deleted_knots.end(), knot) != deleted_knots.end()) { + g_warning("Accessed knot after it was freed at %p", knot); + } +} diff --git a/src/knot-ptr.h b/src/knot-ptr.h new file mode 100644 index 000000000..5895dfd2e --- /dev/null +++ b/src/knot-ptr.h @@ -0,0 +1,8 @@ +#ifndef KNOT_PTR_DETECTOR +#define KNOT_PTR_DETECTOR + +void knot_deleted_callback(void* knot); +void knot_created_callback(void* knot); +void check_if_knot_deleted(void* knot); + +#endif diff --git a/src/knot.cpp b/src/knot.cpp index 61d0dff39..6205af26a 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -21,6 +21,7 @@ #include "desktop.h" #include "desktop-handles.h" #include "knot.h" +#include "knot-ptr.h" #include "document.h" #include "document-undo.h" #include "preferences.h" @@ -118,6 +119,7 @@ SPKnot::SPKnot(SPDesktop *desktop, gchar const *tip) this->_event_handler_id = g_signal_connect(G_OBJECT(this->item), "event", G_CALLBACK(sp_knot_handler), this); + knot_created_callback(this); } SPKnot::~SPKnot() { @@ -162,6 +164,10 @@ SPKnot::~SPKnot() { g_free(this->tip); this->tip = NULL; } + + // FIXME: cannot snap to destroyed knot (lp:1309050) + //sp_event_context_discard_delayed_snap_event(this->desktop->event_context); + knot_deleted_callback(this); } void SPKnot::startDragging(Geom::Point const &p, gint x, gint y, guint32 etime) { @@ -282,7 +288,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot knot->setFlag(SP_KNOT_DRAGGING, TRUE); } - sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, (gpointer) knot, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::KNOT_HANDLER); + sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, knot, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::KNOT_HANDLER); sp_knot_handler_request_position(event, knot); moved = TRUE; } diff --git a/src/knotholder.cpp b/src/knotholder.cpp index cf87423d4..f0e69716b 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -247,12 +247,32 @@ void KnotHolder::add(KnotHolderEntity *e) void KnotHolder::add_pattern_knotholder() { - if ((item->style->fill.isPaintserver()) - && SP_IS_PATTERN(item->style->getFillPaintServer())) - { - PatternKnotHolderEntityXY *entity_xy = new PatternKnotHolderEntityXY(); - PatternKnotHolderEntityAngle *entity_angle = new PatternKnotHolderEntityAngle(); - PatternKnotHolderEntityScale *entity_scale = new PatternKnotHolderEntityScale(); + if ((item->style->fill.isPaintserver()) && SP_IS_PATTERN(item->style->getFillPaintServer())) { + PatternKnotHolderEntityXY *entity_xy = new PatternKnotHolderEntityXY(true); + PatternKnotHolderEntityAngle *entity_angle = new PatternKnotHolderEntityAngle(true); + PatternKnotHolderEntityScale *entity_scale = new PatternKnotHolderEntityScale(true); + entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, + // TRANSLATORS: This refers to the pattern that's inside the object + _("<b>Move</b> the pattern fill inside the object"), + SP_KNOT_SHAPE_CROSS); + + entity_scale->create(desktop, item, this, Inkscape::CTRL_TYPE_SIZER, + _("<b>Scale</b> the pattern fill; uniformly if with <b>Ctrl</b>"), + SP_KNOT_SHAPE_SQUARE, SP_KNOT_MODE_XOR); + + entity_angle->create(desktop, item, this, Inkscape::CTRL_TYPE_ROTATE, + _("<b>Rotate</b> the pattern fill; with <b>Ctrl</b> to snap angle"), + SP_KNOT_SHAPE_CIRCLE, SP_KNOT_MODE_XOR); + + entity.push_back(entity_xy); + entity.push_back(entity_angle); + entity.push_back(entity_scale); + } + + if ((item->style->stroke.isPaintserver()) && SP_IS_PATTERN(item->style->getStrokePaintServer())) { + PatternKnotHolderEntityXY *entity_xy = new PatternKnotHolderEntityXY(false); + PatternKnotHolderEntityAngle *entity_angle = new PatternKnotHolderEntityAngle(false); + PatternKnotHolderEntityScale *entity_scale = new PatternKnotHolderEntityScale(false); entity_xy->create(desktop, item, this, Inkscape::CTRL_TYPE_POINT, // TRANSLATORS: This refers to the pattern that's inside the object _("<b>Move</b> the pattern fill inside the object"), diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index a4d35b339..3c64e5c8e 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -35,6 +35,7 @@ set(live_effects_SRC # lpe-skeleton.cpp lpe-sketch.cpp lpe-spiro.cpp + lpe-roughen.cpp lpe-tangent_to_curve.cpp lpe-test-doEffect-stack.cpp lpe-bspline.cpp @@ -101,6 +102,7 @@ set(live_effects_SRC lpe-skeleton.h lpe-sketch.h lpe-spiro.h + lpe-roughen.h lpe-tangent_to_curve.h lpe-test-doEffect-stack.h lpe-bspline.h diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 263b33b3c..91651a7c8 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -46,6 +46,8 @@ ink_common_sources += \ live_effects/lpe-lattice.h \ live_effects/lpe-lattice2.cpp \ live_effects/lpe-lattice2.h \ + live_effects/lpe-roughen.cpp \ + live_effects/lpe-roughen.h \ live_effects/lpe-simplify.cpp \ live_effects/lpe-simplify.h \ live_effects/lpe-envelope.cpp \ diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index 942ef5891..dac44981c 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -29,6 +29,7 @@ enum EffectType { SPIRO, LATTICE, LATTICE2, + ROUGHEN, SIMPLIFY, ENVELOPE, CONSTRUCT_GRID, diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 0e9f92d09..5a9b6c7a2 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -27,6 +27,7 @@ #include "live_effects/lpe-spiro.h" #include "live_effects/lpe-lattice.h" #include "live_effects/lpe-lattice2.h" +#include "live_effects/lpe-roughen.h" #include "live_effects/lpe-simplify.h" #include "live_effects/lpe-envelope.h" #include "live_effects/lpe-constructgrid.h" @@ -125,6 +126,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { /* 0.91 */ {POWERSTROKE, N_("Power stroke"), "powerstroke"}, {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"}, + {ROUGHEN, N_("Roughen"), "roughen"}, {BSPLINE, N_("BSpline"), "bspline"}, {SIMPLIFY, N_("Simplify"), "simplify"}, {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, @@ -269,6 +271,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case FILLET_CHAMFER: neweffect = static_cast<Effect*> ( new LPEFilletChamfer(lpeobj) ); break; + case ROUGHEN: + neweffect = static_cast<Effect*> ( new LPERoughen(lpeobj) ); + break; default: g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); neweffect = NULL; diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp index 98557196d..bfb2c3ba4 100644 --- a/src/live_effects/lpe-fillet-chamfer.cpp +++ b/src/live_effects/lpe-fillet-chamfer.cpp @@ -517,6 +517,8 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) counter - counterCurves, filletChamferData[counter - counterCurves][X]), &intpart); + } else if (!path_it->closed() && curve_it2 == curve_endit){ + time_it2 = 0; } else { time_it2 = modf(fillet_chamfer_values.to_time( counter + 1, filletChamferData[counter + 1][X]), @@ -529,6 +531,8 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) it1_length + fillet_chamfer_values.to_len( counter - counterCurves, filletChamferData[counter - counterCurves][X]); + } else if (!path_it->closed() && curve_it2 == curve_endit){ + resultLenght = 0; } else { resultLenght = it1_length + fillet_chamfer_values.to_len( @@ -607,6 +611,8 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in) int type = 0; if(path_it->closed() && curve_it2 == curve_endit){ type = abs(filletChamferData[counter - counterCurves][Y]); + } else if (!path_it->closed() && curve_it2 == curve_endit){ + //0 } else { type = abs(filletChamferData[counter + 1][Y]); } diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp new file mode 100644 index 000000000..9af849530 --- /dev/null +++ b/src/live_effects/lpe-roughen.cpp @@ -0,0 +1,389 @@ +/* + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <gtkmm.h> + +#include "live_effects/lpe-roughen.h" +#include "display/curve.h" +#include "live_effects/parameter/parameter.h" +#include "helper/geom.h" +#include <glibmm/i18n.h> +#include <cmath> + +namespace Inkscape { +namespace LivePathEffect { + +static const Util::EnumData<DivisionMethod> DivisionMethodData[DM_END] = { + { DM_SEGMENTS, N_("By number of segments"), "segments" }, + { DM_SIZE, N_("By max. segment size"), "size" } +}; +static const Util::EnumDataConverter<DivisionMethod> + DMConverter(DivisionMethodData, DM_END); + +LPERoughen::LPERoughen(LivePathEffectObject *lpeobject) + : Effect(lpeobject), + // initialise your parameters here: + unit(_("Unit:"), _("Unit"), "unit", &wr, this), + method(_("Method:"), _("Division method"), "method", DMConverter, &wr, + this, DM_SEGMENTS), + maxSegmentSize(_("Max. segment size:"), _("Max. segment size"), + "maxSegmentSize", &wr, this, 10.), + segments(_("Number of segments:"), _("Number of segments"), "segments", + &wr, this, 2), + displaceX(_("Max. displacement in X:"), _("Max. displacement in X"), + "displaceX", &wr, this, 10.), + displaceY(_("Max. displacement in Y:"), _("Max. displacement in Y"), + "displaceY", &wr, this, 10.), + shiftNodes(_("Shift nodes"), _("Shift nodes"), "shiftNodes", &wr, this, + true), + shiftNodeHandles(_("Shift node handles"), _("Shift node handles"), + "shiftNodeHandles", &wr, this, true) { + registerParameter(dynamic_cast<Parameter *>(&unit)); + registerParameter(dynamic_cast<Parameter *>(&method)); + registerParameter(dynamic_cast<Parameter *>(&maxSegmentSize)); + registerParameter(dynamic_cast<Parameter *>(&segments)); + registerParameter(dynamic_cast<Parameter *>(&displaceX)); + registerParameter(dynamic_cast<Parameter *>(&displaceY)); + registerParameter(dynamic_cast<Parameter *>(&shiftNodes)); + registerParameter(dynamic_cast<Parameter *>(&shiftNodeHandles)); + displaceX.param_set_range(0., Geom::infinity()); + displaceY.param_set_range(0., Geom::infinity()); + maxSegmentSize.param_set_range(0., Geom::infinity()); + maxSegmentSize.param_set_increments(1, 1); + maxSegmentSize.param_set_digits(1); + segments.param_set_range(1, Geom::infinity()); + segments.param_set_increments(1, 1); + segments.param_set_digits(0); +} + +LPERoughen::~LPERoughen() {} + +void LPERoughen::doBeforeEffect(SPLPEItem const */*lpeitem*/) { + displaceX.resetRandomizer(); + displaceY.resetRandomizer(); + srand(1); +} + +Gtk::Widget *LPERoughen::newWidget() { + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter *param = *it; + Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); + if (param->param_key == "unit") { + Gtk::Label *unitLabel = Gtk::manage(new Gtk::Label( + Glib::ustring(_("<b>Roughen unit</b>")), Gtk::ALIGN_START)); + unitLabel->set_use_markup(true); + vbox->pack_start(*unitLabel, false, false, 2); + vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + Gtk::PACK_EXPAND_WIDGET); + } + if (param->param_key == "method") { + Gtk::Label *methodLabel = Gtk::manage(new Gtk::Label( + Glib::ustring(_("<b>Add nodes</b> Subdivide each segment")), + Gtk::ALIGN_START)); + methodLabel->set_use_markup(true); + vbox->pack_start(*methodLabel, false, false, 2); + vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + Gtk::PACK_EXPAND_WIDGET); + } + if (param->param_key == "displaceX") { + Gtk::Label *displaceXLabel = Gtk::manage(new Gtk::Label( + Glib::ustring(_("<b>Jitter nodes</b> Move nodes/handles")), + Gtk::ALIGN_START)); + displaceXLabel->set_use_markup(true); + vbox->pack_start(*displaceXLabel, false, false, 2); + vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + Gtk::PACK_EXPAND_WIDGET); + } + Glib::ustring *tip = param->param_getTooltip(); + if (widg) { + vbox->pack_start(*widg, true, true, 2); + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + + ++it; + } + return dynamic_cast<Gtk::Widget *>(vbox); +} + +double LPERoughen::sign(double randNumber) { + if (rand() % 100 < 49) { + randNumber *= -1.; + } + return randNumber; +} + +Geom::Point LPERoughen::randomize() { + double displaceXParsed = Inkscape::Util::Quantity::convert( + displaceX, unit.get_abbreviation(), "px"); + double displaceYParsed = Inkscape::Util::Quantity::convert( + displaceY, unit.get_abbreviation(), "px"); + //maybe is better divide this point by 2... + Geom::Point output = + Geom::Point(sign(displaceXParsed), sign(displaceYParsed)); + return output; +} + +void LPERoughen::doEffect(SPCurve *curve) { + Geom::PathVector const original_pathv = + pathv_to_linear_and_cubic_beziers(curve->get_pathvector()); + curve->reset(); + + //Recorremos todos los paths a los que queremos aplicar el efecto, hasta el + //penúltimo + for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); + path_it != original_pathv.end(); ++path_it) { + //Si está vacÃo... + if (path_it->empty()) + continue; + //Itreadores + + Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve + Geom::Path::const_iterator curve_it2 = + ++(path_it->begin()); // outgoing curve + Geom::Path::const_iterator curve_endit = + path_it->end_default(); // this determines when the loop has to stop + //Creamos las lineas rectas que unen todos los puntos del trazado y donde se + //calcularán + //los puntos clave para los manejadores. + //Esto hace que la curva BSpline no pierda su condición aunque se trasladen + //dichos manejadores + SPCurve *nCurve = new SPCurve(); + if (path_it->closed()) { + // if the path is closed, maybe we have to stop a bit earlier because the + // closing line segment has zerolength. + const Geom::Curve &closingline = + path_it->back_closed(); // the closing line segment is always of type + // Geom::LineSegment. + if (are_near(closingline.initialPoint(), closingline.finalPoint())) { + // closingline.isDegenerate() did not work, because it only checks for + // *exact* zero length, which goes wrong for relative coordinates and + // rounding errors... + // the closing line segment has zero-length. So stop before that one! + curve_endit = path_it->end_open(); + } + } + //Si la curva está cerrada calculamos el punto donde + //deveria estar el nodo BSpline de cierre/inicio de la curva + //en posible caso de que se cierre con una linea recta creando un nodo + //BSPline + Geom::Point initialMove(0, 0); + if (shiftNodes) { + initialMove = randomize(); + } + Geom::Point initialPoint = curve_it1->initialPoint() + initialMove; + nCurve->moveto(initialPoint); + Geom::Point A0(0, 0); + Geom::Point A1(0, 0); + Geom::Point A2(0, 0); + Geom::Point A3(0, 0); + while (curve_it2 != curve_endit) { + //aumentamos los valores para el siguiente paso en el bucle + //Recorremos todos los segmentos menos el último + Geom::CubicBezier const *cubic = NULL; + A0 = curve_it1->initialPoint(); + A1 = curve_it1->initialPoint(); + A2 = curve_it1->finalPoint(); + A3 = curve_it1->finalPoint(); + cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); + if (cubic) { + A1 = (*cubic)[1]; + if (shiftNodes) { + A1 = (*cubic)[1] + initialMove; + } + A2 = (*cubic)[2]; + nCurve->curveto(A1, A2, A3); + } else { + nCurve->lineto(A3); + } + double length = Inkscape::Util::Quantity::convert( + curve_it1->length(0.001), "px", unit.get_abbreviation()); + unsigned int splits = 0; + if (method == DM_SEGMENTS) { + splits = segments; + } else { + splits = ceil(length / maxSegmentSize); + } + for (unsigned int t = splits; t >= 1; t--) { + if (t == 1 && splits != 1) { + continue; + } + const SPCurve *tmp; + if (splits == 1) { + tmp = jitter(nCurve->last_segment()); + } else { + tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t); + } + if (nCurve->get_segment_count() > 1) { + nCurve->backspace(); + nCurve->append_continuous(tmp, 0.001); + } else { + nCurve = tmp->copy(); + } + delete tmp; + } + ++curve_it1; + ++curve_it2; + } + Geom::CubicBezier const *cubic = NULL; + A0 = curve_it1->initialPoint(); + A1 = curve_it1->initialPoint(); + A2 = curve_it1->finalPoint(); + A3 = curve_it1->finalPoint(); + cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); + if (cubic) { + A1 = (*cubic)[1]; + A2 = (*cubic)[2]; + if (path_it->closed()) { + A2 = A2 + initialMove; + A3 = initialPoint; + } + nCurve->curveto(A1, A2, A3); + } else { + if (path_it->closed()) { + A3 = initialPoint; + } + nCurve->lineto(A3); + } + double length = Inkscape::Util::Quantity::convert( + curve_it1->length(0.001), "px", unit.get_abbreviation()); + unsigned int splits = 0; + if (method == DM_SEGMENTS) { + splits = segments; + } else { + splits = ceil(length / maxSegmentSize); + } + for (unsigned int t = splits; t >= 1; t--) { + if (t == 1 && splits != 1) { + continue; + } + const SPCurve *tmp; + if (splits == 1) { + tmp = jitter(nCurve->last_segment()); + } else { + tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t); + } + if (nCurve->get_segment_count() > 1) { + nCurve->backspace(); + nCurve->append_continuous(tmp, 0.001); + } else { + nCurve = tmp->copy(); + } + delete tmp; + } + //y cerramos la curva + if (path_it->closed()) { + nCurve->closepath_current(); + } + curve->append(nCurve, false); + nCurve->reset(); + delete nCurve; + } +} + +SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t) { + SPCurve *out = new SPCurve(); + Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A); + Geom::Point A1(0, 0); + Geom::Point A2(0, 0); + Geom::Point A3(0, 0); + Geom::Point B1(0, 0); + Geom::Point B2(0, 0); + Geom::Point B3(0, 0); + if (shiftNodes) { + A3 = randomize(); + B3 = randomize(); + } + if (shiftNodeHandles) { + A1 = randomize(); + A2 = randomize(); + B1 = randomize(); + B2 = randomize(); + } else { + A2 = A3; + B1 = A3; + B2 = B3; + } + if (cubic) { + std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t); + std::vector<Geom::Point> seg1 = div.first.points(), + seg2 = div.second.points(); + out->moveto(seg1[0]); + out->curveto(seg1[1] + A1, seg1[2] + A2, seg1[3] + A3); + out->curveto(seg2[1] + B1, seg2[2], seg2[3]); + } else if (shiftNodeHandles) { + out->moveto(A->initialPoint()); + out->curveto(A->pointAt(t / 3) + A1, A->pointAt((t / 3) * 2) + A2, + A->pointAt(t) + A3); + out->curveto(A->pointAt(t + (t / 3)) + B1, A->pointAt(t + ((t / 3) * 2)), + A->finalPoint()); + } else { + out->moveto(A->initialPoint()); + out->lineto(A->pointAt(t) + A3); + out->lineto(A->finalPoint()); + } + return out; +} + +SPCurve *LPERoughen::jitter(const Geom::Curve *A) { + SPCurve *out = new SPCurve(); + Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A); + Geom::Point A1(0, 0); + Geom::Point A2(0, 0); + Geom::Point A3(0, 0); + if (shiftNodes) { + A3 = randomize(); + } + if (shiftNodeHandles) { + A1 = randomize(); + A2 = randomize(); + } else { + A2 = A3; + } + if (cubic) { + out->moveto((*cubic)[0]); + out->curveto((*cubic)[1] + A1, (*cubic)[2] + A2, (*cubic)[3] + A3); + } else if (shiftNodeHandles) { + out->moveto(A->initialPoint()); + out->curveto(A->pointAt(0.3333) + A1, A->pointAt(0.6666) + A2, + A->finalPoint() + A3); + } else { + out->moveto(A->initialPoint()); + out->lineto(A->finalPoint() + A3); + } + return out; +} + +Geom::Point LPERoughen::tpoint(Geom::Point A, Geom::Point B, double t) { + using Geom::X; + using Geom::Y; + return Geom::Point(A[X] + t * (B[X] - A[X]), A[Y] + t * (B[Y] - A[Y])); +} + +}; //namespace LivePathEffect +}; /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-roughen.h b/src/live_effects/lpe-roughen.h new file mode 100644 index 000000000..cc7784dc0 --- /dev/null +++ b/src/live_effects/lpe-roughen.h @@ -0,0 +1,57 @@ +#ifndef INKSCAPE_LPE_ROUGHEN_H +#define INKSCAPE_LPE_ROUGHEN_H + +/* + * Inkscape::LPERoughen + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#include "live_effects/parameter/enum.h" +#include "live_effects/effect.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/path.h" +#include "live_effects/parameter/bool.h" +#include "live_effects/parameter/unit.h" +#include "live_effects/parameter/random.h" + +namespace Inkscape { +namespace LivePathEffect { + +enum DivisionMethod { + DM_SEGMENTS, + DM_SIZE, + DM_END +}; + +class LPERoughen : public Effect { + +public: + LPERoughen(LivePathEffectObject *lpeobject); + virtual ~LPERoughen(); + + virtual void doEffect(SPCurve *curve); + virtual double sign(double randNumber); + virtual Geom::Point randomize(); + virtual void doBeforeEffect(SPLPEItem const */*lpeitem*/); + virtual SPCurve *addNodesAndJitter(const Geom::Curve *A, double t); + virtual SPCurve *jitter(const Geom::Curve *A); + virtual Geom::Point tpoint(Geom::Point A, Geom::Point B, double t = 0.5); + virtual Gtk::Widget *newWidget(); + +private: + UnitParam unit; + EnumParam<DivisionMethod> method; + ScalarParam maxSegmentSize; + ScalarParam segments; + RandomParam displaceX; + RandomParam displaceY; + BoolParam shiftNodes; + BoolParam shiftNodeHandles; + LPERoughen(const LPERoughen &); + LPERoughen &operator=(const LPERoughen &); + +}; + +}; //namespace LivePathEffect +}; //namespace Inkscape +#endif diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp index 44d414942..8095056b7 100644 --- a/src/live_effects/parameter/path.cpp +++ b/src/live_effects/parameter/path.cpp @@ -197,7 +197,7 @@ PathParam::param_newWidget() pButton->show(); pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_link_button_click)); static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); - pButton->set_tooltip_text(_("Link to path")); + pButton->set_tooltip_text(_("Link to path on clipboard")); static_cast<Gtk::HBox*>(_widget)->show_all_children(); diff --git a/src/object-edit.cpp b/src/object-edit.cpp index e9b183eca..fe22f6c1c 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -79,8 +79,8 @@ KnotHolder *createKnotHolder(SPItem *item, SPDesktop *desktop) knotholder = new OffsetKnotHolder(desktop, item, NULL); } else if (SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { knotholder = new FlowtextKnotHolder(desktop, SP_FLOWTEXT(item)->get_frame(NULL), NULL); - } else if ((item->style->fill.isPaintserver()) - && SP_IS_PATTERN(item->style->getFillPaintServer())) { + } else if ((item->style->fill.isPaintserver() && SP_IS_PATTERN(item->style->getFillPaintServer())) || + (item->style->stroke.isPaintserver() && SP_IS_PATTERN(item->style->getStrokePaintServer()))) { knotholder = new KnotHolder(desktop, item, NULL); knotholder->add_pattern_knotholder(); } diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 3b8956bc8..0f7aa6368 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -147,6 +147,10 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, // For debugging: print the id of the candidate to the console // SPObject *obj = (SPObject*)item; // std::cout << "Snap candidate added: " << obj->getId() << std::endl; + if (_candidates->size() > 200) { // This makes Inkscape crawl already + std::cout << "Warning: limit of 200 snap target paths reached, some will be ignored" << std::endl; + break; + } } } } diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 8837fbdb1..d77c06a95 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -1621,10 +1621,13 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Affine cons item->doWriteTransform(item->getRepr(), item->transform, NULL, compensate); } - // if we're moving the actual object, not just updating the repr, we can transform the + // if we're transforming the actual object, not just updating the repr, we can transform the // center by the same matrix (only necessary for non-translations) if (set_i2d && item->isCenterSet() && !(affine.isTranslation() || affine.isIdentity())) { - item->setCenter(old_center * affine); + // If there's a viewbox, we might have an affine with a translation component; + // we will only apply the scaling/skewing components, not the translations + // because otherwise the center will move relative to the item + item->setCenter(old_center * affine.withoutTranslation()); item->updateRepr(); } } diff --git a/src/seltrans.cpp b/src/seltrans.cpp index d6f31f073..6b8cd19bb 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -304,6 +304,7 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s /* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes A typical user would rarely ever try to snap such a large number of nodes anyway, because (s)he would hardly be able to discern which node would be snapping */ + std::cout << "Warning: limit of 200 snap sources reached, some will be ignored" << std::endl; _snap_points.resize(200); // Unfortunately, by now we will have lost the font-baseline snappoints :-( } @@ -377,12 +378,6 @@ void Inkscape::SelTrans::transform(Geom::Affine const &rel_affine, Geom::Point c g_return_if_fail(_grabbed); g_return_if_fail(!_empty); - // E.g. scaling a perfectly vertical line in horizontal direction will not work, and will produce an identity affine - - if (rel_affine.isIdentity()) { - return; - } - Geom::Affine const affine( Geom::Translate(-norm) * rel_affine * Geom::Translate(norm) ); if (_show == SHOW_CONTENT) { diff --git a/src/shape-editor.cpp b/src/shape-editor.cpp index bf53e8bc3..7915fe533 100644 --- a/src/shape-editor.cpp +++ b/src/shape-editor.cpp @@ -14,24 +14,15 @@ #include <string.h> #include <glibmm/i18n.h> -#include "sp-object.h" -#include "sp-item.h" -#include "sp-lpe-item.h" -#include "live_effects/lpeobject.h" -#include "selection.h" -#include "desktop.h" -#include "document.h" #include "desktop-handles.h" +#include "document.h" +#include "gc-anchored.h" #include "knotholder.h" -#include "live_effects/parameter/point.h" -#include "xml/node-event-vector.h" -#include "preferences.h" #include "object-edit.h" -#include "style.h" -#include "display/curve.h" -#include <2geom/pathvector.h> - +#include "sp-item.h" +#include "sp-object.h" #include "shape-editor.h" +#include "xml/node-event-vector.h" using Inkscape::createKnotHolder; @@ -44,133 +35,80 @@ ShapeEditor::ShapeEditor(SPDesktop *dt) { } ShapeEditor::~ShapeEditor() { - unset_item(SH_KNOTHOLDER); + unset_item(); } -void ShapeEditor::unset_item(SubType type, bool keep_knotholder) { - switch (type) { - case SH_NODEPATH: - // defunct - break; - case SH_KNOTHOLDER: - if (this->knotholder) { - Inkscape::XML::Node *old_repr = this->knotholder->repr; - if (old_repr && old_repr == knotholder_listener_attached_for) { - sp_repr_remove_listener_by_data(old_repr, this); - Inkscape::GC::release(old_repr); - knotholder_listener_attached_for = NULL; - } - - if (!keep_knotholder) { - delete this->knotholder; - this->knotholder = NULL; - } - } - break; - } -} +void ShapeEditor::unset_item(bool keep_knotholder) { + if (this->knotholder) { + Inkscape::XML::Node *old_repr = this->knotholder->repr; + if (old_repr && old_repr == knotholder_listener_attached_for) { + sp_repr_remove_listener_by_data(old_repr, this); + Inkscape::GC::release(old_repr); + knotholder_listener_attached_for = NULL; + } -bool ShapeEditor::has_nodepath () { - return false; + if (!keep_knotholder) { + delete this->knotholder; + this->knotholder = NULL; + } + } } -bool ShapeEditor::has_knotholder () { - return (this->knotholder != NULL); +bool ShapeEditor::has_knotholder() { + return this->knotholder != NULL; } -void ShapeEditor::update_knotholder () { +void ShapeEditor::update_knotholder() { if (this->knotholder) this->knotholder->update_knots(); } -bool ShapeEditor::has_local_change (SubType type) { - switch (type) { - case SH_NODEPATH: - // defunct - return false; - case SH_KNOTHOLDER: - return (this->knotholder && this->knotholder->local_change != 0); - default: - g_assert_not_reached(); - } +bool ShapeEditor::has_local_change() { + return (this->knotholder && this->knotholder->local_change != 0); } -void ShapeEditor::decrement_local_change (SubType type) { - switch (type) { - case SH_NODEPATH: - // defunct - break; - case SH_KNOTHOLDER: - if (this->knotholder) { - this->knotholder->local_change = FALSE; - } - break; - default: - g_assert_not_reached(); +void ShapeEditor::decrement_local_change() { + if (this->knotholder) { + this->knotholder->local_change = FALSE; } } -const SPItem *ShapeEditor::get_item (SubType type) { +const SPItem *ShapeEditor::get_item() { const SPItem *item = NULL; - switch (type) { - case SH_NODEPATH: - // defunct - break; - case SH_KNOTHOLDER: - if (this->has_knotholder()) { - item = this->knotholder->getItem(); - } - break; + if (this->has_knotholder()) { + item = this->knotholder->getItem(); } return item; } -GList *ShapeEditor::save_nodepath_selection () { - // defunct stub - return NULL; -} - -void ShapeEditor::restore_nodepath_selection (GList */*saved*/) { - // defunct stub -} - -void ShapeEditor::shapeeditor_event_attr_changed(gchar const *name) +void ShapeEditor::event_attr_changed(Inkscape::XML::Node *, gchar const *name, gchar const *, gchar const *, bool, void *data) { - gboolean changed_kh = FALSE; + g_assert(data); + ShapeEditor *sh = static_cast<ShapeEditor *>(data); + bool changed_kh = false; - if (has_knotholder()) + if (sh->has_knotholder()) { - changed_kh = !has_local_change(SH_KNOTHOLDER); - decrement_local_change(SH_KNOTHOLDER); + changed_kh = !sh->has_local_change(); + sh->decrement_local_change(); if (changed_kh) { // this can happen if an LPEItem's knotholder handle was dragged, in which case we want // to keep the knotholder; in all other cases (e.g., if the LPE itself changes) we delete it - reset_item(SH_KNOTHOLDER, !strcmp(name, "d")); + sh->reset_item(!strcmp(name, "d")); } } } - -static void shapeeditor_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name, - gchar const */*old_value*/, gchar const */*new_value*/, - bool /*is_interactive*/, gpointer data) -{ - g_assert(data); - ShapeEditor *sh = static_cast<ShapeEditor *>(data); - - sh->shapeeditor_event_attr_changed(name); -} - static Inkscape::XML::NodeEventVector shapeeditor_repr_events = { NULL, /* child_added */ NULL, /* child_removed */ - shapeeditor_event_attr_changed, + ShapeEditor::event_attr_changed, NULL, /* content_changed */ NULL /* order_changed */ }; -void ShapeEditor::set_item(SPItem *item, SubType type, bool keep_knotholder) { +void ShapeEditor::set_item(SPItem *item, bool keep_knotholder) { if (_blockSetItem) { return; } @@ -178,60 +116,38 @@ void ShapeEditor::set_item(SPItem *item, SubType type, bool keep_knotholder) { // this happens (and should only happen) when for an LPEItem having both knotholder and // nodepath the knotholder is adapted; in this case we don't want to delete the knotholder // since this freezes the handles - unset_item(type, keep_knotholder); + unset_item(keep_knotholder); if (item) { Inkscape::XML::Node *repr; - switch(type) { - case SH_NODEPATH: - // defunct - break; - - case SH_KNOTHOLDER: - if (!this->knotholder) { - // only recreate knotholder if none is present - this->knotholder = createKnotHolder(item, desktop); - } - if (this->knotholder) { - this->knotholder->update_knots(); - // setting new listener - repr = this->knotholder->repr; - if (repr != knotholder_listener_attached_for) { - Inkscape::GC::anchor(repr); - sp_repr_add_listener(repr, &shapeeditor_repr_events, this); - knotholder_listener_attached_for = repr; - } - } - break; + if (!this->knotholder) { + // only recreate knotholder if none is present + this->knotholder = createKnotHolder(item, desktop); + } + if (this->knotholder) { + this->knotholder->update_knots(); + // setting new listener + repr = this->knotholder->repr; + if (repr != knotholder_listener_attached_for) { + Inkscape::GC::anchor(repr); + sp_repr_add_listener(repr, &shapeeditor_repr_events, this); + knotholder_listener_attached_for = repr; + } } } } /** FIXME: This thing is only called when the item needs to be updated in response to repr change. - Why not make a reload function in NodePath and in KnotHolder? */ -void ShapeEditor::reset_item (SubType type, bool keep_knotholder) + Why not make a reload function in KnotHolder? */ +void ShapeEditor::reset_item(bool keep_knotholder) { - switch (type) { - case SH_NODEPATH: - // defunct - break; - case SH_KNOTHOLDER: - if ( knotholder ) { - SPObject *obj = sp_desktop_document(desktop)->getObjectByRepr(knotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. - set_item(SP_ITEM(obj), SH_KNOTHOLDER, keep_knotholder); - } - break; + if (knotholder) { + SPObject *obj = sp_desktop_document(desktop)->getObjectByRepr(knotholder_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. + set_item(SP_ITEM(obj), keep_knotholder); } } -void ShapeEditor::nodepath_destroyed () { -} - -bool ShapeEditor::has_selection() { - return false; // so far, knotholder cannot have selection -} - /** * Returns true if this ShapeEditor has a knot above which the mouse currently hovers. */ diff --git a/src/shape-editor.h b/src/shape-editor.h index ec4b50fa3..df0dbfa3a 100644 --- a/src/shape-editor.h +++ b/src/shape-editor.h @@ -4,8 +4,8 @@ /* * Inkscape::ShapeEditor * - * This is a container class which contains either knotholder (for shapes) or nodepath (for - * paths). It is attached to a single item so only one of these is active at a time. + * This is a container class which contains a knotholder for shapes. + * It is attached to a single item. * * Authors: * bulia byak <buliabyak@users.sf.net> @@ -14,27 +14,12 @@ #include <glib.h> -#include <2geom/forward.h> - - -namespace Inkscape { namespace NodePath { class Path; } } namespace Inkscape { namespace XML { class Node; } } class KnotHolder; class LivePathEffectObject; class SPDesktop; class SPItem; -class SPNodeContext; -class ShapeEditorsCollective; - -#include <2geom/point.h> -#include <boost/optional.hpp> -#include <vector> - -enum SubType{ - SH_NODEPATH, - SH_KNOTHOLDER -}; class ShapeEditor { public: @@ -42,36 +27,25 @@ public: ShapeEditor(SPDesktop *desktop); ~ShapeEditor(); - void set_item (SPItem *item, SubType type, bool keep_knotholder = false); - void unset_item (SubType type, bool keep_knotholder = false); - - bool has_nodepath (); //((deprecated)) - void update_knotholder (); //((deprecated)) + void set_item(SPItem *item, bool keep_knotholder = false); + void unset_item(bool keep_knotholder = false); - bool has_local_change (SubType type); - void decrement_local_change (SubType type); + void update_knotholder(); //((deprecated)) - GList *save_nodepath_selection (); - void restore_nodepath_selection (GList *saved); - - void nodepath_destroyed (); - - bool has_selection (); - - Inkscape::NodePath::Path *get_nodepath() {return NULL;} //((deprecated)) - ShapeEditorsCollective *get_container() {return NULL;} - - // this one is only public because it's called from non-C++ repr changed callback - void shapeeditor_event_attr_changed(gchar const *name); + bool has_local_change(); + void decrement_local_change(); bool knot_mouseover() const; - static void blockSetItem(bool b) {_blockSetItem = b;} + static void blockSetItem(bool b) { _blockSetItem = b; } // kludge? + static void event_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const *name, gchar const * /*old_value*/, + gchar const * /*new_value*/, bool /*is_interactive*/, void *data); private: - bool has_knotholder (); - void reset_item (SubType type, bool keep_knotholder = true); - const SPItem *get_item (SubType type); + bool has_knotholder(); + void reset_item (bool keep_knotholder = true); + const SPItem *get_item(); + static bool _blockSetItem; SPDesktop *desktop; diff --git a/src/sp-flowregion.cpp b/src/sp-flowregion.cpp index 709e9e464..32d3b0f60 100644 --- a/src/sp-flowregion.cpp +++ b/src/sp-flowregion.cpp @@ -42,15 +42,12 @@ static void GetDest(SPObject* child,Shape **computed); SPFlowregion::SPFlowregion() : SPItem() { - //new (&this->computed) std::vector<Shape*>; } SPFlowregion::~SPFlowregion() { for (std::vector<Shape*>::iterator it = this->computed.begin() ; it != this->computed.end() ; ++it) { delete *it; } - - //this->computed.~vector<Shape*>(); } void SPFlowregion::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) { @@ -185,6 +182,8 @@ Inkscape::XML::Node *SPFlowregion::write(Inkscape::XML::Document *xml_doc, Inksc SPItem::write(xml_doc, repr, flags); + this->UpdateComputed(); // copied from update(), see LP Bug 1339305 + return repr; } @@ -372,6 +371,8 @@ static void GetDest(SPObject* child,Shape **computed) tr_mat = SP_ITEM(u_child)->transform; } if ( SP_IS_SHAPE (u_child) ) { + if (!(SP_SHAPE(u_child)->_curve)) + SP_SHAPE (u_child)->set_shape (); curve = SP_SHAPE (u_child)->getCurve (); } else if ( SP_IS_TEXT (u_child) ) { curve = SP_TEXT (u_child)->getNormalizedBpath (); diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index b4fd54ee4..7de65ccc3 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -46,11 +46,9 @@ namespace { SPFlowtext::SPFlowtext() : SPItem() { this->par_indent = 0; - //new (&this->layout) Inkscape::Text::Layout(); } SPFlowtext::~SPFlowtext() { - //this->layout.~Layout(); } void SPFlowtext::child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref) { @@ -255,6 +253,8 @@ Inkscape::XML::Node* SPFlowtext::write(Inkscape::XML::Document* doc, Inkscape::X } } + this->rebuildLayout(); // copied from update(), see LP Bug 1339305 + SPItem::write(doc, repr, flags); return repr; diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 1479acd69..70c54451a 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -262,8 +262,6 @@ SPGradient::SPGradient() : SPPaintServer(), units(), this->vector.built = false; this->vector.stops.clear(); - - //new (&this->modified_connection) sigc::connection(); } SPGradient::~SPGradient() { diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 2cfe97db8..657aca692 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -70,11 +70,9 @@ namespace { SPGroup::SPGroup() : SPLPEItem() { this->_layer_mode = SPGroup::GROUP; - //new (&this->_display_modes) std::map<unsigned int, SPGroup::LayerMode>(); } SPGroup::~SPGroup() { - //this->_display_modes.~map(); } void SPGroup::build(SPDocument *document, Inkscape::XML::Node *repr) { @@ -664,7 +662,8 @@ void SPGroup::scaleChildItemsRec(Geom::Scale const &sc, Geom::Point const &p) Geom::Point old_center(0,0); if (item->isCenterSet()) { - old_center = item->getCenter(); + item->scaleCenter(sc.inverse()); // Convert the old relative center position to the new coordinates already now + old_center = item->getCenter(); // because getCenter() will use the bbox midpoint, which is also already in the new coordinates } gchar const *conn_type = NULL; diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 0f068a914..251825299 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -118,10 +118,6 @@ SPItem::SPItem() : SPObject() { mask_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(mask_ref_changed), this)); avoidRef = new SPAvoidRef(this); - - //new (&constraints) std::vector<SPGuideConstraint>(); - - //new (&_transformed_signal) sigc::signal<void, Geom::Affine const *, SPItem *>(); } SPItem::~SPItem() { @@ -271,6 +267,11 @@ Geom::Point SPItem::getCenter() const { } } +void +SPItem::scaleCenter(Geom::Scale const &sc) { + transform_center_x *= sc[Geom::X]; + transform_center_y *= sc[Geom::Y]; +} namespace { @@ -601,7 +602,7 @@ void SPItem::update(SPCtx* /*ctx*/, guint flags) { v->arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(object->style->opacity.value)); v->arenaitem->setAntialiasing(object->style->shape_rendering.computed != SP_CSS_SHAPE_RENDERING_CRISPEDGES); v->arenaitem->setIsolation( object->style->isolation.value ); - v->arenaitem->setBlendMode( object->style->blend_mode.value ); + v->arenaitem->setBlendMode( object->style->mix_blend_mode.value ); v->arenaitem->setVisible(!item->isHidden()); } } @@ -777,6 +778,7 @@ Geom::OptRect SPItem::visualBounds(Geom::Affine const &transform) const bbox = const_cast<SPItem*>(this)->bbox(transform, SPItem::VISUAL_BBOX); } if (clip_ref->getObject()) { + SP_ITEM(clip_ref->getOwner())->bbox_valid = FALSE; // LP Bug 1349018 bbox.intersectWith(SP_CLIPPATH(clip_ref->getObject())->geometricBounds(transform)); } @@ -1027,7 +1029,7 @@ Inkscape::DrawingItem *SPItem::invoke_show(Inkscape::Drawing &drawing, unsigned ai->setTransform(transform); ai->setOpacity(SP_SCALE24_TO_FLOAT(style->opacity.value)); ai->setIsolation( style->isolation.value ); - ai->setBlendMode( style->blend_mode.value ); + ai->setBlendMode( style->mix_blend_mode.value ); //ai->setCompositeOperator( style->composite_op.value ); ai->setVisible(!isHidden()); ai->setSensitive(sensitive); @@ -1108,9 +1110,10 @@ void SPItem::invoke_hide(unsigned key) // Adjusters -void SPItem::adjust_pattern (Geom::Affine const &postmul, bool set) +void SPItem::adjust_pattern(Geom::Affine const &postmul, bool set, PatternTransform pt) { - if (style && (style->fill.isPaintserver())) { + bool fill = (pt == TRANSFORM_FILL || pt == TRANSFORM_BOTH); + if (fill && style && (style->fill.isPaintserver())) { SPObject *server = style->getFillPaintServer(); if ( SP_IS_PATTERN(server) ) { SPPattern *pattern = sp_pattern_clone_if_necessary(this, SP_PATTERN(server), "fill"); @@ -1118,7 +1121,8 @@ void SPItem::adjust_pattern (Geom::Affine const &postmul, bool set) } } - if (style && (style->stroke.isPaintserver())) { + bool stroke = (pt == TRANSFORM_STROKE || pt == TRANSFORM_BOTH); + if (stroke && style && (style->stroke.isPaintserver())) { SPObject *server = style->getStrokePaintServer(); if ( SP_IS_PATTERN(server) ) { SPPattern *pattern = sp_pattern_clone_if_necessary(this, SP_PATTERN(server), "stroke"); diff --git a/src/sp-item.h b/src/sp-item.h index d605c99b9..15784d041 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -51,6 +51,14 @@ enum { SP_EVENT_MOUSEOUT }; +// TODO make a completely new function that transforms either the fill or +// stroke of any SPItem without adding an extra parameter to adjust_pattern. +enum PatternTransform { + TRANSFORM_BOTH, + TRANSFORM_FILL, + TRANSFORM_STROKE +}; + /** * Event structure. * @@ -159,6 +167,7 @@ public: void unsetCenter(); bool isCenterSet() const; Geom::Point getCenter() const; + void scaleCenter(Geom::Scale const &sc); bool isVisibleAndUnlocked() const; @@ -198,7 +207,7 @@ public: Inkscape::DrawingItem *invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags); void invoke_hide(unsigned int key); void getSnappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs=0) const; - void adjust_pattern(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false); + void adjust_pattern(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false, PatternTransform = TRANSFORM_BOTH); void adjust_gradient(/* Geom::Affine const &premul, */ Geom::Affine const &postmul, bool set = false); void adjust_stroke(gdouble ex); void adjust_stroke_width_recursive(gdouble ex); diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 439e44508..bb7d9f273 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -340,6 +340,7 @@ static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem) { g_return_if_fail(lpeitem != NULL); + if (SP_IS_GROUP(lpeitem)) { GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); for ( GSList const *iter = item_list; iter; iter = iter->next ) { @@ -361,6 +362,7 @@ static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem) { g_return_if_fail(lpeitem != NULL); + if (SP_IS_GROUP(lpeitem)) { GSList const *item_list = sp_item_group_item_list(SP_GROUP(lpeitem)); for ( GSList const *iter = item_list; iter; iter = iter->next ) { diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp index c6a4b730d..3aa1e4eb6 100644 --- a/src/sp-offset.cpp +++ b/src/sp-offset.cpp @@ -107,11 +107,6 @@ SPOffset::SPOffset() : SPShape() { this->sourceRepr = NULL; this->sourceObject = NULL; - new (&this->_modified_connection) sigc::connection(); - new (&this->_delete_connection) sigc::connection(); - new (&this->_changed_connection) sigc::connection(); - new (&this->_transformed_connection) sigc::connection(); - // set up the uri reference this->sourceRef = new SPUseReference(this); this->_changed_connection = this->sourceRef->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_offset_href_changed), this)); diff --git a/src/sp-path.cpp b/src/sp-path.cpp index d1fb850e1..5c076b7cb 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -140,10 +140,10 @@ void SPPath::build(SPDocument *document, Inkscape::XML::Node *repr) { SPShape::build(document, repr); - //this->readAttr( "inkscape:original-d" ); //lp1299948 + // this->readAttr( "inkscape:original-d" ); // lp1299948 if (gchar const* s = this->getRepr()->attribute("inkscape:original-d")) { - //write it to XML, and to my curve, but don't update patheffects + // write it to XML, and to my curve, but don't update patheffects Geom::PathVector pv = sp_svg_read_pathv(s); SPCurve *curve = new SPCurve(pv); @@ -154,7 +154,6 @@ void SPPath::build(SPDocument *document, Inkscape::XML::Node *repr) { if (curve) { _curve_before_lpe = curve->ref(); } - //this->getRepr()->setAttribute("inkscape:original-d", s); } this->readAttr( "d" ); diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index 61d35e6ff..de9103dee 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -57,8 +57,6 @@ static void sp_shape_update_marker_view (SPShape *shape, Inkscape::DrawingItem * SPShape::SPShape() : SPLPEItem() { for ( int i = 0 ; i < SP_MARKER_LOC_QTY ; i++ ) { - new (&this->_release_connect[i]) sigc::connection(); - new (&this->_modified_connect[i]) sigc::connection(); this->_marker[i] = NULL; } diff --git a/src/sp-star.cpp b/src/sp-star.cpp index eac33ed7b..712029468 100644 --- a/src/sp-star.cpp +++ b/src/sp-star.cpp @@ -513,10 +513,12 @@ void SPStar::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape:: } } -Geom::Affine SPStar::set_transform(Geom::Affine const &xform) +Geom::Affine SPStar::set_transform(Geom::Affine const &tform) { + Geom::Affine xform = (randomized == 0 ? tform.withoutTranslation() : tform); + // Only set transform with proportional scaling - if (!xform.withoutTranslation().isUniformScale()) { + if (!xform.isUniformScale()) { return xform; } @@ -530,7 +532,7 @@ Geom::Affine SPStar::set_transform(Geom::Affine const &xform) /* This function takes care of translation and scaling, we return whatever parts we can't handle. */ - Geom::Affine ret(Geom::Affine(xform).withoutTranslation()); + Geom::Affine ret(xform); gdouble const s = hypot(ret[0], ret[1]); if (s > 1e-9) { ret[0] /= s; diff --git a/src/sp-string.cpp b/src/sp-string.cpp index 08755a5fc..b561187d0 100644 --- a/src/sp-string.cpp +++ b/src/sp-string.cpp @@ -30,10 +30,14 @@ #include "sp-string.h" +#include "style.h" + #include "xml/repr.h" #include "sp-factory.h" +#include <iostream> + namespace { SPObject* createString() { return new SPString(); @@ -47,7 +51,6 @@ namespace { #####################################################*/ SPString::SPString() : SPObject() { - //new (&this->string) Glib::ustring(); } SPString::~SPString() { @@ -66,45 +69,110 @@ void SPString::release() { void SPString::read_content() { - SPString* object = this; + SPString* object = this; SPString *string = SP_STRING(object); string->string.clear(); //XML Tree being used directly here while it shouldn't be. gchar const *xml_string = string->getRepr()->content(); - // see algorithms described in svg 1.1 section 10.15 - if (object->xml_space.value == SP_XML_SPACE_PRESERVE) { - for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) { - gunichar c = g_utf8_get_char(xml_string); - if ((c == 0xa) || (c == 0xd) || (c == '\t')) { - c = ' '; - } - string->string += c; + + // std::cout << ">" << (xml_string?xml_string:"Null") << "<" << std::endl; + + // SVG2/CSS Text Level 3 'white-space' has five values. + // See: http://dev.w3.org/csswg/css-text/#white-space + // | New Lines | Spaces/Tabs | Text Wrapping + // ---------|------------|--------------|-------------- + // normal | Collapes | Collapse | Wrap + // pre | Preserve | Preserve | No Wrap + // nowrap | Collapse | Collapse | No Wrap + // pre-wrap | Preserve | Preserve | Wrap + // pre-line | Preserve | Collapse | Wrap + + // 'xml:space' has two values: + // 'default' which corresponds to 'normal' (without wrapping). + // 'preserve' which corresponds to 'pre' except new lines are converted to spaces. + // See algorithms described in svg 1.1 section 10.15 + + bool collapse_space = true; + bool collapse_line = true; + bool is_css = false; + + // Strings don't have style, check parent for style + if( object->parent && object->parent->style ) { + if( object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRE || + object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PREWRAP || + object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRELINE ) { + collapse_line = false; + } + if( object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PRE || + object->parent->style->white_space.computed == SP_CSS_WHITE_SPACE_PREWRAP ) { + collapse_space = false; + } + if( object->parent->style->white_space.computed != SP_CSS_WHITE_SPACE_NORMAL ) { + is_css = true; // If white-space not normal, we assume white-space is set. } } - else { - bool whitespace = false; - for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) { - gunichar c = g_utf8_get_char(xml_string); - if ((c == 0xa) || (c == 0xd)) { + if( !is_css ) { + // SVG 2: Use 'xml:space' only if 'white-space' not 'normal'. + if (object->xml_space.value == SP_XML_SPACE_PRESERVE) { + collapse_space = false; + } + } + + bool white_space = false; + for ( ; *xml_string ; xml_string = g_utf8_next_char(xml_string) ) { + + gunichar c = g_utf8_get_char(xml_string); + switch (c) { + case 0xd: // Carriage return + // XML Parsers convert 0xa, 0xd, 0xD 0xA to 0xA. CSS also follows this rule so we + // should never see 0xd. + std::cerr << "SPString: Carriage Return found! Argh!" << std::endl; continue; - } - if ((c == ' ') || (c == '\t')) { - whitespace = true; - } else { - if (whitespace && (!string->string.empty() || (object->getPrev() != NULL))) { + break; + case 0xa: // Line feed + if( collapse_line ) { + if( !is_css && collapse_space ) continue; // xml:space == 'default' strips LFs. + white_space = true; // Convert to space and collapse + } else { + string->string += c; // Preserve line feed + continue; + } + break; + case '\t': // Tab + if( collapse_space ) { + white_space = true; // Convert to space and collapse + } else { + string->string += c; // Preserve tab + continue; + } + break; + case ' ': // Space + if( collapse_space ) { + white_space = true; // Collapse white space + } else { + string->string += c; // Preserve space + continue; + } + break; + default: + if( white_space && (!string->string.empty() || (object->getPrev() != NULL))) { string->string += ' '; } string->string += c; - whitespace = false; - } - } - if (whitespace && object->getRepr()->next() != NULL) { // can't use SPObject::getNext() when the SPObject tree is still being built - string->string += ' '; - } + white_space = false; + + } // End switch + } // End loop + + // Insert white space at end if more text follows + if (white_space && object->getRepr()->next() != NULL) { // can't use SPObject::getNext() when the SPObject tree is still being built + string->string += ' '; } + + // std::cout << ">" << string->string << "<" << std::endl; object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/sp-text.cpp b/src/sp-text.cpp index bbc7ec43d..3bb1e16d0 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -70,8 +70,6 @@ namespace { # SPTEXT #####################################################*/ SPText::SPText() : SPItem() { - //new (&this->layout) Inkscape::Text::Layout; - //new (&this->attributes) TextTagAttributes; } SPText::~SPText() { @@ -84,19 +82,22 @@ void SPText::build(SPDocument *doc, Inkscape::XML::Node *repr) { this->readAttr( "dy" ); this->readAttr( "rotate" ); + // SVG 2 Auto wrapped text + this->readAttr( "width" ); + this->readAttr( "height" ); + SPItem::build(doc, repr); this->readAttr( "sodipodi:linespacing" ); // has to happen after the styles are read } void SPText::release() { - //this->attributes.~TextTagAttributes(); - //this->layout.~Layout(); - SPItem::release(); } void SPText::set(unsigned int key, const gchar* value) { + //std::cout << "SPText::set: " << sp_attribute_name( key ) << ": " << (value?value:"Null") << std::endl; + if (this->attributes.readSingleAttribute(key, value)) { this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } else { @@ -114,6 +115,22 @@ void SPText::set(unsigned int key, const gchar* value) { this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); break; + case SP_ATTR_WIDTH: + if (!this->width.read(value) || this->width.value < 0.0) { + this->width.unset(); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_HEIGHT: + if (!this->height.read(value) || this->height.value < 0.0) { + this->height.unset(); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + default: SPItem::set(key, value); break; @@ -278,6 +295,7 @@ Inkscape::XML::Node *SPText::write(Inkscape::XML::Document *xml_doc, Inkscape::X } this->attributes.writeTo(repr); + this->rebuildLayout(); // copied from update(), see LP Bug 1339305 // deprecated attribute, but keep it around for backwards compatibility if (this->style->line_height.set && !this->style->line_height.inherit && !this->style->line_height.normal && this->style->line_height.unit == SP_CSS_UNIT_PERCENT) { @@ -288,6 +306,14 @@ Inkscape::XML::Node *SPText::write(Inkscape::XML::Document *xml_doc, Inkscape::X this->getRepr()->setAttribute("sodipodi:linespacing", NULL); } + // SVG 2 Auto-wrapped text + if( this->width.computed > 0.0 ) { + sp_repr_set_svg_double(repr, "width", this->width.computed); + } + if( this->height.computed > 0.0 ) { + sp_repr_set_svg_double(repr, "height", this->height.computed); + } + SPItem::write(xml_doc, repr, flags); return repr; diff --git a/src/sp-text.h b/src/sp-text.h index 5f0485083..b9ae745c9 100644 --- a/src/sp-text.h +++ b/src/sp-text.h @@ -45,7 +45,11 @@ public: //semiprivate: (need to be accessed by the C-style functions still) TextTagAttributes attributes; Inkscape::Text::Layout layout; - + + // SVG 2 Auto-wrapped text + SVGLength width; + SVGLength height; + /** when the object is transformed it's nicer to change the font size and coordinates when we can, rather than just applying a matrix transform. is_root is used to indicate to the function that it should diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp index edbb9faa7..0f6eb106f 100644 --- a/src/sp-tref.cpp +++ b/src/sp-tref.cpp @@ -64,12 +64,8 @@ static void sp_tref_delete_self(SPObject *deleted, SPTRef *self); SPTRef::SPTRef() : SPItem() { this->stringChild = NULL; - //new (&this->attributes) TextTagAttributes; - this->href = NULL; this->uriOriginalRef = new SPTRefReference(this); - //new (&this->_delete_connection) sigc::connection(); - //new (&this->_changed_connection) sigc::connection(); this->_changed_connection = this->uriOriginalRef->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_tref_href_changed), this)); @@ -77,9 +73,6 @@ SPTRef::SPTRef() : SPItem() { SPTRef::~SPTRef() { delete this->uriOriginalRef; - - //this->_delete_connection.~connection(); - //this->_changed_connection.~connection(); } void SPTRef::build(SPDocument *document, Inkscape::XML::Node *repr) { diff --git a/src/sp-tspan.cpp b/src/sp-tspan.cpp index 8c4105777..c3f7689e7 100644 --- a/src/sp-tspan.cpp +++ b/src/sp-tspan.cpp @@ -63,7 +63,6 @@ namespace { #####################################################*/ SPTSpan::SPTSpan() : SPItem() { this->role = SP_TSPAN_ROLE_UNSPECIFIED; - //new (&this->attributes) TextTagAttributes; } SPTSpan::~SPTSpan() { @@ -81,8 +80,6 @@ void SPTSpan::build(SPDocument *doc, Inkscape::XML::Node *repr) { } void SPTSpan::release() { - //this->attributes.~TextTagAttributes(); - SPItem::release(); } @@ -227,8 +224,6 @@ const char* SPTSpan::displayName() const { void refresh_textpath_source(SPTextPath* offset); SPTextPath::SPTextPath() : SPItem() { - //new (&this->attributes) TextTagAttributes; - this->startOffset._set = false; this->originalPath = NULL; this->isUpdating=false; diff --git a/src/sp-use.cpp b/src/sp-use.cpp index e8fe3687f..e3fcd252a 100644 --- a/src/sp-use.cpp +++ b/src/sp-use.cpp @@ -37,6 +37,9 @@ #include "sp-symbol.h" #include "sp-use.h" #include "sp-use-reference.h" +#include "sp-shape.h" +#include "sp-text.h" +#include "sp-flowtext.h" namespace { SPObject* createUse() { @@ -176,6 +179,16 @@ Inkscape::XML::Node* SPUse::write(Inkscape::XML::Document *xml_doc, Inkscape::XM g_free(uri_string); } + if (SP_IS_SHAPE(this->child)) { + SP_SHAPE(this->child)->set_shape(); // evaluate SPCurve of child + } else if (SP_IS_TEXT(this->child)) { + SP_TEXT(this->child)->rebuildLayout(); // refresh Layout, LP Bug 1339305 + } else if (SP_IS_FLOWTEXT(this->child)) { + if (SP_IS_FLOWREGION(SP_FLOWTEXT(this->child)->firstChild())) + SP_FLOWREGION(SP_FLOWTEXT(this->child)->firstChild())->UpdateComputed(); + SP_FLOWTEXT(this->child)->rebuildLayout(); + } + return repr; } diff --git a/src/style-enums.h b/src/style-enums.h index 356029a40..024943458 100644 --- a/src/style-enums.h +++ b/src/style-enums.h @@ -113,6 +113,14 @@ enum SPTextAnchor { SP_CSS_TEXT_ANCHOR_END }; +enum SPWhiteSpace { + SP_CSS_WHITE_SPACE_NORMAL, + SP_CSS_WHITE_SPACE_PRE, + SP_CSS_WHITE_SPACE_NOWRAP, + SP_CSS_WHITE_SPACE_PREWRAP, + SP_CSS_WHITE_SPACE_PRELINE +}; + enum SPCSSBaselineShift { SP_CSS_BASELINE_SHIFT_BASELINE, SP_CSS_BASELINE_SHIFT_SUB, @@ -326,6 +334,15 @@ static SPStyleEnum const enum_text_anchor[] = { {NULL, -1} }; +static SPStyleEnum const enum_white_space[] = { + {"normal", SP_CSS_WHITE_SPACE_NORMAL }, + {"pre", SP_CSS_WHITE_SPACE_PRE }, + {"nowrap", SP_CSS_WHITE_SPACE_NOWRAP }, + {"pre-wrap", SP_CSS_WHITE_SPACE_PREWRAP}, + {"pre-line", SP_CSS_WHITE_SPACE_PRELINE}, + {NULL, -1} +}; + static SPStyleEnum const enum_direction[] = { {"ltr", SP_CSS_DIRECTION_LTR}, {"rtl", SP_CSS_DIRECTION_RTL}, diff --git a/src/style-internal.cpp b/src/style-internal.cpp index c686a1807..2212f8fff 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -270,18 +270,18 @@ SPILength::read( gchar const *str ) { } else if (!strcmp(e, "em")) { /* EM square */ unit = SP_CSS_UNIT_EM; - if( style && &style->font_size ) { + if( style ) { computed = value * style->font_size.computed; } else { - computed = value * style->font_size.font_size_default; + computed = value * SPIFontSize::font_size_default; } } else if (!strcmp(e, "ex")) { /* ex square */ unit = SP_CSS_UNIT_EX; - if( style && &style->font_size ) { + if( style ) { computed = value * style->font_size.computed * 0.5; // FIXME } else { - computed = value * style->font_size.font_size_default * 0.5; + computed = value * SPIFontSize::font_size_default * 0.5; } } else if (!strcmp(e, "%")) { /* Percentage */ @@ -1390,13 +1390,13 @@ SPIFilter::read( gchar const *str ) { } } -const Glib::ustring -SPIFilter::write( guint const flags, SPIBase const *const base) const { - +const Glib::ustring SPIFilter::write( guint const flags, SPIBase const *const /*base*/) const +{ // TODO: fix base //SPILength const *const my_base = dynamic_cast<const SPILength*>(base); if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set)) + ((flags & SP_STYLE_FLAG_IFSET) && this->set) || + ((flags & SP_STYLE_FLAG_IFDIFF) && this->set)) { if (this->inherit) { return (name + ":inherit;"); @@ -1440,7 +1440,7 @@ SPIFilter::cascade( const SPIBase* const parent ) { void SPIFilter::merge( const SPIBase* const parent ) { if( const SPIFilter* p = dynamic_cast<const SPIFilter*>(parent) ) { - // The "correct" thing to due is to combine the filter primitives. + // The "correct" thing to do is to combine the filter primitives. // The next best thing is to keep any filter on this object. If there // is no filter on this object, then use any filter on the parent. if( (!set || inherit) && p->href && p->href->getObject() ) { // is the getObject() needed? @@ -1453,9 +1453,11 @@ SPIFilter::merge( const SPIBase* const parent ) { } } else { // If we don't have an href, create it - if( &style->document ) { // FIXME + if( style->document ) { // FIXME href = new SPFilterReference(style->document); //href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + } else if (style->object) { + href = new SPFilterReference(style->object); } } if( href ) { @@ -1921,9 +1923,8 @@ SPIFont::read( gchar const *str ) { } } -const Glib::ustring -SPIFont::write( guint const flags, SPIBase const *const base) const { - +const Glib::ustring SPIFont::write( guint const /*flags*/, SPIBase const *const /*base*/) const +{ // At the moment, do nothing. We could add a preference to write out // 'font' shorthand rather than longhand properties. diff --git a/src/style-internal.h b/src/style-internal.h index e76f9faaf..b549bb8ef 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -340,7 +340,7 @@ class SPIEnum : public SPIBase { public: SPIEnum() : SPIBase( "anonymous_enum" ), enums( NULL ), value(0), computed(0) {}; - SPIEnum( Glib::ustring name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : + SPIEnum( Glib::ustring const name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : SPIBase( name, inherits ), enums( enums ), value(value), computed(value), value_default(value), computed_default(value) {}; // Following is needed for font-weight diff --git a/src/style.cpp b/src/style.cpp index 3691a3e48..66672e949 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -121,10 +121,6 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // Text related properties text_indent( "text-indent", 0.0 ), // SPILength text_align( "text-align", enum_text_align, SP_CSS_TEXT_ALIGN_START ), - text_decoration(), - text_decoration_line(), - text_decoration_style(), - text_decoration_color( "text-decoration-color" ), // SPIColor letter_spacing( "letter-spacing", 0.0 ), // SPILengthOrNormal word_spacing( "word-spacing", 0.0 ), // SPILengthOrNormal @@ -135,6 +131,12 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : writing_mode( "writing-mode", enum_writing_mode, SP_CSS_WRITING_MODE_LR_TB ), baseline_shift(), text_anchor( "text-anchor", enum_text_anchor, SP_CSS_TEXT_ANCHOR_START ), + white_space( "white-space", enum_white_space, SP_CSS_WHITE_SPACE_NORMAL ), + + text_decoration(), + text_decoration_line(), + text_decoration_style(), + text_decoration_color( "text-decoration-color" ), // SPIColor // General visual properties clip_rule( "clip-rule", enum_clip_rule, SP_WIND_RULE_NONZERO ), @@ -144,7 +146,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : opacity( "opacity", SP_SCALE24_MAX, false ), isolation( "isolation", enum_isolation, SP_CSS_ISOLATION_AUTO ), - blend_mode( "blend_mode", enum_blend_mode, SP_CSS_BLEND_NORMAL ), + mix_blend_mode( "mix-blend-mode", enum_blend_mode, SP_CSS_BLEND_NORMAL ), paint_order(), // SPIPaintOrder @@ -189,7 +191,6 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : image_rendering( "image-rendering", enum_image_rendering, SP_CSS_IMAGE_RENDERING_AUTO), shape_rendering( "shape-rendering", enum_shape_rendering, SP_CSS_SHAPE_RENDERING_AUTO), text_rendering( "text-rendering", enum_text_rendering, SP_CSS_TEXT_RENDERING_AUTO ) - { // std::cout << "SPStyle::SPStyle( SPDocument ): Entrance: (" << _count << ")" << std::endl; // std::cout << " Document: " << (document_in?"present":"null") << std::endl; @@ -231,11 +232,6 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : document = document_in; } - new (&release_connection) sigc::connection(); - new (&filter_modified_connection) sigc::connection(); - new (&fill_ps_modified_connection) sigc::connection(); - new (&stroke_ps_modified_connection) sigc::connection(); - // 'font' shorthand requires access to included properties. font.setStylePointer( this ); @@ -302,6 +298,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : _properties.push_back( &writing_mode ); _properties.push_back( &baseline_shift ); _properties.push_back( &text_anchor ); + _properties.push_back( &white_space ); _properties.push_back( &clip_rule ); _properties.push_back( &display ); @@ -310,7 +307,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : _properties.push_back( &opacity ); _properties.push_back( &isolation ); - _properties.push_back( &blend_mode ); + _properties.push_back( &mix_blend_mode ); _properties.push_back( &color_interpolation ); _properties.push_back( &color_interpolation_filters ); @@ -384,6 +381,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // _propmap.insert( std::make_pair( writing_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::writing_mode ) ) ); // _propmap.insert( std::make_pair( baseline_shift.name, reinterpret_cast<SPIBasePtr>(&SPStyle::baseline_shift ) ) ); // _propmap.insert( std::make_pair( text_anchor.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_anchor ) ) ); + // _propmap.insert( std::make_pair( white_space.name, reinterpret_cast<SPIBasePtr>(&SPStyle::white_space ) ) ); // _propmap.insert( std::make_pair( clip_rule.name, reinterpret_cast<SPIBasePtr>(&SPStyle::clip_rule ) ) ); // _propmap.insert( std::make_pair( display.name, reinterpret_cast<SPIBasePtr>(&SPStyle::display ) ) ); @@ -392,7 +390,7 @@ SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : // _propmap.insert( std::make_pair( opacity.name, reinterpret_cast<SPIBasePtr>(&SPStyle::opacity ) ) ); // _propmap.insert( std::make_pair( isolation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::isolation ) ) ); - // _propmap.insert( std::make_pair( blend_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::blend_mode ) ) ); + // _propmap.insert( std::make_pair( mix_blend_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::mix_blend_mode ) ) ); // _propmap.insert( std::make_pair( color_interpolation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::color_interpolation ) ) ); // _propmap.insert( std::make_pair( color_interpolation_filters.name, reinterpret_cast<SPIBasePtr>(&SPStyle::color_interpolation_filters ) ) ); @@ -442,7 +440,6 @@ SPStyle::~SPStyle() { // Remove connections release_connection.disconnect(); - release_connection.~connection(); // The following shoud be moved into SPIPaint and SPIFilter if (fill.value.href) { @@ -457,12 +454,7 @@ SPStyle::~SPStyle() { filter_modified_connection.disconnect(); } - filter_modified_connection.~connection(); - fill_ps_modified_connection.~connection(); - stroke_ps_modified_connection.~connection(); - _properties.clear(); - //_propmap.clear(); // std::cout << "SPStyle::~SPstyle(): Exit\n" << std::endl; } @@ -681,6 +673,9 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { case SP_PROP_TEXT_ANCHOR: text_anchor.readIfUnset( val ); break; + case SP_PROP_WHITE_SPACE: + white_space.readIfUnset( val ); + break; case SP_PROP_BASELINE_SHIFT: baseline_shift.readIfUnset( val ); break; @@ -724,8 +719,8 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { case SP_PROP_ISOLATION: isolation.readIfUnset( val ); break; - case SP_PROP_BLEND_MODE: - blend_mode.readIfUnset( val ); + case SP_PROP_MIX_BLEND_MODE: + mix_blend_mode.readIfUnset( val ); break; /* SVG */ @@ -1634,6 +1629,9 @@ sp_style_unset_property_attrs(SPObject *o) if (style->text_anchor.set) { repr->setAttribute("text-anchor", NULL); } + if (style->white_space.set) { + repr->setAttribute("white_space", NULL); + } if (style->writing_mode.set) { repr->setAttribute("writing_mode", NULL); } @@ -1723,6 +1721,7 @@ sp_css_attr_unset_text(SPCSSAttr *css) sp_repr_css_set_property(css, "block-progression", NULL); sp_repr_css_set_property(css, "writing-mode", NULL); sp_repr_css_set_property(css, "text-anchor", NULL); + sp_repr_css_set_property(css, "white_space", NULL); sp_repr_css_set_property(css, "kerning", NULL); // not implemented yet sp_repr_css_set_property(css, "dominant-baseline", NULL); // not implemented yet sp_repr_css_set_property(css, "alignment-baseline", NULL); // not implemented yet diff --git a/src/style.h b/src/style.h index eb8a3ed91..01a9d4b84 100644 --- a/src/style.h +++ b/src/style.h @@ -92,8 +92,10 @@ private: public: /* ----------------------- THE PROPERTIES ------------------------- */ + /* Match order in style.cpp. */ + + /* Font ---------------------------- */ - /* Font */ /** Font style */ SPIEnum font_style; /** Which substyle of the font */ @@ -113,23 +115,13 @@ public: /** Full font name, as font_factory::ConstructFontSpecification would give, for internal use. */ SPIString font_specification; + /* Text ----------------------------- */ + /** First line indent of paragraphs (css2 16.1) */ SPILength text_indent; /** text alignment (css2 16.2) (not to be confused with text-anchor) */ SPIEnum text_align; - /** text decoration (css2 16.3.1) */ - SPITextDecoration text_decoration; - /** CSS 3 2.1, 2.2, 2.3 */ - /** Not done yet, test_decoration3 = css3 2.4*/ - SPITextDecorationLine text_decoration_line; - SPITextDecorationStyle text_decoration_style; // SPIEnum? Only one can be set at time. - SPIColor text_decoration_color; - // used to implement text_decoration, not saved to or read from SVG file - SPITextDecorationData text_decoration_data; - - // 16.3.2 is text-shadow. That's complicated. - /** letter spacing (css2 16.4) */ SPILengthOrNormal letter_spacing; /** word spacing (also css2 16.4) */ @@ -151,6 +143,25 @@ public: /** Anchor of the text (svg1.1 10.9.1) */ SPIEnum text_anchor; + /** white space (svg2) */ + SPIEnum white_space; + + /* Text Decoration ----------------------- */ + + /** text decoration (css2 16.3.1) */ + SPITextDecoration text_decoration; + /** CSS 3 2.1, 2.2, 2.3 */ + /** Not done yet, test_decoration3 = css3 2.4*/ + SPITextDecorationLine text_decoration_line; + SPITextDecorationStyle text_decoration_style; // SPIEnum? Only one can be set at time. + SPIColor text_decoration_color; + // used to implement text_decoration, not saved to or read from SVG file + SPITextDecorationData text_decoration_data; + + // 16.3.2 is text-shadow. That's complicated. + + /* General visual properties ------------- */ + /** clip-rule: 0 nonzero, 1 evenodd */ SPIEnum clip_rule; @@ -168,8 +179,7 @@ public: /** mix-blend-mode: CSS Compositing and Blending Level 1 */ SPIEnum isolation; - // Could be shared with Filter blending mode - SPIEnum blend_mode; + SPIEnum mix_blend_mode; SPIPaintOrder paint_order; @@ -216,6 +226,8 @@ public: SPIString marker_end; SPIString* marker_ptrs[SP_MARKER_LOC_QTY]; + /* Filter effects ------------------------ */ + /** Filter effect */ SPIFilter filter; /** Filter blend mode */ @@ -226,6 +238,7 @@ public: /** enable-background, used for defining where filter effects get their background image */ SPIEnum enable_background; + /* Rendering hints ----------------------- */ /** hints on how to render: e.g. speed vs. accuracy. * As of April, 2013, only image_rendering used. */ diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index 8e2502545..1209b19cd 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -601,6 +601,12 @@ Glib::ustring ClipboardManagerImpl::getPathParameter(SPDesktop* desktop) */ Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop) { + // https://bugs.launchpad.net/inkscape/+bug/1293979 + // basically, when we do a depth-first search, we're stopping + // at the first object to be <svg:path> or <svg:text>. + // but that could then return the id of the object's + // clip path or mask, not the original path! + SPDocument *tempdoc = _retrieveClipboard(); // any target will do here if ( tempdoc == NULL ) { _userWarn(desktop, _("Nothing on the clipboard.")); @@ -608,6 +614,9 @@ Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop) } Inkscape::XML::Node *root = tempdoc->getReprRoot(); + // 1293979: strip out the defs of the document + root->removeChild(tempdoc->getDefs()->getRepr()); + Inkscape::XML::Node *repr = sp_repr_lookup_name(root, "svg:path", -1); // unlimited search depth if ( repr == NULL ) { repr = sp_repr_lookup_name(root, "svg:text", -1); diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index a435c5583..dd6be2604 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -2242,6 +2242,8 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) clonetiler_remove (NULL, dlg, false); + double scale_units = Inkscape::Util::Quantity::convert(1, "px", sp_desktop_document(desktop)->getDefaultUnit()); + double shiftx_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_i", 0, -10000, 10000); double shifty_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_per_i", 0, -10000, 10000); double shiftx_per_j = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_j", 0, -10000, 10000); @@ -2311,8 +2313,8 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) int jmax = prefs->getInt(prefs_path + "jmax", 2); bool fillrect = prefs->getBool(prefs_path + "fillrect"); - double fillwidth = prefs->getDoubleLimited(prefs_path + "fillwidth", 50, 0, 1e6); - double fillheight = prefs->getDoubleLimited(prefs_path + "fillheight", 50, 0, 1e6); + double fillwidth = scale_units*prefs->getDoubleLimited(prefs_path + "fillwidth", 50, 0, 1e6); + double fillheight = scale_units*prefs->getDoubleLimited(prefs_path + "fillheight", 50, 0, 1e6); bool dotrace = prefs->getBool(prefs_path + "dotrace"); int pick = prefs->getInt(prefs_path + "pick"); @@ -2358,11 +2360,11 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX ); Geom::OptRect r = item->documentBounds(bbox_type); if (r) { - w = r->dimensions()[Geom::X]; - h = r->dimensions()[Geom::Y]; - x0 = r->min()[Geom::X]; - y0 = r->min()[Geom::Y]; - center = desktop->dt2doc(item->getCenter()); + w = scale_units*r->dimensions()[Geom::X]; + h = scale_units*r->dimensions()[Geom::Y]; + x0 = scale_units*r->min()[Geom::X]; + y0 = scale_units*r->min()[Geom::Y]; + center = scale_units*desktop->dt2doc(item->getCenter()); sp_repr_set_svg_double(obj_repr, "inkscape:tile-cx", center[Geom::X]); sp_repr_set_svg_double(obj_repr, "inkscape:tile-cy", center[Geom::Y]); @@ -2578,7 +2580,7 @@ void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) Geom::Point new_center; bool center_set = false; if (obj_repr->attribute("inkscape:transform-center-x") || obj_repr->attribute("inkscape:transform-center-y")) { - new_center = desktop->dt2doc(item->getCenter()) * t; + new_center = scale_units*desktop->dt2doc(item->getCenter()) * t; center_set = true; } diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index d427e3590..7e69e439a 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -251,7 +251,7 @@ void DialogManager::showDialog(gchar const *name, bool grabfocus) { /** * Shows the named dialog, creating it if necessary. */ -void DialogManager::showDialog(GQuark name, bool grabfocus) { +void DialogManager::showDialog(GQuark name, bool /*grabfocus*/) { bool wantTiming = Inkscape::Preferences::get()->getBool("/dialogs/debug/trackAppear", false); GTimer *timer = (wantTiming) ? g_timer_new() : 0; // if needed, must be created/started before getDialog() Dialog *dialog = getDialog(name); @@ -262,8 +262,8 @@ void DialogManager::showDialog(GQuark name, bool grabfocus) { tracker->setAutodelete(true); timer = 0; } - if (grabfocus) - dialog->present(); + // should check for grabfocus, but lp:1348927 prevents it + dialog->present(); } if ( timer ) { diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index ce3b1314c..00b54017c 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -596,6 +596,7 @@ void DocumentProperties::removeSelectedProfile(){ //XML Tree being used directly here while it shouldn't be. sp_repr_unparent(obj->getRepr()); DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_EDIT_REMOVE_COLOR_PROFILE, _("Remove linked color profile")); + break; // removing the color profile likely invalidates part of the traversed list, stop traversing here. } current = g_slist_next(current); } diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp index b5dac0595..65351cb68 100644 --- a/src/ui/dialog/layers.cpp +++ b/src/ui/dialog/layers.cpp @@ -926,9 +926,8 @@ LayersPanel::LayersPanel() : // ------------------------------------------------------- { - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, 0, "Rename", (int)BUTTON_RENAME ) ); - _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_DUPLICATE, 0, "Duplicate", (int)BUTTON_DUPLICATE ) ); _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_NEW, 0, "New", (int)BUTTON_NEW ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, 0, "Rename", (int)BUTTON_RENAME ) ); _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); @@ -944,9 +943,14 @@ LayersPanel::LayersPanel() : _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); - _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RAISE, INKSCAPE_ICON("go-up"), "Up", (int)BUTTON_UP ) ); - _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOWER, INKSCAPE_ICON("go-down"), "Down", (int)BUTTON_DOWN ) ); + _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RAISE, INKSCAPE_ICON("layer-raise"), "Up", (int)BUTTON_UP ) ); + _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOWER, INKSCAPE_ICON("layer-lower"), "Down", (int)BUTTON_DOWN ) ); + _popupMenu.append(*Gtk::manage(new Gtk::SeparatorMenuItem())); + + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_DUPLICATE, 0, "Duplicate", (int)BUTTON_DUPLICATE ) ); + _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_DELETE, 0, "Delete", (int)BUTTON_DELETE ) ); + _popupMenu.show_all_children(); } // ------------------------------------------------------- diff --git a/src/ui/dialog/template-widget.cpp b/src/ui/dialog/template-widget.cpp index ef91962d4..9758b35ac 100644 --- a/src/ui/dialog/template-widget.cpp +++ b/src/ui/dialog/template-widget.cpp @@ -120,7 +120,7 @@ void TemplateWidget::_displayTemplateDetails() if (_current_template.long_description != "") message += _("Description: ") + _current_template.long_description + "\n\n"; - if (~_current_template.keywords.empty()){ + if (!_current_template.keywords.empty()){ message += _("Keywords: "); for (std::set<Glib::ustring>::iterator it = _current_template.keywords.begin(); it != _current_template.keywords.end(); ++it) message += *it + " "; diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index 01cff5ad7..9839be437 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -1219,6 +1219,7 @@ double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ h = h2; } double pos = 0.0000; + const double handleCubicGap = 0.01; Node *n = h->parent(); Node * nextNode = NULL; nextNode = n->nodeToward(h); @@ -1226,7 +1227,7 @@ double PathManipulator::BSplineHandlePosition(Handle *h, Handle *h2){ SPCurve *lineInsideNodes = new SPCurve(); lineInsideNodes->moveto(n->position()); lineInsideNodes->lineto(nextNode->position()); - pos = Geom::nearest_point(h->position(),*lineInsideNodes->first_segment()); + pos = Geom::nearest_point(Geom::Point(h->position()[X] - handleCubicGap,h->position()[Y] - handleCubicGap),*lineInsideNodes->first_segment()); } if (pos == 0.0000 && !h2){ return BSplineHandlePosition(h, h->other()); @@ -1244,6 +1245,7 @@ Geom::Point PathManipulator::BSplineHandleReposition(Handle *h, Handle *h2){ Geom::Point PathManipulator::BSplineHandleReposition(Handle *h,double pos){ using Geom::X; using Geom::Y; + const double handleCubicGap = 0.01; Geom::Point ret = h->position(); Node *n = h->parent(); Geom::D2< Geom::SBasis > SBasisInsideNodes; @@ -1255,7 +1257,7 @@ Geom::Point PathManipulator::BSplineHandleReposition(Handle *h,double pos){ lineInsideNodes->lineto(nextNode->position()); SBasisInsideNodes = lineInsideNodes->first_segment()->toSBasis(); ret = SBasisInsideNodes.valueAt(pos); - ret = Geom::Point(ret[X] + 0.005,ret[Y] + 0.005); + ret = Geom::Point(ret[X] + handleCubicGap,ret[Y] + handleCubicGap); }else{ if(pos == 0.0000){ ret = n->position(); diff --git a/src/ui/tools/arc-tool.cpp b/src/ui/tools/arc-tool.cpp index 43f8eb9e1..9fd68f1b9 100644 --- a/src/ui/tools/arc-tool.cpp +++ b/src/ui/tools/arc-tool.cpp @@ -102,8 +102,8 @@ ArcTool::~ArcTool() { * destroys old and creates new knotholder. */ void ArcTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void ArcTool::setup() { @@ -115,7 +115,7 @@ void ArcTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/box3d-tool.cpp b/src/ui/tools/box3d-tool.cpp index e00894d55..b998267c2 100644 --- a/src/ui/tools/box3d-tool.cpp +++ b/src/ui/tools/box3d-tool.cpp @@ -112,8 +112,8 @@ Box3dTool::~Box3dTool() { * destroys old and creates new knotholder. */ void Box3dTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); if (selection->perspList().size() == 1) { // selecting a single box changes the current perspective @@ -147,7 +147,7 @@ void Box3dTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp index d74848dc6..3fb56b2ad 100644 --- a/src/ui/tools/flood-tool.cpp +++ b/src/ui/tools/flood-tool.cpp @@ -119,8 +119,8 @@ FloodTool::~FloodTool() { * destroys old and creates new knotholder. */ void FloodTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void FloodTool::setup() { @@ -130,7 +130,7 @@ void FloodTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/freehand-base.cpp b/src/ui/tools/freehand-base.cpp index 1c5b7b8e3..aad924844 100644 --- a/src/ui/tools/freehand-base.cpp +++ b/src/ui/tools/freehand-base.cpp @@ -44,6 +44,8 @@ #include "live_effects/lpe-powerstroke.h" #include "style.h" #include "ui/control-manager.h" +// clipboard support +#include "ui/clipboard.h" #include "ui/tools/freehand-base.h" #include <gdk/gdkkeysyms.h> @@ -238,6 +240,31 @@ static void spdc_apply_powerstroke_shape(const std::vector<Geom::Point> & points Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); static_cast<LPEPowerStroke*>(lpe)->offset_points.param_set_and_write_new_value(points); + // find out stroke width (TODO: is there an easier way??) + SPDesktop *desktop = dc->desktop; + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:path"); + Inkscape::GC::release(repr); + + char const* tool = SP_IS_PEN_CONTEXT(dc) ? "/tools/freehand/pen" : "/tools/freehand/pencil"; + + // apply the tool's current style + sp_desktop_apply_style_tool(desktop, repr, tool, false); + + double stroke_width = 1.0; + char const *style_str = NULL; + style_str = repr->attribute("style"); + if (style_str) { + SPStyle *style = sp_style_new(SP_ACTIVE_DOCUMENT); + sp_style_merge_from_style_string(style, style_str); + stroke_width = style->stroke_width.computed; + style->stroke_width.computed = 0; + sp_style_unref(style); + } + + char * width_str = new char[50]; + sprintf(width_str, "0,%f", stroke_width / 2.); + // write powerstroke parameters: lpe->getRepr()->setAttribute("start_linecap_type", "zerowidth"); lpe->getRepr()->setAttribute("end_linecap_type", "zerowidth"); @@ -245,6 +272,9 @@ static void spdc_apply_powerstroke_shape(const std::vector<Geom::Point> & points lpe->getRepr()->setAttribute("sort_points", "true"); lpe->getRepr()->setAttribute("interpolator_type", "CubicBezierJohan"); lpe->getRepr()->setAttribute("interpolator_beta", "0.2"); + lpe->getRepr()->setAttribute("offset_points", width_str); + + delete [] width_str; } static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, SPCurve *curve) @@ -261,7 +291,13 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, Effect::createAndApply(BSPLINE, dc->desktop->doc(), item); } - int shape = prefs->getInt(tool_name(dc) + "/shape", 0); + //Store the clipboard path to apply in the future without the use of clipboard + static Geom::PathVector previous_shape_pathv; + enum shapeType { NONE, TRIANGLE_IN, TRIANGLE_OUT, ELLIPSE, CLIPBOARD, LAST_APPLIED }; + static shapeType previous_shape_type = NONE; + + + shapeType shape = (shapeType)prefs->getInt(tool_name(dc) + "/shape", 0); bool shape_applied = false; SPCSSAttr *css_item = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); const char *cstroke = sp_repr_css_property(css_item, "stroke", "none"); @@ -269,11 +305,18 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, #define SHAPE_LENGTH 10 #define SHAPE_HEIGHT 10 + if(shape == LAST_APPLIED){ + shape = previous_shape_type; + if(shape == CLIPBOARD){ + shape = LAST_APPLIED; + } + } + switch (shape) { - case 0: + case NONE: // don't apply any shape break; - case 1: + case TRIANGLE_IN: { // "triangle in" std::vector<Geom::Point> points(1); @@ -283,7 +326,7 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, shape_applied = true; break; } - case 2: + case TRIANGLE_OUT: { // "triangle out" guint curve_length = curve->get_segment_count(); @@ -294,7 +337,7 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, shape_applied = true; break; } - case 3: + case ELLIPSE: { // "ellipse" SPCurve *c = new SPCurve(); @@ -307,22 +350,40 @@ static void spdc_check_for_and_apply_waiting_LPE(FreehandBase *dc, SPItem *item, c->closepath(); spdc_paste_curve_as_freehand_shape(c, dc, item); c->unref(); + shape_applied = true; break; } - case 4: + case CLIPBOARD: { // take shape from clipboard; TODO: catch the case where clipboard is empty Effect::createAndApply(PATTERN_ALONG_PATH, dc->desktop->doc(), item); Effect* lpe = SP_LPE_ITEM(item)->getCurrentLPE(); static_cast<LPEPatternAlongPath*>(lpe)->pattern.on_paste_button_click(); + Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); + Glib::ustring svgd = cm->getPathParameter(SP_ACTIVE_DESKTOP); + previous_shape_pathv = sp_svg_read_pathv(svgd.data()); shape_applied = true; break; } + case LAST_APPLIED: + { + if(previous_shape_pathv.size() != 0){ + SPCurve * c = new SPCurve(); + c->set_pathvector(previous_shape_pathv); + spdc_paste_curve_as_freehand_shape(c, dc, item); + c->unref(); + + shape_applied = true; + } + break; + } default: break; } + previous_shape_type = shape; + if (shape_applied) { // apply original stroke color as fill and unset stroke; then return SPCSSAttr *css = sp_repr_css_attr_new(); diff --git a/src/ui/tools/lpe-tool.cpp b/src/ui/tools/lpe-tool.cpp index 9ab6d7814..e9b9421f1 100644 --- a/src/ui/tools/lpe-tool.cpp +++ b/src/ui/tools/lpe-tool.cpp @@ -129,8 +129,7 @@ void LpeTool::setup() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (item) { - this->shape_editor->set_item(item, SH_NODEPATH); - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } if (prefs->getBool("/tools/lpetool/selcue")) { @@ -146,9 +145,9 @@ void sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpoint { LpeTool *lc = SP_LPETOOL_CONTEXT(data); - lc->shape_editor->unset_item(SH_KNOTHOLDER); + lc->shape_editor->unset_item(); SPItem *item = selection->singleItem(); - lc->shape_editor->set_item(item, SH_KNOTHOLDER); + lc->shape_editor->set_item(item); } void LpeTool::set(const Inkscape::Preferences::Entry& val) { diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index ce487831d..d2584ee74 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -19,6 +19,7 @@ #include "display/curve.h" #include "display/sp-canvas.h" #include "document.h" +#include "live_effects/effect.h" #include "live_effects/lpeobject.h" #include "message-context.h" #include "selection.h" @@ -168,6 +169,9 @@ NodeTool::~NodeTool() { if (this->flash_tempitem) { this->desktop->remove_temporary_canvasitem(this->flash_tempitem); } + if (this->helperpath_tmpitem) { + this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); + } if (this->helperpath_tmpitem) { this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); @@ -252,6 +256,7 @@ void NodeTool::setup() { ))) ); + this->helperpath_tmpitem = NULL; this->cursor_drag = false; this->show_transform_handles = true; this->single_node_transform_handles = false; @@ -288,12 +293,15 @@ void NodeTool::setup() { this->update_helperpath(); } -void NodeTool::update_helperpath(){ +// show helper paths of the applied LPE, if any +void NodeTool::update_helperpath () { Inkscape::Selection *selection = sp_desktop_selection (this->desktop); + if (this->helperpath_tmpitem) { this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); this->helperpath_tmpitem = NULL; } + if (SP_IS_LPE_ITEM(selection->singleItem())) { Inkscape::LivePathEffect::Effect *lpe = SP_LPE_ITEM(selection->singleItem())->getCurrentLPE(); if (lpe && lpe->isVisible()/* && lpe->showOrigPath()*/) { @@ -310,7 +318,7 @@ void NodeTool::update_helperpath(){ c->transform(selection->singleItem()->i2dt_affine()); SPCanvasItem *helperpath = sp_canvas_bpath_new(sp_desktop_tempgroup(this->desktop), c); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helperpath), - 0x0000ff9A, 1.0, + 0x0000ff9A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helperpath), 0, SP_WIND_RULE_NONZERO); this->helperpath_tmpitem = this->desktop->add_temporary_canvasitem(helperpath,0); @@ -438,7 +446,7 @@ void NodeTool::selection_changed(Inkscape::Selection *sel) { this->_shape_editors.find(r.item) == this->_shape_editors.end()) { ShapeEditor *si = new ShapeEditor(this->desktop); - si->set_item(r.item, SH_KNOTHOLDER); + si->set_item(r.item); this->_shape_editors.insert(const_cast<SPItem*&>(r.item), si); } } @@ -474,6 +482,7 @@ bool NodeTool::root_handler(GdkEvent* event) { switch (event->type) { case GDK_MOTION_NOTIFY: { + this->update_helperpath(); combine_motion_events(desktop->canvas, event->motion, 0); this->update_helperpath(); SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h index 9f0c40aa8..ab72f3632 100644 --- a/src/ui/tools/node-tool.h +++ b/src/ui/tools/node-tool.h @@ -66,8 +66,8 @@ private: sigc::connection _sizeUpdatedConn; SPItem *flashed_item; - Inkscape::Display::TemporaryItem *flash_tempitem; Inkscape::Display::TemporaryItem *helperpath_tmpitem; + Inkscape::Display::TemporaryItem *flash_tempitem; Inkscape::UI::Selector* _selector; Inkscape::UI::PathSharedData* _path_data; SPCanvasGroup *_transform_handle_group; diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp index 56bcbef0d..9a73d497f 100644 --- a/src/ui/tools/pen-tool.cpp +++ b/src/ui/tools/pen-tool.cpp @@ -84,17 +84,17 @@ namespace Tools { static Geom::Point pen_drag_origin_w(0, 0); static bool pen_within_tolerance = false; static int pen_last_paraxial_dir = 0; // last used direction in horizontal/vertical mode; 0 = horizontal, 1 = vertical - +const double handleCubicGap = 0.01; namespace { - ToolBase* createPenContext() { - return new PenTool(); - } + ToolBase* createPenContext() { + return new PenTool(); + } - bool penContextRegistered = ToolFactory::instance().registerObject("/tools/freehand/pen", createPenContext); + bool penContextRegistered = ToolFactory::instance().registerObject("/tools/freehand/pen", createPenContext); } const std::string& PenTool::getPrefsPath() { - return PenTool::prefsPath; + return PenTool::prefsPath; } const std::string PenTool::prefsPath = "/tools/freehand/pen"; @@ -321,7 +321,7 @@ bool PenTool::item_handler(SPItem* item, GdkEvent* event) { } if (!ret) { - ret = FreehandBase::item_handler(item, event); + ret = FreehandBase::item_handler(item, event); } return ret; @@ -359,7 +359,7 @@ bool PenTool::root_handler(GdkEvent* event) { } if (!ret) { - ret = FreehandBase::root_handler(event); + ret = FreehandBase::root_handler(event); } return ret; @@ -1268,15 +1268,15 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { } // asign the value in a third of the distance of the last segment. - if(this->bspline){ + if (this->bspline){ this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]); } Geom::Point const pt( (this->npoints < 4) ? crv->finalPoint() : this->p[3] ); - + this->npoints = 2; // delete the last segment of the green curve - if( this->green_curve->get_segment_count() == 1){ + if (this->green_curve->get_segment_count() == 1) { this->npoints = 5; if (this->green_bpaths) { if (this->green_bpaths->data) { @@ -1285,11 +1285,12 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { this->green_bpaths = g_slist_remove(this->green_bpaths, this->green_bpaths->data); } this->green_curve->reset(); - }else{ + } else { this->green_curve->backspace(); } + // assign the value of this->p[1] to the oposite of the green line last segment - if(this->spiro){ + if (this->spiro){ Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(this->green_curve->last_segment()); if ( cubic ) { this->p[1] = (*cubic)[3] + (*cubic)[3] - (*cubic)[2]; @@ -1298,6 +1299,7 @@ bool PenTool::_handleKeyPress(GdkEvent *event) { this->p[1] = this->p[0]; } } + sp_canvas_item_hide(this->c0); sp_canvas_item_hide(this->c1); sp_canvas_item_hide(this->cl0); @@ -1452,7 +1454,7 @@ void PenTool::_bspline_spiro_on() this->p[0] = this->red_curve->first_segment()->initialPoint(); this->p[3] = this->red_curve->first_segment()->finalPoint(); this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); - this->p[2] = Geom::Point(this->p[2][X] + 0.005,this->p[2][Y] + 0.005); + this->p[2] = Geom::Point(this->p[2][X] + handleCubicGap,this->p[2][Y] + handleCubicGap); } } @@ -1520,7 +1522,7 @@ void PenTool::_bspline_spiro_start_anchor_on() Geom::Point A = tmpCurve->last_segment()->initialPoint(); Geom::Point D = tmpCurve->last_segment()->finalPoint(); Geom::Point C = D + (1./3)*(A - D); - C = Geom::Point(C[X] + 0.005,C[Y] + 0.005); + C = Geom::Point(C[X] + handleCubicGap,C[Y] + handleCubicGap); if(cubic){ lastSeg->moveto(A); lastSeg->curveto((*cubic)[1],C,D); @@ -1578,10 +1580,10 @@ void PenTool::_bspline_spiro_motion(bool shift){ this->npoints = 5; SPCurve *tmpCurve = new SPCurve(); this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); - this->p[2] = Geom::Point(this->p[2][X] + 0.005,this->p[2][Y] + 0.005); + this->p[2] = Geom::Point(this->p[2][X] + handleCubicGap,this->p[2][Y] + handleCubicGap); if(this->green_curve->is_empty() && !this->sa){ this->p[1] = this->p[0] + (1./3)*(this->p[3] - this->p[0]); - this->p[1] = Geom::Point(this->p[1][X] + 0.005,this->p[1][Y] + 0.005); + this->p[1] = Geom::Point(this->p[1][X] + handleCubicGap,this->p[1][Y] + handleCubicGap); }else if(!this->green_curve->is_empty()){ tmpCurve = this->green_curve->copy(); }else{ @@ -1606,7 +1608,7 @@ void PenTool::_bspline_spiro_motion(bool shift){ WPower->reset(); this->p[1] = SBasisWPower.valueAt(WP); if(!Geom::are_near(this->p[1],this->p[0])) - this->p[1] = Geom::Point(this->p[1][X] + 0.005,this->p[1][Y] + 0.005); + this->p[1] = Geom::Point(this->p[1][X] + handleCubicGap,this->p[1][Y] + handleCubicGap); if(shift) this->p[2] = this->p[3]; }else{ @@ -1636,7 +1638,7 @@ void PenTool::_bspline_spiro_end_anchor_on() using Geom::X; using Geom::Y; this->p[2] = this->p[3] + (1./3)*(this->p[0] - this->p[3]); - this->p[2] = Geom::Point(this->p[2][X] + 0.005,this->p[2][Y] + 0.005); + this->p[2] = Geom::Point(this->p[2][X] + handleCubicGap,this->p[2][Y] + handleCubicGap); SPCurve *tmpCurve = new SPCurve(); SPCurve *lastSeg = new SPCurve(); Geom::Point C(0,0); @@ -1659,7 +1661,7 @@ void PenTool::_bspline_spiro_end_anchor_on() Geom::CubicBezier const * cubic = dynamic_cast<Geom::CubicBezier const*>(&*tmpCurve->last_segment()); if(this->bspline){ C = tmpCurve->last_segment()->finalPoint() + (1./3)*(tmpCurve->last_segment()->initialPoint() - tmpCurve->last_segment()->finalPoint()); - C = Geom::Point(C[X] + 0.005,C[Y] + 0.005); + C = Geom::Point(C[X] + handleCubicGap,C[Y] + handleCubicGap); }else{ C = this->p[3] + this->p[3] - this->p[2]; } @@ -2084,7 +2086,7 @@ void PenTool::_setSubsequentPoint(Geom::Point const p, bool statusbar, guint sta // we are drawing horizontal/vertical lines and hit an anchor; Geom::Point const origin = this->p[0]; // if the previous point and the anchor are not aligned either horizontally or vertically... - if ((abs(p[Geom::X] - origin[Geom::X]) > 1e-9) && (abs(p[Geom::Y] - origin[Geom::Y]) > 1e-9)) { + if ((std::abs(p[Geom::X] - origin[Geom::X]) > 1e-9) && (std::abs(p[Geom::Y] - origin[Geom::Y]) > 1e-9)) { // ...then we should draw an L-shaped path, consisting of two paraxial segments Geom::Point intermed = p; this->_setToNearestHorizVert(intermed, status, false); diff --git a/src/ui/tools/rect-tool.cpp b/src/ui/tools/rect-tool.cpp index 39f422c1a..819671dd6 100644 --- a/src/ui/tools/rect-tool.cpp +++ b/src/ui/tools/rect-tool.cpp @@ -102,8 +102,8 @@ RectTool::~RectTool() { * destroys old and creates new knotholder. */ void RectTool::selection_changed(Inkscape::Selection* selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void RectTool::setup() { @@ -113,7 +113,7 @@ void RectTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection.disconnect(); diff --git a/src/ui/tools/spiral-tool.cpp b/src/ui/tools/spiral-tool.cpp index 5ae229df8..83712457a 100644 --- a/src/ui/tools/spiral-tool.cpp +++ b/src/ui/tools/spiral-tool.cpp @@ -104,8 +104,8 @@ SpiralTool::~SpiralTool() { * destroys old and creates new knotholder. */ void SpiralTool::selection_changed(Inkscape::Selection *selection) { - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void SpiralTool::setup() { @@ -119,7 +119,7 @@ void SpiralTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } Inkscape::Selection *selection = sp_desktop_selection(this->desktop); diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index 08d3119a1..29f1b9a73 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -677,7 +677,7 @@ bool SprayTool::root_handler(GdkEvent* event) { desktop->setToolboxAdjustmentValue("population", this->population * 100); Geom::Point const scroll_w(event->button.x, event->button.y); Geom::Point const scroll_dt = desktop->point();; - Geom::Point motion_doc(desktop->dt2doc(scroll_dt)); + switch (event->scroll.direction) { case GDK_SCROLL_DOWN: case GDK_SCROLL_UP: { diff --git a/src/ui/tools/star-tool.cpp b/src/ui/tools/star-tool.cpp index 68f998920..ed28c0a8d 100644 --- a/src/ui/tools/star-tool.cpp +++ b/src/ui/tools/star-tool.cpp @@ -112,8 +112,8 @@ StarTool::~StarTool() { void StarTool::selection_changed(Inkscape::Selection* selection) { g_assert (selection != NULL); - this->shape_editor->unset_item(SH_KNOTHOLDER); - this->shape_editor->set_item(selection->singleItem(), SH_KNOTHOLDER); + this->shape_editor->unset_item(); + this->shape_editor->set_item(selection->singleItem()); } void StarTool::setup() { @@ -129,7 +129,7 @@ void StarTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } Inkscape::Selection *selection = sp_desktop_selection(this->desktop); diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp index ac830fe6b..b60a39e5d 100644 --- a/src/ui/tools/text-tool.cpp +++ b/src/ui/tools/text-tool.cpp @@ -177,7 +177,7 @@ void TextTool::setup() { SPItem *item = sp_desktop_selection(this->desktop)->singleItem(); if (item && SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { - this->shape_editor->set_item(item, SH_KNOTHOLDER); + this->shape_editor->set_item(item); } this->sel_changed_connection = sp_desktop_selection(desktop)->connectChangedFirst( @@ -1411,10 +1411,10 @@ void TextTool::_selectionChanged(Inkscape::Selection *selection) ToolBase *ec = SP_EVENT_CONTEXT(this); - ec->shape_editor->unset_item(SH_KNOTHOLDER); + ec->shape_editor->unset_item(); SPItem *item = selection->singleItem(); if (item && SP_IS_FLOWTEXT(item) && SP_FLOWTEXT(item)->has_internal_frame()) { - ec->shape_editor->set_item(item, SH_KNOTHOLDER); + ec->shape_editor->set_item(item); } if (this->text && (item != this->text)) { diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index 4195c9eb2..c23da87c0 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -58,6 +58,7 @@ #include "sp-guide.h" #include "color.h" #include "knot.h" +#include "knot-ptr.h" // globals for temporary switching to selector by space static bool selector_toggled = FALSE; @@ -1289,8 +1290,7 @@ void sp_event_context_snap_delay_handler(ToolBase *ec, // now, just in case there's no future motion event that drops under the speed limit (when // stopping abruptly) delete ec->_delayed_snap_event; - ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, - event, origin); // watchdog is reset, i.e. pushed forward in time + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, event, origin); // watchdog is reset, i.e. pushed forward in time // If the watchdog expires before a new motion event is received, we will snap (as explained // above). This means however that when the timer is too short, we will always snap and that the // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will @@ -1301,15 +1301,13 @@ void sp_event_context_snap_delay_handler(ToolBase *ec, // snap, and set a new watchdog again. if (ec->_delayed_snap_event == NULL) { // no watchdog has been set // it might have already expired, so we'll set a new one; the snapping frequency will be limited this way - ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, - dse_item2, event, origin); + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, event, origin); } // else: watchdog has been set before and we'll wait for it to expire } } else { // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog g_assert(ec->_delayed_snap_event == NULL); - ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, - event, origin); + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, event, origin); } prev_pos = event_pos; @@ -1362,6 +1360,7 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) { break; case DelayedSnapEvent::KNOT_HANDLER: { gpointer knot = dse->getItem2(); + check_if_knot_deleted(knot); if (knot && SP_IS_KNOT(knot)) { sp_knot_handler_request_position(dse->getEvent(), SP_KNOT(knot)); } diff --git a/src/ui/widget/style-swatch.cpp b/src/ui/widget/style-swatch.cpp index a33c1d09f..98f4e47cd 100644 --- a/src/ui/widget/style-swatch.cpp +++ b/src/ui/widget/style-swatch.cpp @@ -261,7 +261,7 @@ void StyleSwatch::setStyle(SPCSSAttr *css) Glib::ustring css_string; sp_repr_css_write_string (_css, css_string); SPStyle *temp_spstyle = sp_style_new(SP_ACTIVE_DOCUMENT); - if (~css_string.empty()) { + if (!css_string.empty()) { sp_style_merge_from_style_string (temp_spstyle, css_string.c_str()); } diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 09b5d24a9..7717a304d 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -1247,8 +1247,8 @@ SPDesktopWidget::shutdown() void SPDesktopWidget::requestCanvasUpdate() { // ^^ also this->desktop != 0 - g_return_if_fail (this->desktop != NULL); - g_return_if_fail (this->desktop->main != NULL); + g_return_if_fail(this->desktop != NULL); + g_return_if_fail(this->desktop->main != NULL); gtk_widget_queue_draw (GTK_WIDGET (SP_CANVAS_ITEM (this->desktop->main)->canvas)); } diff --git a/src/widgets/pencil-toolbar.cpp b/src/widgets/pencil-toolbar.cpp index 772107101..cf09a4d34 100644 --- a/src/widgets/pencil-toolbar.cpp +++ b/src/widgets/pencil-toolbar.cpp @@ -162,6 +162,7 @@ static GList * freehand_shape_dropdown_items_list() { glist = g_list_append (glist, _("Triangle out")); glist = g_list_append (glist, _("Ellipse")); glist = g_list_append (glist, _("From clipboard")); + glist = g_list_append (glist, _("Last applied")); return glist; } diff --git a/src/widgets/ruler.cpp b/src/widgets/ruler.cpp index 5d5151343..8e818843d 100644 --- a/src/widgets/ruler.cpp +++ b/src/widgets/ruler.cpp @@ -1394,7 +1394,7 @@ sp_ruler_draw_ticks (SPRuler *ruler) (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) && (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0)) { - if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur)) + if (std::abs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur)) sprintf (unit_str, "%dk", ((int) cur)/1000); else sprintf (unit_str, "%d", (int) cur); diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index 64a7cd5e7..36a151c52 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -31,7 +31,6 @@ #include "libnrtype/font-lister.h" #include <glibmm/i18n.h> #include "text-toolbar.h" -#include "connection-pool.h" #include "desktop-handles.h" #include "desktop-style.h" #include "desktop.h" @@ -54,6 +53,7 @@ #include "toolbox.h" #include "ui/icon-names.h" #include "ui/tools/text-tool.h" +#include "ui/tools/tool-base.h" #include "verbs.h" #include "xml/repr.h" @@ -1200,6 +1200,7 @@ static void sp_text_toolbox_select_cb( GtkEntry* entry, GtkEntryIconPosition /*p selection->setList(selectList); } +static void text_toolbox_watch_ec(SPDesktop* dt, Inkscape::UI::Tools::ToolBase* ec, GObject* holder); // Define all the "widgets" in the toolbar. void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) @@ -1622,31 +1623,35 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje // Is this necessary to call? Shouldn't hurt. sp_text_toolbox_selection_changed(sp_desktop_selection(desktop), holder); - // Watch selection - Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolboxGTK"); - - sigc::connection *c_selection_changed = - new sigc::connection (sp_desktop_selection (desktop)->connectChanged - (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), holder))); - pool->add_connection ("selection-changed", c_selection_changed); - - sigc::connection *c_selection_modified = - new sigc::connection (sp_desktop_selection (desktop)->connectModified - (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), holder))); - pool->add_connection ("selection-modified", c_selection_modified); - - sigc::connection *c_subselection_changed = - new sigc::connection (desktop->connectToolSubselectionChanged - (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), holder))); - pool->add_connection ("tool-subselection-changed", c_subselection_changed); - - Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool); + desktop->connectEventContextChanged(sigc::bind(sigc::ptr_fun(text_toolbox_watch_ec), holder)); g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder ); } +static void text_toolbox_watch_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec, GObject* holder) { + using sigc::connection; + using sigc::bind; + using sigc::ptr_fun; + static connection c_selection_changed; + static connection c_selection_modified; + static connection c_subselection_changed; + + if (SP_IS_TEXT_CONTEXT(ec)) { + // Watch selection + c_selection_changed = sp_desktop_selection(desktop)->connectChanged(bind(ptr_fun(sp_text_toolbox_selection_changed), holder)); + c_selection_modified = sp_desktop_selection (desktop)->connectModified(bind(ptr_fun(sp_text_toolbox_selection_modified), holder)); + c_subselection_changed = desktop->connectToolSubselectionChanged(bind(ptr_fun(sp_text_toolbox_subselection_changed), holder)); + } else { + if (c_selection_changed) + c_selection_changed.disconnect(); + if (c_selection_modified) + c_selection_modified.disconnect(); + if (c_subselection_changed) + c_subselection_changed.disconnect(); + } +} /* Local Variables: mode:c++ |
