diff options
| author | Ted Gould <ted@gould.cx> | 2010-07-13 20:16:51 +0000 |
|---|---|---|
| committer | Ted Gould <ted@gould.cx> | 2010-07-13 20:16:51 +0000 |
| commit | ef2d72d29fce61231bdd7888ab8330503158e9d6 (patch) | |
| tree | 4685bfddef010181fdd87fc182016b994f2b69d9 /src | |
| parent | Pulling in a more recent trunk (diff) | |
| parent | Translations. Romanian nsh file update. (diff) | |
| download | inkscape-ef2d72d29fce61231bdd7888ab8330503158e9d6.tar.gz inkscape-ef2d72d29fce61231bdd7888ab8330503158e9d6.zip | |
Upgrading to trunk
(bzr r8254.1.57)
Diffstat (limited to 'src')
128 files changed, 3590 insertions, 1871 deletions
diff --git a/src/2geom/circle.cpp b/src/2geom/circle.cpp index c3cea0ae7..00b91de12 100644 --- a/src/2geom/circle.cpp +++ b/src/2geom/circle.cpp @@ -97,6 +97,23 @@ Circle::arc(Point const& initial, Point const& inner, Point const& final, return e.arc(initial, inner, final, _svg_compliant); } +void +Circle::getPath(std::vector<Path> &path_out) { + Path pb; + + D2<SBasis> B; + Linear bo = Linear(0, 2 * M_PI); + + B[0] = cos(bo,4); + B[1] = sin(bo,4); + + B = B * m_ray + m_centre; + + pb.append(SBasisCurve(B)); + + path_out.push_back(pb); +} + } // end namespace Geom diff --git a/src/2geom/circle.h b/src/2geom/circle.h index 27d4fcc3f..c346b8c8f 100644 --- a/src/2geom/circle.h +++ b/src/2geom/circle.h @@ -38,7 +38,7 @@ #include <2geom/point.h> #include <2geom/exception.h> - +#include <2geom/path.h> namespace Geom { @@ -56,6 +56,11 @@ class Circle { } + Circle(Point center, double r) + : m_centre(center), m_ray(r) + { + } + Circle(double A, double B, double C, double D) { set(A, B, C, D); @@ -86,6 +91,9 @@ class Circle arc(Point const& initial, Point const& inner, Point const& final, bool _svg_compliant = true); + void + getPath(std::vector<Path> &path_out); + Point center() const { return m_centre; diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp index ce5bf89bc..0a5441254 100644 --- a/src/2geom/sbasis-to-bezier.cpp +++ b/src/2geom/sbasis-to-bezier.cpp @@ -99,6 +99,10 @@ int sgn(unsigned int j, unsigned int k) */ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz) { + if (sb.size() == 0) { + THROW_RANGEERROR("size of sb is too small"); + } + size_t q, n; bool even; if (sz == 0) diff --git a/src/attributes.cpp b/src/attributes.cpp index 3cfe04fab..c44a7da4e 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -280,6 +280,7 @@ static SPStyleProp const props[] = { {SP_ATTR_GRADIENTUNITS, "gradientUnits"}, {SP_ATTR_GRADIENTTRANSFORM, "gradientTransform"}, {SP_ATTR_SPREADMETHOD, "spreadMethod"}, + {SP_ATTR_OSB_SWATCH, "osb:paint"}, /* SPRadialGradient */ {SP_ATTR_FX, "fx"}, {SP_ATTR_FY, "fy"}, diff --git a/src/attributes.h b/src/attributes.h index c21087e36..aadb4d165 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -281,6 +281,7 @@ enum SPAttributeEnum { SP_ATTR_GRADIENTUNITS, SP_ATTR_GRADIENTTRANSFORM, SP_ATTR_SPREADMETHOD, + SP_ATTR_OSB_SWATCH, /* SPRadialGradient */ SP_ATTR_FX, SP_ATTR_FY, diff --git a/src/color.cpp b/src/color.cpp index 07c15ff15..ae1bfa05d 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -17,7 +17,6 @@ #include <math.h> #include "color.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #include "svg/svg-color.h" #include "svg/css-ostringstream.h" @@ -30,8 +29,7 @@ static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second #define PROFILE_EPSILON 0.00000001 SPColor::SPColor() : - icc(0), - device(0) + icc(0) { v.c[0] = 0; v.c[1] = 0; @@ -39,22 +37,19 @@ SPColor::SPColor() : } SPColor::SPColor( SPColor const& other ) : - icc(0), - device(0) + icc(0) { *this = other; } SPColor::SPColor( float r, float g, float b ) : - icc(0), - device(0) + icc(0) { set( r, g, b ); } SPColor::SPColor( guint32 value ) : - icc(0), - device(0) + icc(0) { set( value ); } @@ -62,16 +57,13 @@ SPColor::SPColor( guint32 value ) : SPColor::~SPColor() { delete icc; - delete device; icc = 0; - device = 0; } SPColor& SPColor::operator= (SPColor const& other) { SVGICCColor* tmp_icc = other.icc ? new SVGICCColor(*other.icc) : 0; - SVGDeviceColor* tmp_device = other.device ? new SVGDeviceColor(*other.device) : 0; v.c[0] = other.v.c[0]; v.c[1] = other.v.c[1]; @@ -81,11 +73,6 @@ SPColor& SPColor::operator= (SPColor const& other) } icc = tmp_icc; - if ( device ) { - delete device; - } - device = tmp_device; - return *this; } @@ -100,7 +87,6 @@ bool SPColor::operator == (SPColor const& other) const && (v.c[2] != other.v.c[2]); match &= profileMatches( icc, other.icc ); -//TODO?: match &= devicecolorMatches( device, other.device ); return match; } @@ -219,38 +205,6 @@ std::string SPColor::toString() const css << ')'; } - if ( device && device->type != DEVICE_COLOR_INVALID) { - if ( !css.str().empty() ) { - css << " "; - } - - switch(device->type){ - case DEVICE_GRAY: - css << "device-gray("; - break; - case DEVICE_RGB: - css << "device-rgb("; - break; - case DEVICE_CMYK: - css << "device-cmyk("; - break; - case DEVICE_NCHANNEL: - css << "device-nchannel("; - break; - case DEVICE_COLOR_INVALID: - //should not be reached - break; - } - - for (vector<double>::const_iterator i(device->colors.begin()), - iEnd(device->colors.end()); - i != iEnd; ++i) { - if (i!=device->colors.begin()) css << ", "; - css << *i; - } - css << ')'; - } - return css.str(); } diff --git a/src/color.h b/src/color.h index 7fd351360..bebeaec60 100644 --- a/src/color.h +++ b/src/color.h @@ -34,7 +34,6 @@ #define SP_RGBA32_F_COMPOSE(r,g,b,a) SP_RGBA32_U_COMPOSE (SP_COLOR_F_TO_U (r), SP_COLOR_F_TO_U (g), SP_COLOR_F_TO_U (b), SP_COLOR_F_TO_U (a)) struct SVGICCColor; -struct SVGDeviceColor; /** * An RGB color with optional icc-color part @@ -60,7 +59,6 @@ struct SPColor { std::string toString() const; SVGICCColor* icc; - SVGDeviceColor* device; union { float c[3]; } v; diff --git a/src/context-fns.cpp b/src/context-fns.cpp index 0ff7bd120..b22cd488d 100644 --- a/src/context-fns.cpp +++ b/src/context-fns.cpp @@ -132,11 +132,11 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item /* Try to snap p[0] (the opposite corner) along the constraint vector */ s[0] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE), - Inkscape::Snapper::ConstraintLine(p[0] - p[1])); + Inkscape::Snapper::SnapConstraint(p[0] - p[1])); /* Try to snap p[1] (the dragged corner) along the constraint vector */ s[1] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), - Inkscape::Snapper::ConstraintLine(p[1] - p[0])); + Inkscape::Snapper::SnapConstraint(p[1] - p[0])); /* Choose the best snap and update points accordingly */ if (s[0].getSnapDistance() < s[1].getSnapDistance()) { @@ -157,7 +157,7 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item /* Our origin is the opposite corner. Snap the drag point along the constraint vector */ p[0] = center; snappoint = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), - Inkscape::Snapper::ConstraintLine(p[1] - p[0])); + Inkscape::Snapper::SnapConstraint(p[1] - p[0])); if (snappoint.getSnapped()) { p[1] = snappoint.getPoint(); } diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 2225be5ee..5866b14fb 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -44,7 +44,6 @@ #include "desktop-style.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #include "box3d-side.h" /** @@ -196,10 +195,29 @@ sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write // 3. If nobody has intercepted the signal, apply the style to the selection if (!intercepted) { + + // Remove text attributes if not text... + // Do this once in case a zillion objects are selected. + SPCSSAttr *css_no_text = sp_repr_css_attr_new(); + sp_repr_css_merge(css_no_text, css); + css_no_text = sp_css_attr_unset_text(css_no_text); + for (GSList const *i = desktop->selection->itemList(); i != NULL; i = i->next) { - /// \todo if the style is text-only, apply only to texts? - sp_desktop_apply_css_recursive(SP_OBJECT(i->data), css, true); + + // If not text, don't apply text attributes (can a group have text attributes?) + if ( SP_IS_TEXT(i->data) || SP_IS_FLOWTEXT(i->data) + || SP_IS_TSPAN(i->data) || SP_IS_TREF(i->data) || SP_IS_TEXTPATH(i->data) + || SP_IS_FLOWDIV(i->data) || SP_IS_FLOWPARA(i->data) || SP_IS_FLOWTSPAN(i->data)) { + + sp_desktop_apply_css_recursive(SP_OBJECT(i->data), css, true); + + } else { + + sp_desktop_apply_css_recursive(SP_OBJECT(i->data), css_no_text, true); + + } } + sp_repr_css_attr_unref(css_no_text); } } @@ -417,6 +435,20 @@ stroke_average_width (GSList const *objects) return avgwidth / (g_slist_length ((GSList *) objects) - n_notstroked); } +static bool vectorsClose( std::vector<double> const &lhs, std::vector<double> const &rhs ) +{ + static double epsilon = 1e-6; + bool isClose = false; + if ( lhs.size() == rhs.size() ) { + isClose = true; + for ( size_t i = 0; (i < lhs.size()) && isClose; ++i ) { + isClose = fabs(lhs[i] - rhs[i]) < epsilon; + } + } + return isClose; +} + + /** * Write to style_res the average fill or stroke of list of objects, if applicable. */ @@ -433,7 +465,6 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill paint_res->set = TRUE; SVGICCColor* iccColor = 0; - SVGDeviceColor* devColor = 0; bool iccSeen = false; gfloat c[4]; @@ -517,11 +548,15 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill iccColor = paint->value.color.icc; iccSeen = true; } else { - if (same_color && (prev[0] != d[0] || prev[1] != d[1] || prev[2] != d[2])) + if (same_color && (prev[0] != d[0] || prev[1] != d[1] || prev[2] != d[2])) { same_color = false; - if ( iccSeen ) { - if(paint->value.color.icc) { - // TODO fix this + iccColor = 0; + } + if ( iccSeen && iccColor ) { + if ( !paint->value.color.icc + || (iccColor->colorProfile != paint->value.color.icc->colorProfile) + || !vectorsClose(iccColor->colors, paint->value.color.icc->colors) ) { + same_color = false; iccColor = 0; } } @@ -533,22 +568,6 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill c[2] += d[2]; c[3] += SP_SCALE24_TO_FLOAT (isfill? style->fill_opacity.value : style->stroke_opacity.value); - // average device color - unsigned int it; - if (i==objects /*if this is the first object in the GList*/ - && paint->value.color.device){ - devColor = new SVGDeviceColor(*paint->value.color.device); - for (it=0; it < paint->value.color.device->colors.size(); it++){ - devColor->colors[it] = 0; - } - } - - if (devColor && paint->value.color.device && paint->value.color.device->type == devColor->type){ - for (it=0; it < paint->value.color.device->colors.size(); it++){ - devColor->colors[it] += paint->value.color.device->colors[it]; - } - } - num ++; } @@ -588,14 +607,6 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill paint_res->value.color.icc = tmp; } - // divide and store the device-color - if (devColor){ - for (unsigned int it=0; it < devColor->colors.size(); it++){ - devColor->colors[it] /= num; - } - paint_res->value.color.device = devColor; - } - if (num > 1) { if (same_color) return QUERY_STYLE_MULTIPLE_SAME; @@ -1052,6 +1063,101 @@ objects_query_fontstyle (GSList *objects, SPStyle *style_res) } /** + * Write to style_res the baseline numbers. + */ +int +objects_query_baselines (GSList *objects, SPStyle *style_res) +{ + bool different = false; + + // Only baseline-shift at the moment + // We will return: + // If baseline-shift is same for all objects: + // The full baseline-shift data (used for subscripts and superscripts) + // If baseline-shift is different: + // The average baseline-shift (not implemented at the moment as this is complicated June 2010) + SPIBaselineShift old; + old.value = 0.0; + old.computed = 0.0; + + // double baselineshift = 0.0; + bool set = false; + + int texts = 0; + + for (GSList const *i = objects; i != NULL; i = i->next) { + SPObject *obj = SP_OBJECT (i->data); + + if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj) + && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj) + && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj)) + continue; + + SPStyle *style = SP_OBJECT_STYLE (obj); + if (!style) continue; + + texts ++; + + SPIBaselineShift current; + if(style->baseline_shift.set) { + + current.set = style->baseline_shift.set; + current.inherit = style->baseline_shift.inherit; + current.type = style->baseline_shift.type; + current.literal = style->baseline_shift.literal; + current.value = style->baseline_shift.value; + current.computed = style->baseline_shift.computed; + + if( set ) { + if( current.set != old.set || + current.inherit != old.inherit || + current.type != old.type || + current.literal != old.literal || + current.value != old.value || + current.computed != old.computed ) { + // Maybe this needs to be better thought out. + different = true; + } + } + + set = true; + + old.set = current.set; + old.inherit = current.inherit; + old.type = current.type; + old.literal = current.literal; + old.value = current.value; + old.computed = current.computed; + } + } + + if (different || !set ) { + style_res->baseline_shift.set = false; + style_res->baseline_shift.computed = 0.0; + } else { + style_res->baseline_shift.set = old.set; + style_res->baseline_shift.inherit = old.inherit; + style_res->baseline_shift.type = old.type; + style_res->baseline_shift.literal = old.literal; + style_res->baseline_shift.value = old.value; + style_res->baseline_shift.computed = old.computed; + } + + if (texts == 0 || !set) + return QUERY_STYLE_NOTHING; + + if (texts > 1) { + if (different) { + return QUERY_STYLE_MULTIPLE_DIFFERENT; + } else { + return QUERY_STYLE_MULTIPLE_SAME; + } + } else { + return QUERY_STYLE_SINGLE; + } +} + +/** * Write to style_res the average font family of objects. */ int @@ -1346,6 +1452,8 @@ sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property) return objects_query_fontstyle (list, style); } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) { return objects_query_fontnumbers (list, style); + } else if (property == QUERY_STYLE_PROPERTY_BASELINES) { + return objects_query_baselines (list, style); } else if (property == QUERY_STYLE_PROPERTY_BLEND) { return objects_query_blend (list, style); diff --git a/src/desktop-style.h b/src/desktop-style.h index 40461da72..e1ca5b3e7 100644 --- a/src/desktop-style.h +++ b/src/desktop-style.h @@ -48,6 +48,7 @@ enum { // which property was queried (add when you need more) QUERY_STYLE_PROPERTY_FONTFAMILY, // font-family QUERY_STYLE_PROPERTY_FONTSTYLE, // font style QUERY_STYLE_PROPERTY_FONTNUMBERS, // size, spacings + QUERY_STYLE_PROPERTY_BASELINES, // baseline-shift QUERY_STYLE_PROPERTY_MASTEROPACITY, // opacity QUERY_STYLE_PROPERTY_BLEND, // blend QUERY_STYLE_PROPERTY_BLUR // blur diff --git a/src/desktop.cpp b/src/desktop.cpp index 52f172577..a93fb6a4a 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -462,9 +462,9 @@ void SPDesktop::displayModeToggle() { _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); break; case Inkscape::RENDERMODE_OUTLINE: - _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); + _setDisplayMode(Inkscape::RENDERMODE_NORMAL); break; - case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW: +// case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW: default: _setDisplayMode(Inkscape::RENDERMODE_NORMAL); } diff --git a/src/desktop.h b/src/desktop.h index 00f6cfdd5..af2473baf 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -203,9 +203,9 @@ struct SPDesktop : public Inkscape::UI::View::View void setDisplayModeOutline() { _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); } - void setDisplayModePrintColorsPreview() { - _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); - } +// void setDisplayModePrintColorsPreview() { +// _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); +// } void displayModeToggle(); Inkscape::RenderMode _display_mode; Inkscape::RenderMode getMode() const { return _display_mode; } diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 37469fa73..d17689b06 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -794,9 +794,10 @@ void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Poin sc.grid_lines.push_back(dummy); } -void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +void CanvasAxonomGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const { SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); + dummy.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating sc.points.push_back(dummy); } diff --git a/src/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index 58185e2e6..4a9820792 100644 --- a/src/display/canvas-axonomgrid.h +++ b/src/display/canvas-axonomgrid.h @@ -79,7 +79,7 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const; CanvasAxonomGrid *grid; }; diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index a79a6b610..fe1e92824 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -1030,9 +1030,10 @@ void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point co sc.grid_lines.push_back(dummy); } -void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +void CanvasXYGridSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const { SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GRID, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); + dummy.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating sc.points.push_back(dummy); } diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index a11d77d1d..0aedb02a0 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -167,7 +167,7 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const; CanvasXYGrid *grid; }; diff --git a/src/display/nr-arena-image.cpp b/src/display/nr-arena-image.cpp index 493943168..ec11d9ed1 100644 --- a/src/display/nr-arena-image.cpp +++ b/src/display/nr-arena-image.cpp @@ -92,6 +92,7 @@ nr_arena_image_init (NRArenaImage *image) image->grid2px.setIdentity(); image->style = 0; + image->render_opacity = TRUE; } static void diff --git a/src/display/nr-filter-flood.cpp b/src/display/nr-filter-flood.cpp index 1d804f969..fd0600cdb 100644 --- a/src/display/nr-filter-flood.cpp +++ b/src/display/nr-filter-flood.cpp @@ -32,7 +32,7 @@ FilterFlood::~FilterFlood() {} int FilterFlood::render(FilterSlot &slot, FilterUnits const &/*units*/) { -g_message("rendering feflood"); +//g_message("rendering feflood"); NRPixBlock *in = slot.get(_input); if (!in) { g_warning("Missing source image for feFlood (in=%d)", _input); @@ -60,7 +60,7 @@ g_message("rendering feflood"); #if ENABLE_LCMS icc_color_to_sRGB(icc, &r, &g, &b); -g_message("result: r:%d g:%d b:%d", r, g, b); +//g_message("result: r:%d g:%d b:%d", r, g, b); #endif //ENABLE_LCMS for(i=0; i < 4*in_h*in_w; i+=4){ diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index fe5bd0371..0409e64b1 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -51,7 +51,6 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap g_assert(_desktop != NULL); if (!p.getSnapped()) { - g_warning("No snapping took place, so no snap target will be displayed"); return; // If we haven't snapped, then it is of no use to draw a snapindicator } diff --git a/src/document.cpp b/src/document.cpp index 101c54e30..eff6d6e81 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -682,7 +682,7 @@ void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) margin_units = sp_unit_get_by_abbreviation(units_abbr); } if (margin_units == NULL) { - margin_units = &sp_unit_get_by_id(SP_UNIT_PX); + margin_units = &px; } margin_top = getMarginLength(nv_repr, "fit-margin-top", margin_units, &px, w, h, false); diff --git a/src/draw-context.cpp b/src/draw-context.cpp index da22c8a7a..a531b88d1 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -511,7 +511,7 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p, /* Snap it along best vector */ SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; m.setup(SP_EVENT_CONTEXT_DESKTOP(ec)); - m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::ConstraintLine(best)); + m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::SnapConstraint(best)); } } } @@ -837,7 +837,7 @@ void spdc_create_single_dot(SPEventContext *ec, Geom::Point const &pt, char cons Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Geom::Matrix const i2d (sp_item_i2d_affine (item)); - Geom::Point pp = pt; + Geom::Point pp = pt * i2d.inverse(); double rad = 0.5 * prefs->getDouble(tool_path + "/dot-size", 3.0); if (event_state & GDK_MOD1_MASK) { /* TODO: We vary the dot size between 0.5*rad and 1.5*rad, where rad is the dot size @@ -856,7 +856,6 @@ void spdc_create_single_dot(SPEventContext *ec, Geom::Point const &pt, char cons sp_repr_set_svg_double (repr, "sodipodi:rx", rad * stroke_width); sp_repr_set_svg_double (repr, "sodipodi:ry", rad * stroke_width); item->updateRepr(); - sp_item_set_item_transform(item, i2d.inverse()); sp_desktop_selection(desktop)->set(item); diff --git a/src/ege-adjustment-action.cpp b/src/ege-adjustment-action.cpp index e6ec392ad..17e11db2d 100644 --- a/src/ege-adjustment-action.cpp +++ b/src/ege-adjustment-action.cpp @@ -60,6 +60,8 @@ #include <gtk/gtktooltips.h> #include <gtk/gtkradiomenuitem.h> +#include "widgets/icon.h" +#include "icon-size.h" #include "ege-adjustment-action.h" @@ -132,6 +134,8 @@ struct _EgeAdjustmentActionPrivate gboolean transferFocus; GList* descriptions; gchar* appearance; + gchar* iconId; + Inkscape::IconSize iconSize; }; #define EGE_ADJUSTMENT_ACTION_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_ADJUSTMENT_ACTION_TYPE, EgeAdjustmentActionPrivate ) ) @@ -143,7 +147,9 @@ enum { PROP_DIGITS, PROP_SELFID, PROP_TOOL_POST, - PROP_APPEARANCE + PROP_APPEARANCE, + PROP_ICON_ID, + PROP_ICON_SIZE }; enum { @@ -253,6 +259,24 @@ static void ege_adjustment_action_class_init( EgeAdjustmentActionClass* klass ) "", (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + g_object_class_install_property( objClass, + PROP_ICON_ID, + g_param_spec_string( "iconId", + "Icon ID", + "The id for the icon", + "", + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, + PROP_ICON_SIZE, + g_param_spec_int( "iconSize", + "Icon Size", + "The size the icon", + (int)Inkscape::ICON_SIZE_MENU, + (int)Inkscape::ICON_SIZE_DECORATION, + (int)Inkscape::ICON_SIZE_SMALL_TOOLBAR, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + g_type_class_add_private( klass, sizeof(EgeAdjustmentActionClass) ); } } @@ -276,6 +300,8 @@ static void ege_adjustment_action_init( EgeAdjustmentAction* action ) action->private_data->transferFocus = FALSE; action->private_data->descriptions = 0; action->private_data->appearance = 0; + action->private_data->iconId = 0; + action->private_data->iconSize = Inkscape::ICON_SIZE_SMALL_TOOLBAR; } static void ege_adjustment_action_finalize( GObject* object ) @@ -353,6 +379,14 @@ static void ege_adjustment_action_get_property( GObject* obj, guint propId, GVal g_value_set_string( value, action->private_data->appearance ); break; + case PROP_ICON_ID: + g_value_set_string( value, action->private_data->iconId ); + break; + + case PROP_ICON_SIZE: + g_value_set_int( value, action->private_data->iconSize ); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); } @@ -440,6 +474,20 @@ void ege_adjustment_action_set_property( GObject* obj, guint propId, const GValu } break; + case PROP_ICON_ID: + { + gchar* tmp = action->private_data->iconId; + action->private_data->iconId = g_value_dup_string( value ); + g_free( tmp ); + } + break; + + case PROP_ICON_SIZE: + { + action->private_data->iconSize = (Inkscape::IconSize)g_value_get_int( value ); + } + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec ); } @@ -791,7 +839,8 @@ static GtkWidget* create_tool_item( GtkAction* action ) const gchar* sss = g_value_get_string( &value ); if ( act->private_data->appearanceMode == APPEARANCE_FULL ) { - spinbutton = gtk_hscale_new( act->private_data->adj); + // Slider + spinbutton = gtk_hscale_new( act->private_data->adj); gtk_widget_set_size_request(spinbutton, 100, -1); gtk_scale_set_digits (GTK_SCALE(spinbutton), 0); gtk_signal_connect(GTK_OBJECT(spinbutton), "format-value", GTK_SIGNAL_FUNC(slider_format_falue), (void *) sss); @@ -823,11 +872,18 @@ static GtkWidget* create_tool_item( GtkAction* action ) } if ( act->private_data->appearanceMode != APPEARANCE_FULL ) { - GtkWidget* lbl = gtk_label_new( sss ? sss : "wwww" ); - GtkWidget* filler1 = gtk_label_new(" "); - gtk_misc_set_alignment( GTK_MISC(lbl), 1.0, 0.5 ); - gtk_box_pack_start( GTK_BOX(hb), filler1, FALSE, FALSE, 0 ); - gtk_box_pack_start( GTK_BOX(hb), lbl, FALSE, FALSE, 0 ); + GtkWidget* filler1 = gtk_label_new(" "); + gtk_box_pack_start( GTK_BOX(hb), filler1, FALSE, FALSE, 0 ); + + // Use an icon if available or use short-label + if ( act->private_data->iconId && strcmp( act->private_data->iconId, "" ) != 0 ) { + GtkWidget* icon = sp_icon_new( act->private_data->iconSize, act->private_data->iconId ); + gtk_box_pack_start( GTK_BOX(hb), icon, FALSE, FALSE, 0 ); + } else { + GtkWidget* lbl = gtk_label_new( sss ? sss : "wwww" ); + gtk_misc_set_alignment( GTK_MISC(lbl), 1.0, 0.5 ); + gtk_box_pack_start( GTK_BOX(hb), lbl, FALSE, FALSE, 0 ); + } } if ( act->private_data->appearanceMode == APPEARANCE_FULL ) { diff --git a/src/ege-select-one-action.cpp b/src/ege-select-one-action.cpp index ebc7ea26d..664ffd13d 100644 --- a/src/ege-select-one-action.cpp +++ b/src/ege-select-one-action.cpp @@ -67,6 +67,7 @@ static void ege_select_one_action_set_property( GObject* obj, guint propId, cons static gint find_text_index(EgeSelectOneAction *act, gchar const* text); static void commit_pending_change(EgeSelectOneAction *act); static void resync_active( EgeSelectOneAction* act, gint active, gboolean override ); +static void resync_sensitive( EgeSelectOneAction* act ); static void combo_entry_changed_cb( GtkEntry* widget, gpointer user_data ); static gboolean combo_entry_focus_lost_cb( GtkWidget *widget, GdkEventFocus *event, gpointer data ); static void combo_changed_cb( GtkComboBox* widget, gpointer user_data ); @@ -105,6 +106,7 @@ struct _EgeSelectOneActionPrivate gint labelColumn; gint iconColumn; gint tooltipColumn; + gint sensitiveColumn; gint appearanceMode; gint selectionMode; gint iconSize; @@ -125,6 +127,7 @@ enum { PROP_LABEL_COLUMN, PROP_ICON_COLUMN, PROP_TOOLTIP_COLUMN, + PROP_SENSITIVE_COLUMN, PROP_ICON_PROP, PROP_ICON_SIZE, PROP_APPEARANCE, @@ -214,6 +217,14 @@ void ege_select_one_action_class_init( EgeSelectOneActionClass* klass ) (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); g_object_class_install_property( objClass, + PROP_SENSITIVE_COLUMN, + g_param_spec_int( "sensitive-column", + "Sensitive Column", + "The column of the model that holds sensitive state", + -1, G_MAXINT, -1, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT) ) ); + + g_object_class_install_property( objClass, PROP_ICON_PROP, g_param_spec_string( "icon-property", "Icon Property", @@ -265,6 +276,7 @@ void ege_select_one_action_init( EgeSelectOneAction* action ) action->private_data->labelColumn = 0; action->private_data->iconColumn = -1; action->private_data->tooltipColumn = -1; + action->private_data->sensitiveColumn = -1; action->private_data->appearanceMode = APPEARANCE_NONE; action->private_data->selectionMode = SELECTION_CLOSED; action->private_data->radioActionType = 0; @@ -347,6 +359,15 @@ void ege_select_one_action_set_active( EgeSelectOneAction* action, gint val ) g_object_set( G_OBJECT(action), "active", val, NULL ); } +void ege_select_one_action_update_sensitive( EgeSelectOneAction* action ) +{ + if( action->private_data->sensitiveColumn < 0 ) { + g_warning( "ege_select_one_action: Attempt to update sensitivity of item without sensitive column\n" ); + return; + } + resync_sensitive( action ); +} + gint ege_select_one_action_get_label_column( EgeSelectOneAction* action ) { g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); @@ -391,6 +412,17 @@ void ege_select_one_action_set_tooltip_column( EgeSelectOneAction* action, gint g_object_set( G_OBJECT(action), "tooltip-column", col, NULL ); } +gint ege_select_one_action_get_sensitive_column( EgeSelectOneAction* action ) +{ + g_return_val_if_fail( IS_EGE_SELECT_ONE_ACTION(action), 0 ); + return action->private_data->sensitiveColumn; +} + +void ege_select_one_action_set_sensitive_column( EgeSelectOneAction* action, gint col ) +{ + g_object_set( G_OBJECT(action), "sensitive-column", col, NULL ); +} + void ege_select_one_action_set_appearance( EgeSelectOneAction* action, gchar const* val ) { g_object_set( G_OBJECT(action), "appearance", val, NULL ); @@ -425,6 +457,10 @@ void ege_select_one_action_get_property( GObject* obj, guint propId, GValue* val g_value_set_int( value, action->private_data->tooltipColumn ); break; + case PROP_SENSITIVE_COLUMN: + g_value_set_int( value, action->private_data->sensitiveColumn ); + break; + case PROP_ICON_PROP: g_value_set_string( value, action->private_data->iconProperty ); break; @@ -480,6 +516,12 @@ void ege_select_one_action_set_property( GObject* obj, guint propId, const GValu } break; + case PROP_SENSITIVE_COLUMN: + { + action->private_data->sensitiveColumn = g_value_get_int( value ); + } + break; + case PROP_ICON_PROP: { gchar* tmp = action->private_data->iconProperty; @@ -623,7 +665,10 @@ GtkWidget* create_tool_item( GtkAction* action ) gchar* sss = 0; g_object_get( G_OBJECT(action), "short_label", &sss, NULL ); - if (sss) { + // If short_label not defined, g_object_get will return label. + // This hack allows a label to be used with a drop-down menu when + // no label is used with a set of icons that are self-explanatory. + if (sss && strcmp( sss, "NotUsed" ) != 0 ) { GtkWidget* lbl; lbl = gtk_label_new(sss); gtk_box_pack_start( GTK_BOX(holder), lbl, FALSE, FALSE, 4 ); @@ -634,6 +679,7 @@ GtkWidget* create_tool_item( GtkAction* action ) gchar* str = 0; gchar* tip = 0; gchar* iconId = 0; + gboolean sens = true; /* gint size = 0; */ @@ -650,6 +696,11 @@ GtkWidget* create_tool_item( GtkAction* action ) act->private_data->tooltipColumn, &tip, -1 ); } + if ( act->private_data->sensitiveColumn >= 0 ) { + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->sensitiveColumn, &sens, + -1 ); + } if ( act->private_data->radioActionType ) { void* obj = g_object_new( act->private_data->radioActionType, @@ -678,6 +729,10 @@ GtkWidget* create_tool_item( GtkAction* action ) ract = gtk_radio_action_new( "Name 1", str, tip, iconId, index ); } + if ( act->private_data->sensitiveColumn >= 0 ) { + gtk_action_set_sensitive( GTK_ACTION(ract), sens ); + } + gtk_radio_action_set_group( ract, group ); group = gtk_radio_action_get_group( ract ); @@ -855,6 +910,73 @@ void resync_active( EgeSelectOneAction* act, gint active, gboolean override ) } } +void resync_sensitive( EgeSelectOneAction* act ) +{ + GSList* proxies = gtk_action_get_proxies( GTK_ACTION(act) ); + while ( proxies ) { + if ( GTK_IS_TOOL_ITEM(proxies->data) ) { + /* Search for the things we built up in create_tool_item() */ + GList* children = gtk_container_get_children( GTK_CONTAINER(proxies->data) ); + if ( children && children->data ) { + gpointer combodata = g_object_get_data( G_OBJECT(children->data), "ege-combo-box" ); + if (!combodata && GTK_IS_ALIGNMENT(children->data)) { + GList *other = gtk_container_get_children( GTK_CONTAINER(children->data) ); + combodata = g_object_get_data( G_OBJECT(other->data), "ege-combo-box" ); + } + if ( GTK_IS_COMBO_BOX(combodata) ) { + /* Not implemented */ + } else if ( GTK_IS_HBOX(children->data) ) { + gpointer data = g_object_get_data( G_OBJECT(children->data), "ege-proxy_action-group" ); + if ( data ) { + GSList* group = (GSList*)data; + // List is backwards in group as compared to GtkTreeModel, we better do matching. + while ( group ) { +#if GTK_CHECK_VERSION(2,16,0) + GtkRadioAction* ract = GTK_RADIO_ACTION(group->data); + const gchar* label = gtk_action_get_label( GTK_ACTION( ract ) ); + + // Search for matching GtkTreeModel entry + GtkTreeIter iter; + gboolean valid; + valid = gtk_tree_model_get_iter_first( act->private_data->model, &iter ); + gboolean sens = true; + + while( valid ) { + + gchar* str = 0; + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->labelColumn, &str, + -1 ); + + if( strcmp( label, str ) == 0 ) { + gtk_tree_model_get( act->private_data->model, &iter, + act->private_data->sensitiveColumn, &sens, + -1 ); + break; + } + g_free( str ); + + valid = gtk_tree_model_iter_next( act->private_data->model, &iter ); + } + + gtk_action_set_sensitive( GTK_ACTION(ract), sens ); +#endif + + group = g_slist_next(group); + } + } + } + } + } else if ( GTK_IS_MENU_ITEM(proxies->data) ) { + /* Not implemented */ + } + + proxies = g_slist_next( proxies ); + } + + g_signal_emit( G_OBJECT(act), signals[CHANGED], 0); +} + void combo_changed_cb( GtkComboBox* widget, gpointer user_data ) { EgeSelectOneAction* act = EGE_SELECT_ONE_ACTION(user_data); diff --git a/src/ege-select-one-action.h b/src/ege-select-one-action.h index aa20d55a7..36943b978 100644 --- a/src/ege-select-one-action.h +++ b/src/ege-select-one-action.h @@ -145,6 +145,13 @@ void ege_select_one_action_set_active_text( EgeSelectOneAction* action, gchar co */ void ege_select_one_action_set_active( EgeSelectOneAction* action, gint val ); +//void ege_select_one_action_set_sensitive( EgeSelectOneAction *action, gint val, gboolean sensitive ); + +/** + * Update sensitive parameters. + * @param action The action to update. + */ +void ege_select_one_action_update_sensitive( EgeSelectOneAction *action ); /** * Returns the column used for the display label. @@ -199,6 +206,22 @@ void ege_select_one_action_set_tooltip_column( EgeSelectOneAction* action, gint /** + * Returns the column used for tracking sensitivity. + * + * @param action The action to fetch the sensitive column for. + */ +gint ege_select_one_action_get_sensitive_column( EgeSelectOneAction* action ); + +/** + * Sets the column used for sensitivity (if any). + * + * @param action The action to set the sensitive column for. + * @param col column to use. + */ +void ege_select_one_action_set_sensitive_column( EgeSelectOneAction* action, gint col ); + + +/** * Sets a hint to be used in determining the display form. * This is the XForms style 'appearance' hint: "full", "compact", "minimal". * diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp index 61760e9d9..16adebac3 100644 --- a/src/extension/internal/cairo-ps-out.cpp +++ b/src/extension/internal/cairo-ps-out.cpp @@ -195,12 +195,7 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con // Create LaTeX file (if requested) if (new_textToLaTeX) { - gchar * tex_filename; - //strip filename of ".ps", do not add ".tex" here. - gsize n = g_str_has_suffix(filename, ".ps") ? strlen(filename)-3 : strlen(filename); - tex_filename = g_strndup(filename, n); - ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_areaDrawing, new_areaPage, false); - g_free(tex_filename); + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, false); if (!ret) throw Inkscape::Extension::Output::save_failed(); @@ -283,12 +278,7 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co // Create LaTeX file (if requested) if (new_textToLaTeX) { - gchar * tex_filename; - //strip filename of ".eps", do not add ".tex" here. - gsize n = g_str_has_suffix(filename, ".eps") ? strlen(filename)-4 : strlen(filename); - tex_filename = g_strndup(filename, n); - ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_areaDrawing, new_areaPage, false); - g_free(tex_filename); + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, false); if (!ret) throw Inkscape::Extension::Output::save_failed(); diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index cf3c72432..28d1db9a4 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -1120,11 +1120,11 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver); - sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built + SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built Geom::Point p1 (lg->x1.computed, lg->y1.computed); Geom::Point p2 (lg->x2.computed, lg->y2.computed); - if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { // convert to userspace Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0); p1 *= bbox2user; @@ -1144,12 +1144,12 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver); - sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built + SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built Geom::Point c (rg->cx.computed, rg->cy.computed); Geom::Point f (rg->fx.computed, rg->fy.computed); double r = rg->r.computed; - if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) + if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) apply_bbox2user = true; // create radial gradient pattern @@ -1172,7 +1172,7 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain SPGradient *g = SP_GRADIENT(paintserver); // set extend type - SPGradientSpread spread = sp_gradient_get_spread(g); + SPGradientSpread spread = g->fetchSpread(); switch (spread) { case SP_GRADIENT_SPREAD_REPEAT: { cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp index 808590e04..e8eff20b7 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -213,12 +213,7 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, // Create LaTeX file (if requested) if (new_textToLaTeX) { - gchar * tex_filename; - //strip filename of ".pdf", do not add ".tex" here. - gsize n = g_str_has_suffix(filename, ".pdf") ? strlen(filename)-4 : strlen(filename); - tex_filename = g_strndup(filename, n); - ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_exportDrawing, new_exportCanvas, true); - g_free(tex_filename); + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_exportDrawing, new_exportCanvas, true); if (!ret) throw Inkscape::Extension::Output::save_failed(); diff --git a/src/extension/internal/filter/drop-shadow.h b/src/extension/internal/filter/drop-shadow.h index 34939222c..a48175fde 100644 --- a/src/extension/internal/filter/drop-shadow.h +++ b/src/extension/internal/filter/drop-shadow.h @@ -57,21 +57,26 @@ DropShadow::get_filter_text (Inkscape::Extension::Extension * ext) { if (_filter != NULL) g_free((void *)_filter); - float blur = ext->get_param_float("blur"); - float opacity = ext->get_param_float("opacity") / 100; - float x = ext->get_param_float("xoffset"); - float y = ext->get_param_float("yoffset"); + std::ostringstream blur; + std::ostringstream opacity; + std::ostringstream x; + std::ostringstream y; + + blur << ext->get_param_float("blur"); + opacity << ext->get_param_float("opacity") / 100; + x << ext->get_param_float("xoffset"); + y << ext->get_param_float("yoffset"); _filter = g_strdup_printf( "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" inkscape:label=\"Drop shadow\" width=\"1.5\" height=\"1.5\" x=\"-.25\" y=\"-.25\">\n" - "<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"%f\" result=\"blur\"/>\n" - "<feColorMatrix result=\"bluralpha\" type=\"matrix\" values=\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 %f 0 \" />\n" - "<feOffset in=\"bluralpha\" dx=\"%f\" dy=\"%f\" result=\"offsetBlur\"/>\n" + "<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"%s\" result=\"blur\"/>\n" + "<feColorMatrix result=\"bluralpha\" type=\"matrix\" values=\"1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 %s 0 \" />\n" + "<feOffset in=\"bluralpha\" dx=\"%s\" dy=\"%s\" result=\"offsetBlur\"/>\n" "<feMerge>\n" "<feMergeNode in=\"offsetBlur\"/>\n" "<feMergeNode in=\"SourceGraphic\"/>\n" "</feMerge>\n" - "</filter>\n", blur, opacity, x, y); + "</filter>\n", blur.str().c_str(), opacity.str().c_str(), x.str().c_str(), y.str().c_str()); return _filter; }; @@ -112,21 +117,26 @@ DropGlow::get_filter_text (Inkscape::Extension::Extension * ext) { if (_filter != NULL) g_free((void *)_filter); - float blur = ext->get_param_float("blur"); - float opacity = ext->get_param_float("opacity") / 100; - float x = ext->get_param_float("xoffset"); - float y = ext->get_param_float("yoffset"); + std::ostringstream blur; + std::ostringstream opacity; + std::ostringstream x; + std::ostringstream y; + + blur << ext->get_param_float("blur"); + opacity << ext->get_param_float("opacity") / 100; + x << ext->get_param_float("xoffset"); + y << ext->get_param_float("yoffset"); _filter = g_strdup_printf( "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" inkscape:label=\"Drop shadow\" width=\"1.5\" height=\"1.5\" x=\"-.25\" y=\"-.25\">\n" - "<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"%f\" result=\"blur\"/>\n" - "<feColorMatrix result=\"bluralpha\" type=\"matrix\" values=\"-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 %f 0 \" />\n" - "<feOffset in=\"bluralpha\" dx=\"%f\" dy=\"%f\" result=\"offsetBlur\"/>\n" + "<feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"%s\" result=\"blur\"/>\n" + "<feColorMatrix result=\"bluralpha\" type=\"matrix\" values=\"-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 %s 0 \" />\n" + "<feOffset in=\"bluralpha\" dx=\"%s\" dy=\"%s\" result=\"offsetBlur\"/>\n" "<feMerge>\n" "<feMergeNode in=\"offsetBlur\"/>\n" "<feMergeNode in=\"SourceGraphic\"/>\n" "</feMerge>\n" - "</filter>\n", blur, opacity, x, y); + "</filter>\n", blur.str().c_str(), opacity.str().c_str(), x.str().c_str(), y.str().c_str()); return _filter; }; diff --git a/src/extension/internal/filter/snow.h b/src/extension/internal/filter/snow.h index cb5c55f57..2bb798cf1 100644 --- a/src/extension/internal/filter/snow.h +++ b/src/extension/internal/filter/snow.h @@ -51,12 +51,13 @@ Snow::get_filter_text (Inkscape::Extension::Extension * ext) { if (_filter != NULL) g_free((void *)_filter); - float drift = ext->get_param_float("drift"); + std::ostringstream drift; + drift << ext->get_param_float("drift"); _filter = g_strdup_printf( "<filter xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" inkscape:label=\"Snow\">\n" "<feConvolveMatrix order=\"3 3\" kernelMatrix=\"1 1 1 0 0 0 -1 -1 -1\" preserveAlpha=\"false\" divisor=\"3\"/>\n" - "<feMorphology operator=\"dilate\" radius=\"1 %f\"/>\n" + "<feMorphology operator=\"dilate\" radius=\"1 %s\"/>\n" "<feGaussianBlur stdDeviation=\"1.6270889487870621\" result=\"result0\"/>\n" "<feColorMatrix values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0\" result=\"result1\"/>\n" "<feOffset dx=\"0\" dy=\"1\" result=\"result5\"/>\n" @@ -67,7 +68,7 @@ Snow::get_filter_text (Inkscape::Extension::Extension * ext) "<feColorMatrix values=\"0.4 0 0 0 0.6 0 0.4 0 0 0.6 0 0 0 0 1 0 0 0 1 0\" result=\"result4\"/>\n" "<feComposite in2=\"result5\" in=\"result4\"/>\n" "<feComposite in2=\"SourceGraphic\"/>\n" - "</filter>\n", drift); + "</filter>\n", drift.str().c_str()); return _filter; }; diff --git a/src/extension/internal/grid.cpp b/src/extension/internal/grid.cpp index 39bca52d8..1593ffe79 100644 --- a/src/extension/internal/grid.cpp +++ b/src/extension/internal/grid.cpp @@ -120,7 +120,9 @@ Grid::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc Glib::ustring style("fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"); style += ";stroke-width:"; gchar floatstring[64]; - sprintf(floatstring, "%f", line_width); + std::ostringstream stringstream; + stringstream << line_width; + sprintf(floatstring, "%s", stringstream.str().c_str()); style += floatstring; style += "pt"; path->setAttribute("style", style.c_str()); diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp index c6c597b7a..00448b89e 100644 --- a/src/extension/internal/latex-text-renderer.cpp +++ b/src/extension/internal/latex-text-renderer.cpp @@ -51,7 +51,7 @@ namespace Internal { /** * This method is called by the PDF, EPS and PS output extensions. - * @param filename This should be the filename without extension to which the tex code should be written. Output goes to <filename>.tex. + * @param filename This should be the filename without '_tex' extension to which the tex code should be written. Output goes to <filename>_tex, note the underscore instead of period. */ bool latex_render_document_text_to_file( SPDocument *doc, gchar const *filename, @@ -132,7 +132,7 @@ LaTeXTextRenderer::setTargetFile(gchar const *filename) { _filename = g_path_get_basename(filename); - gchar *filename_ext = g_strdup_printf("%s.tex", filename); + gchar *filename_ext = g_strdup_printf("%s_tex", filename); Inkscape::IO::dump_fopen_call(filename_ext, "K"); FILE *osf = Inkscape::IO::fopen_utf8name(filename_ext, "w+"); if (!osf) { @@ -176,14 +176,26 @@ LaTeXTextRenderer::setTargetFile(gchar const *filename) { static char const preamble[] = "%% To include the image in your LaTeX document, write\n" -"%% \\input{<filename>.tex}\n" +"%% \\input{<filename>.pdf_tex}\n" "%% instead of\n" "%% \\includegraphics{<filename>.pdf}\n" "%% To scale the image, write\n" -"%% \\def{\\svgwidth}{<desired width>}\n" -"%% \\input{<filename>.tex}\n" +"%% \\def\\svgwidth{<desired width>}\n" +"%% \\input{<filename>.pdf_tex}\n" "%% instead of\n" "%% \\includegraphics[width=<desired width>]{<filename>.pdf}\n" +"%%\n" +"%% Images with a different path to the parent latex file can\n" +"%% be accessed with the `import' package (which may need to be\n" +"%% installed) using\n" +"%% \\usepackage{import}\n" +"%% in the preamble, and then including the image with\n" +"%% \\import{<path to file>}{<filename>.pdf_tex}\n" +"%% Alternatively, one can specify\n" +"%% \\graphicspath{{<path to file>/}}\n" +"%% \n" +"%% For more information, please see info/svg-inkscape on CTAN:\n" +"%% http://tug.ctan.org/tex-archive/info/svg-inkscape\n" "\n" "\\begingroup\n" " \\makeatletter\n" diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index c95c1b2c5..974a13b5f 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -40,6 +40,7 @@ #include "svg/css-ostringstream.h" #include "preferences.h" +#define noSP_GR_VERBOSE // Terminology: // @@ -52,9 +53,11 @@ static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *gr); -SPGradient * -sp_gradient_ensure_vector_normalized(SPGradient *gr) +SPGradient *sp_gradient_ensure_vector_normalized(SPGradient *gr) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_ensure_vector_normalized(%p)", gr); +#endif g_return_val_if_fail(gr != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); @@ -67,9 +70,9 @@ sp_gradient_ensure_vector_normalized(SPGradient *gr) } /* First make sure we have vector directly defined (i.e. gr has its own stops) */ - if (!gr->has_stops) { + if ( !gr->hasStops() ) { /* We do not have stops ourselves, so flatten stops as well */ - sp_gradient_ensure_vector(gr); + gr->ensureVector(); g_assert(gr->vector.built); // this adds stops from gr->vector as children to gr sp_gradient_repr_write_vector (gr); @@ -91,13 +94,16 @@ sp_gradient_ensure_vector_normalized(SPGradient *gr) * Creates new private gradient for the given vector */ -static SPGradient * -sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type) +static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_get_private_normalized(%p, %p, %d)", document, vector, type); +#endif + g_return_val_if_fail(document != NULL, NULL); g_return_val_if_fail(vector != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(vector), NULL); - g_return_val_if_fail(SP_GRADIENT_HAS_STOPS(vector), NULL); + g_return_val_if_fail(vector->hasStops(), NULL); SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document); @@ -131,8 +137,7 @@ sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPG /** Count how many times gr is used by the styles of o and its descendants */ -guint -count_gradient_hrefs(SPObject *o, SPGradient *gr) +guint count_gradient_hrefs(SPObject *o, SPGradient *gr) { if (!o) return 1; @@ -167,18 +172,21 @@ count_gradient_hrefs(SPObject *o, SPGradient *gr) /** * If gr has other users, create a new private; also check if gr links to vector, relink if not */ -SPGradient * -sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, - SPGradientType type, SPObject *o) +SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, + SPGradientType type, SPObject *o) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_fork_private_if_necessary(%p, %p, %d, %p)", gr, vector, type, o); +#endif g_return_val_if_fail(gr != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); // Orphaned gradient, no vector with stops at the end of the line; this used to be an assert // but i think we should not abort on this - maybe just write a validity warning into some sort // of log - if (!vector || !SP_GRADIENT_HAS_STOPS(vector)) + if ( !vector || !vector->hasStops() ) { return (gr); + } // user is the object that uses this gradient; normally it's item but for tspans, we // check its ancestor text so that tspans don't get different gradients from their @@ -202,7 +210,7 @@ sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, SPDocument *doc = SP_OBJECT_DOCUMENT(gr); SPObject *defs = SP_DOCUMENT_DEFS(doc); - if ((gr->has_stops) || + if ((gr->hasStops()) || (gr->state != SP_GRADIENT_STATE_UNKNOWN) || (SP_OBJECT_PARENT(gr) != SP_OBJECT(defs)) || (SP_OBJECT_HREFCOUNT(gr) > 1)) { @@ -236,9 +244,11 @@ sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, } } -SPGradient * -sp_gradient_fork_vector_if_necessary (SPGradient *gr) +SPGradient *sp_gradient_fork_vector_if_necessary(SPGradient *gr) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_fork_vector_if_necessary(%p)", gr); +#endif // Some people actually prefer their gradient vectors to be shared... Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (!prefs->getBool("/options/forkgradientvectors/value", true)) @@ -261,9 +271,11 @@ sp_gradient_fork_vector_if_necessary (SPGradient *gr) /** * Obtain the vector from the gradient. A forked vector will be created and linked to this gradient if another gradient uses it. */ -SPGradient * -sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector) +SPGradient *sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_get_forked_vector_if_necessary(%p, %d)", gradient, force_vector); +#endif SPGradient *vector = gradient->getVector(force_vector); vector = sp_gradient_fork_vector_if_necessary (vector); if ( gradient != vector && gradient->ref->getObject() != vector ) { @@ -278,9 +290,11 @@ sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vect * instead. No forking or reapplying is done because this is only called for newly created privates. * @return The new gradient. */ -SPGradient * -sp_gradient_reset_to_userspace (SPGradient *gr, SPItem *item) +SPGradient *sp_gradient_reset_to_userspace(SPGradient *gr, SPItem *item) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_reset_to_userspace(%p, %p)", gr, item); +#endif Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr); // calculate the bbox of the item @@ -330,16 +344,22 @@ sp_gradient_reset_to_userspace (SPGradient *gr, SPItem *item) * Convert an item's gradient to userspace if necessary, also fork it if necessary. * @return The new gradient. */ -SPGradient * -sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property) +SPGradient *sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_convert_to_userspace(%p, %p, \"%s\")", gr, item, property); +#endif g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); + if ( gr && gr->isSolid() ) { + return gr; + } + // First, fork it if it is shared gr = sp_gradient_fork_private_if_necessary(gr, gr->getVector(), SP_IS_RADIALGRADIENT(gr) ? SP_GRADIENT_TYPE_RADIAL : SP_GRADIENT_TYPE_LINEAR, SP_OBJECT(item)); - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr); @@ -442,9 +462,11 @@ sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *prop return gr; } -void -sp_gradient_transform_multiply(SPGradient *gradient, Geom::Matrix postmul, bool set) +void sp_gradient_transform_multiply(SPGradient *gradient, Geom::Matrix postmul, bool set) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_transform_multiply(%p, , %d)", gradient, set); +#endif if (set) { gradient->gradientTransform = postmul; } else { @@ -457,24 +479,23 @@ sp_gradient_transform_multiply(SPGradient *gradient, Geom::Matrix postmul, bool g_free(c); } -SPGradient * -sp_item_gradient (SPItem *item, bool fill_or_stroke) +SPGradient *sp_item_gradient(SPItem *item, bool fill_or_stroke) { - SPStyle *style = SP_OBJECT_STYLE (item); - SPGradient *gradient = NULL; + SPStyle *style = item->style; + SPGradient *gradient = 0; if (fill_or_stroke) { if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_GRADIENT (server)) { - gradient = SP_GRADIENT (server); + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { + gradient = SP_GRADIENT(server); } } } else { if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_GRADIENT (server)) { - gradient = SP_GRADIENT (server); + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { + gradient = SP_GRADIENT(server); } } } @@ -482,7 +503,7 @@ sp_item_gradient (SPItem *item, bool fill_or_stroke) return gradient; } -SPStop* sp_last_stop(SPGradient *gradient) +SPStop *sp_last_stop(SPGradient *gradient) { for (SPStop *stop = gradient->getFirstStop(); stop != NULL; stop = stop->getNextStop()) { if (stop->getNextStop() == NULL) @@ -491,8 +512,7 @@ SPStop* sp_last_stop(SPGradient *gradient) return NULL; } -SPStop* -sp_get_stop_i(SPGradient *gradient, guint stop_i) +SPStop *sp_get_stop_i(SPGradient *gradient, guint stop_i) { SPStop *stop = gradient->getFirstStop(); @@ -512,20 +532,22 @@ sp_get_stop_i(SPGradient *gradient, guint stop_i) return stop; } -guint32 -average_color (guint32 c1, guint32 c2, gdouble p) +guint32 average_color(guint32 c1, guint32 c2, gdouble p) { guint32 r = (guint32) (SP_RGBA32_R_U (c1) * (1 - p) + SP_RGBA32_R_U (c2) * p); guint32 g = (guint32) (SP_RGBA32_G_U (c1) * (1 - p) + SP_RGBA32_G_U (c2) * p); guint32 b = (guint32) (SP_RGBA32_B_U (c1) * (1 - p) + SP_RGBA32_B_U (c2) * p); guint32 a = (guint32) (SP_RGBA32_A_U (c1) * (1 - p) + SP_RGBA32_A_U (c2) * p); - return SP_RGBA32_U_COMPOSE (r, g, b, a); + return SP_RGBA32_U_COMPOSE(r, g, b, a); } -SPStop * -sp_vector_add_stop (SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gfloat offset) +SPStop *sp_vector_add_stop(SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gfloat offset) { +#ifdef SP_GR_VERBOSE + g_message("sp_vector_add_stop(%p, %p, %p, %f)", vector, prev_stop, next_stop, offset); +#endif + Inkscape::XML::Node *new_stop_repr = NULL; new_stop_repr = SP_OBJECT_REPR(prev_stop)->duplicate(SP_OBJECT_REPR(vector)->document()); SP_OBJECT_REPR(vector)->addChild(new_stop_repr, SP_OBJECT_REPR(prev_stop)); @@ -547,8 +569,7 @@ sp_vector_add_stop (SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gf return newstop; } -void -sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) +void sp_item_gradient_edit_stop(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); @@ -588,8 +609,7 @@ sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool } } -guint32 -sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) +guint32 sp_item_gradient_stop_query_style(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); @@ -641,9 +661,11 @@ sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i return 0; } -void -sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke, SPCSSAttr *stop) +void sp_item_gradient_stop_set_style(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke, SPCSSAttr *stop) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_stop_set_style(%p, %d, %d, %d, %p)", item, point_type, point_i, fill_or_stroke, stop); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); if (!gradient || !SP_IS_GRADIENT(gradient)) @@ -698,9 +720,11 @@ sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, } } -void -sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke) +void sp_item_gradient_reverse_vector(SPItem *item, bool fill_or_stroke) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_reverse_vector(%p, %d)", item, fill_or_stroke); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); if (!gradient || !SP_IS_GRADIENT(gradient)) return; @@ -756,9 +780,11 @@ sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke) Set the position of point point_type of the gradient applied to item (either fill_or_stroke) to p_w (in desktop coordinates). Write_repr if you want the change to become permanent. */ -void -sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom::Point p_w, bool fill_or_stroke, bool write_repr, bool scale) +void sp_item_gradient_set_coords(SPItem *item, guint point_type, guint point_i, Geom::Point p_w, bool fill_or_stroke, bool write_repr, bool scale) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_set_coords(%p, %d, %d, ...)", item, point_type, point_i ); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); if (!gradient || !SP_IS_GRADIENT(gradient)) @@ -817,7 +843,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom // using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end. double offset = get_offset_between_points (p, Geom::Point(lg->x1.computed, lg->y1.computed), Geom::Point(lg->x2.computed, lg->y2.computed)); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (lg, false); - sp_gradient_ensure_vector(lg); + lg->ensureVector(); lg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; @@ -911,7 +937,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom Geom::Point end = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed); double offset = get_offset_between_points (p, start, end); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (rg, false); - sp_gradient_ensure_vector(rg); + rg->ensureVector(); rg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; @@ -927,7 +953,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom Geom::Point end = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed); double offset = get_offset_between_points (p, start, end); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(rg, false); - sp_gradient_ensure_vector(rg); + rg->ensureVector(); rg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; @@ -953,8 +979,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom } } -SPGradient * -sp_item_gradient_get_vector (SPItem *item, bool fill_or_stroke) +SPGradient *sp_item_gradient_get_vector(SPItem *item, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); @@ -964,14 +989,15 @@ sp_item_gradient_get_vector (SPItem *item, bool fill_or_stroke) return NULL; } -SPGradientSpread -sp_item_gradient_get_spread (SPItem *item, bool fill_or_stroke) +SPGradientSpread sp_item_gradient_get_spread(SPItem *item, bool fill_or_stroke) { + SPGradientSpread spread = SP_GRADIENT_SPREAD_PAD; SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); - if (gradient) - return sp_gradient_get_spread (gradient); - return SP_GRADIENT_SPREAD_PAD; + if (gradient) { + spread = gradient->fetchSpread(); + } + return spread; } @@ -980,9 +1006,11 @@ Returns the position of point point_type of the gradient applied to item (either in desktop coordinates. */ -Geom::Point -sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) +Geom::Point sp_item_gradient_get_coords(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_get_coords(%p, %d, %d, %d)", item, point_type, point_i, fill_or_stroke); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); Geom::Point p (0, 0); @@ -1036,7 +1064,7 @@ sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool } } - if (SP_GRADIENT(gradient)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (SP_GRADIENT(gradient)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item)); Geom::OptRect bbox = item->getBounds(Geom::identity()); // we need "true" bbox without item_i2d_affine if (bbox) { @@ -1056,9 +1084,11 @@ sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool * gr has to be a normalized vector. */ -SPGradient * -sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_fill) +SPGradient *sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_fill) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_set_gradient(%p, %p, %d, %d)", item, gr, type, is_fill); +#endif g_return_val_if_fail(item != NULL, NULL); g_return_val_if_fail(SP_IS_ITEM(item), NULL); g_return_val_if_fail(gr != NULL, NULL); @@ -1127,6 +1157,9 @@ sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_ static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *link) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_repr_set_link(%p, %p)", repr, link); +#endif g_return_if_fail(repr != NULL); if (link) { g_return_if_fail(SP_IS_GRADIENT(link)); @@ -1141,59 +1174,54 @@ static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *lin } } -/* - * Get default normalized gradient vector of document, create if there is none - */ -SPGradient * -sp_document_default_gradient_vector(SPDocument *document, guint32 color) +static void addStop( Inkscape::XML::Node *parent, Glib::ustring const &color, gint opacity, gchar const *offset ) { - SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document); - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); - - Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient"); - - repr->setAttribute("inkscape:collect", "always"); - // set here, but removed when it's edited in the gradient editor - // to further reduce clutter, we could - // (1) here, search gradients by color and return what is found without duplication - // (2) in fill & stroke, show only one copy of each gradient in list - - Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop"); - - gchar b[64]; - sp_svg_write_color(b, sizeof(b), color); - +#ifdef SP_GR_VERBOSE + g_message("addStop(%p, %s, %d, %s)", parent, color.c_str(), opacity, offset); +#endif + Inkscape::XML::Node *stop = parent->document()->createElement("svg:stop"); { - gchar *t = g_strdup_printf("stop-color:%s;stop-opacity:1;", b); - stop->setAttribute("style", t); - g_free(t); + gchar *tmp = g_strdup_printf( "stop-color:%s;stop-opacity:%d;", color.c_str(), opacity ); + stop->setAttribute( "style", tmp ); + g_free(tmp); } - stop->setAttribute("offset", "0"); + stop->setAttribute( "offset", offset ); - repr->appendChild(stop); + parent->appendChild(stop); Inkscape::GC::release(stop); +} - stop = xml_doc->createElement("svg:stop"); +/* + * Get default normalized gradient vector of document, create if there is none + */ +SPGradient *sp_document_default_gradient_vector( SPDocument *document, SPColor const &color, bool singleStop ) +{ + SPDefs *defs = static_cast<SPDefs *>(SP_DOCUMENT_DEFS(document)); + Inkscape::XML::Document *xml_doc = document->rdoc; - { - gchar *t = g_strdup_printf("stop-color:%s;stop-opacity:0;", b); - stop->setAttribute("style", t); - g_free(t); - } + Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient"); - stop->setAttribute("offset", "1"); + if ( !singleStop ) { + repr->setAttribute("inkscape:collect", "always"); + // set here, but removed when it's edited in the gradient editor + // to further reduce clutter, we could + // (1) here, search gradients by color and return what is found without duplication + // (2) in fill & stroke, show only one copy of each gradient in list + } - repr->appendChild(stop); - Inkscape::GC::release(stop); + Glib::ustring colorStr = color.toString(); + addStop( repr, colorStr, 1, "0" ); + if ( !singleStop ) { + addStop( repr, colorStr, 0, "1" ); + } SP_OBJECT_REPR(defs)->addChild(repr, NULL); Inkscape::GC::release(repr); /* fixme: This does not look like nice */ - SPGradient *gr; - gr = (SPGradient *) document->getObjectByRepr(repr); + SPGradient *gr = static_cast<SPGradient *>(document->getObjectByRepr(repr)); g_assert(gr != NULL); g_assert(SP_IS_GRADIENT(gr)); /* fixme: Maybe add extra sanity check here */ @@ -1206,13 +1234,12 @@ sp_document_default_gradient_vector(SPDocument *document, guint32 color) Return the preferred vector for \a o, made from (in order of preference) its current vector, current fill or stroke color, or from desktop style if \a o is NULL or doesn't have style. */ -SPGradient * -sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop, - SPObject *const o, bool const is_fill) +SPGradient *sp_gradient_vector_for_object( SPDocument *const doc, SPDesktop *const desktop, + SPObject *const o, bool const is_fill, bool singleStop ) { - guint32 rgba = 0; + SPColor color; if (o == NULL || SP_OBJECT_STYLE(o) == NULL) { - rgba = sp_desktop_get_color(desktop, is_fill); + color = sp_desktop_get_color(desktop, is_fill); } else { // take the color of the object SPStyle const &style = *SP_OBJECT_STYLE(o); @@ -1220,21 +1247,21 @@ sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop, ? style.fill : style.stroke ); if (paint.isPaintserver()) { - SPObject *server = is_fill? SP_OBJECT_STYLE_FILL_SERVER(o) : SP_OBJECT_STYLE_STROKE_SERVER(o); - if (SP_IS_GRADIENT (server)) { + SPObject *server = is_fill? o->style->getFillPaintServer() : o->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { return SP_GRADIENT(server)->getVector(true); } else { - rgba = sp_desktop_get_color(desktop, is_fill); + color = sp_desktop_get_color(desktop, is_fill); } } else if (paint.isColor()) { - rgba = paint.value.color.toRGBA32( 0xff ); + color = paint.value.color; } else { // if o doesn't use flat color, then take current color of the desktop. - rgba = sp_desktop_get_color(desktop, is_fill); + color = sp_desktop_get_color(desktop, is_fill); } } - return sp_document_default_gradient_vector(doc, rgba); + return sp_document_default_gradient_vector( doc, color, singleStop ); } diff --git a/src/gradient-chemistry.h b/src/gradient-chemistry.h index 3f72fa394..0c8d0afe7 100644 --- a/src/gradient-chemistry.h +++ b/src/gradient-chemistry.h @@ -42,9 +42,8 @@ SPGradient *sp_item_set_gradient (SPItem *item, SPGradient *gr, SPGradientType t /* * Get default normalized gradient vector of document, create if there is none */ - -SPGradient *sp_document_default_gradient_vector (SPDocument *document, guint32 color = 0); -SPGradient *sp_gradient_vector_for_object (SPDocument *doc, SPDesktop *desktop, SPObject *o, bool is_fill); +SPGradient *sp_document_default_gradient_vector( SPDocument *document, SPColor const &color, bool singleStop ); +SPGradient *sp_gradient_vector_for_object( SPDocument *doc, SPDesktop *desktop, SPObject *o, bool is_fill, bool singleStop = false ); void sp_object_ensure_fill_gradient_normalized (SPObject *object); void sp_object_ensure_stroke_gradient_normalized (SPObject *object); diff --git a/src/gradient-context.cpp b/src/gradient-context.cpp index ddb153ffd..bf1566b26 100644 --- a/src/gradient-context.cpp +++ b/src/gradient-context.cpp @@ -1,5 +1,3 @@ -#define __SP_GRADIENT_CONTEXT_C__ - /* * Gradient drawing and editing tool * @@ -381,7 +379,7 @@ sp_gradient_context_add_stops_between_selected_stops (SPGradientContext *rc) if (SP_IS_GRADIENT (parent)) { doc = SP_OBJECT_DOCUMENT (parent); sp_vector_add_stop (SP_GRADIENT (parent), this_stop, next_stop, offset); - sp_gradient_ensure_vector (SP_GRADIENT (parent)); + SP_GRADIENT(parent)->ensureVector(); } } diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index 11de93d68..1492e9008 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -1,5 +1,3 @@ -#define __GRADIENT_DRAG_C__ - /* * On-canvas gradient dragging * @@ -178,41 +176,74 @@ gr_drag_style_query (SPStyle *style, int property, gpointer data) } } -bool -gr_drag_style_set (const SPCSSAttr *css, gpointer data) +bool GrDrag::styleSet( const SPCSSAttr *css ) { - GrDrag *drag = (GrDrag *) data; - - if (!drag->selected) + if (!selected) { return false; + } - SPCSSAttr *stop = sp_repr_css_attr_new (); + SPCSSAttr *stop = sp_repr_css_attr_new(); // See if the css contains interesting properties, and if so, translate them into the format // acceptable for gradient stops // any of color properties, in order of increasing priority: - if (css->attribute("flood-color")) + if (css->attribute("flood-color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("flood-color")); + } - if (css->attribute("lighting-color")) + if (css->attribute("lighting-color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("lighting-color")); + } - if (css->attribute("color")) + if (css->attribute("color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("color")); + } - if (css->attribute("stroke") && strcmp(css->attribute("stroke"), "none")) + if (css->attribute("stroke") && strcmp(css->attribute("stroke"), "none")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("stroke")); + } - if (css->attribute("fill") && strcmp(css->attribute("fill"), "none")) + if (css->attribute("fill") && strcmp(css->attribute("fill"), "none")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("fill")); + } - if (css->attribute("stop-color")) + if (css->attribute("stop-color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("stop-color")); + } + + // Make sure the style is allowed for gradient stops. + if ( !sp_repr_css_property_is_unset( stop, "stop-color") ) { + Glib::ustring tmp = sp_repr_css_property( stop, "stop-color", "" ); + Glib::ustring::size_type pos = tmp.find("url(#"); + if ( pos != Glib::ustring::npos ) { + Glib::ustring targetName = tmp.substr(pos + 5, tmp.length() - 6); + const GSList *gradients = sp_document_get_resource_list(desktop->doc(), "gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( targetName == grad->getId() ) { + SPGradient *vect = grad->getVector(); + SPStop *firstStop = (vect) ? vect->getFirstStop() : grad->getFirstStop(); + if (firstStop) { + Glib::ustring stopColorStr; + if (firstStop->currentColor) { + stopColorStr = sp_object_get_style_property(firstStop, "color", NULL); + } else { + stopColorStr = firstStop->specified_color.toString(); + } + if ( !stopColorStr.empty() ) { + sp_repr_css_set_property( stop, "stop-color", stopColorStr.c_str() ); + } + } + break; + } + } + } + } if (css->attribute("stop-opacity")) { // direct setting of stop-opacity has priority - sp_repr_css_set_property (stop, "stop-opacity", css->attribute("stop-opacity")); + sp_repr_css_set_property(stop, "stop-opacity", css->attribute("stop-opacity")); } else { // multiply all opacity properties: gdouble accumulated = 1.0; accumulated *= sp_svg_read_percentage(css->attribute("flood-opacity"), 1.0); @@ -222,11 +253,12 @@ gr_drag_style_set (const SPCSSAttr *css, gpointer data) Inkscape::CSSOStringStream os; os << accumulated; - sp_repr_css_set_property (stop, "stop-opacity", os.str().c_str()); + sp_repr_css_set_property(stop, "stop-opacity", os.str().c_str()); if ((css->attribute("fill") && !css->attribute("stroke") && !strcmp(css->attribute("fill"), "none")) || - (css->attribute("stroke") && !css->attribute("fill") && !strcmp(css->attribute("stroke"), "none"))) - sp_repr_css_set_property (stop, "stop-opacity", "0"); // if a single fill/stroke property is set to none, don't change color, set opacity to 0 + (css->attribute("stroke") && !css->attribute("fill") && !strcmp(css->attribute("stroke"), "none"))) { + sp_repr_css_set_property(stop, "stop-opacity", "0"); // if a single fill/stroke property is set to none, don't change color, set opacity to 0 + } } if (!stop->attributeList()) { // nothing for us here, pass it on @@ -234,13 +266,13 @@ gr_drag_style_set (const SPCSSAttr *css, gpointer data) return false; } - for (GList const* sel = drag->selected; sel != NULL; sel = sel->next) { // for all selected draggers - GrDragger* dragger = (GrDragger*) sel->data; + for (GList const* sel = selected; sel != NULL; sel = sel->next) { // for all selected draggers + GrDragger* dragger = reinterpret_cast<GrDragger*>(sel->data); for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger - GrDraggable *draggable = (GrDraggable *) i->data; + GrDraggable *draggable = reinterpret_cast<GrDraggable *>(i->data); - drag->local_change = true; - sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop); + local_change = true; + sp_item_gradient_stop_set_style(draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop); } } @@ -348,7 +380,7 @@ GrDrag::addStopNearPoint (SPItem *item, Geom::Point mouse_p, double tolerance) SPStop *newstop = sp_vector_add_stop (vector, prev_stop, next_stop, offset); - sp_gradient_ensure_vector (gradient); + gradient->ensureVector(); updateDraggers(); return newstop; @@ -404,51 +436,47 @@ GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p) } -GrDrag::GrDrag(SPDesktop *desktop) { - - this->desktop = desktop; - - this->selection = sp_desktop_selection(desktop); - - this->draggers = NULL; - this->lines = NULL; - this->selected = NULL; - - this->hor_levels.clear(); - this->vert_levels.clear(); - - this->local_change = false; - - this->sel_changed_connection = this->selection->connectChanged( - sigc::bind ( +GrDrag::GrDrag(SPDesktop *desktop) : + selected(0), + keep_selection(false), + local_change(false), + desktop(desktop), + hor_levels(), + vert_levels(), + draggers(0), + lines(0), + selection(sp_desktop_selection(desktop)), + sel_changed_connection(), + sel_modified_connection(), + style_set_connection(), + style_query_connection() +{ + sel_changed_connection = selection->connectChanged( + sigc::bind( sigc::ptr_fun(&gr_drag_sel_changed), (gpointer)this ) ); - this->sel_modified_connection = this->selection->connectModified( + sel_modified_connection = selection->connectModified( sigc::bind( sigc::ptr_fun(&gr_drag_sel_modified), (gpointer)this ) ); - this->style_set_connection = this->desktop->connectSetStyle( - sigc::bind( - sigc::ptr_fun(&gr_drag_style_set), - (gpointer)this ) - ); + style_set_connection = desktop->connectSetStyle( sigc::mem_fun(*this, &GrDrag::styleSet) ); - this->style_query_connection = this->desktop->connectQueryStyle( + style_query_connection = desktop->connectQueryStyle( sigc::bind( sigc::ptr_fun(&gr_drag_style_query), (gpointer)this ) ); - this->updateDraggers (); - this->updateLines (); - this->updateLevels (); + updateDraggers(); + updateLines(); + updateLevels(); if (desktop->gr_item) { - this->setSelected (getDraggerFor (desktop->gr_item, desktop->gr_point_type, desktop->gr_point_i, desktop->gr_fill_or_stroke)); + setSelected(getDraggerFor(desktop->gr_item, desktop->gr_point_type, desktop->gr_point_i, desktop->gr_fill_or_stroke)); } } @@ -503,17 +531,18 @@ GrDraggable::~GrDraggable () } -SPObject * -GrDraggable::getServer () +SPObject *GrDraggable::getServer() { - if (!item) + if (!item) { return NULL; + } SPObject *server = NULL; - if (fill_or_stroke) - server = SP_OBJECT_STYLE_FILL_SERVER (item); - else - server = SP_OBJECT_STYLE_STROKE_SERVER (item); + if (fill_or_stroke) { + server = item->style->getFillPaintServer(); + }else { + server = item->style->getStrokePaintServer(); + } return server; } @@ -659,7 +688,7 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0); } if (snap_vector) { - Inkscape::Snapper::ConstraintLine cl(dr_snap, p + *snap_vector - dr_snap); + Inkscape::Snapper::SnapConstraint cl(dr_snap, p + *snap_vector - dr_snap); Inkscape::SnappedPoint s = m.constrainedSnap(Inkscape::SnapCandidatePoint(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE), cl); if (s.getSnapped()) { s.setTransformation(s.getPoint() - p); @@ -809,9 +838,10 @@ gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, gu } else { p = snap_vector_midpoint (p, low_lim, high_lim, 0); if (!(state & GDK_SHIFT_MASK)) { + Inkscape::Snapper::SnapConstraint cl(low_lim, high_lim - low_lim); SPDesktop *desktop = dragger->parent->desktop; SnapManager &m = desktop->namedview->snap_manager; - Inkscape::Snapper::ConstraintLine cl(low_lim, high_lim - low_lim); + m.setup(desktop); m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE, cl); } } @@ -1623,41 +1653,43 @@ GrDrag::grabKnot (SPItem *item, gint point_type, gint point_i, bool fill_or_stro Regenerates the draggers list from the current selection; is called when selection is changed or modified, also when a radial dragger needs to update positions of other draggers in the gradient */ -void -GrDrag::updateDraggers () +void GrDrag::updateDraggers () { while (selected) { selected = g_list_remove(selected, selected->data); } // delete old draggers for (GList const* i = this->draggers; i != NULL; i = i->next) { - delete ((GrDragger *) i->data); + delete static_cast<GrDragger *>(i->data); } - g_list_free (this->draggers); + g_list_free(this->draggers); this->draggers = NULL; - g_return_if_fail (this->selection != NULL); + g_return_if_fail(this->selection != NULL); for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = SP_ITEM(i->data); - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { - addDraggersLinear (SP_LINEARGRADIENT (server), item, true); - } else if (SP_IS_RADIALGRADIENT (server)) { - addDraggersRadial (SP_RADIALGRADIENT (server), item, true); + SPPaintServer *server = style->getFillPaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { + addDraggersLinear( SP_LINEARGRADIENT(server), item, true ); + } else if ( SP_IS_RADIALGRADIENT(server) ) { + addDraggersRadial( SP_RADIALGRADIENT(server), item, true ); } } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { - addDraggersLinear (SP_LINEARGRADIENT (server), item, false); - } else if (SP_IS_RADIALGRADIENT (server)) { - addDraggersRadial (SP_RADIALGRADIENT (server), item, false); + SPPaintServer *server = style->getStrokePaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { + addDraggersLinear( SP_LINEARGRADIENT(server), item, false ); + } else if ( SP_IS_RADIALGRADIENT(server) ) { + addDraggersRadial( SP_RADIALGRADIENT(server), item, false ); } } } @@ -1702,10 +1734,12 @@ GrDrag::updateLines () SPStyle *style = SP_OBJECT_STYLE (item); if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, true), sp_item_gradient_get_coords (item, POINT_LG_END, 0, true), GR_LINE_COLOR_FILL); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { Geom::Point center = sp_item_gradient_get_coords (item, POINT_RG_CENTER, 0, true); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, true), GR_LINE_COLOR_FILL); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, true), GR_LINE_COLOR_FILL); @@ -1713,10 +1747,12 @@ GrDrag::updateLines () } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, false), sp_item_gradient_get_coords (item, POINT_LG_END, 0, false), GR_LINE_COLOR_STROKE); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { Geom::Point center = sp_item_gradient_get_coords (item, POINT_RG_CENTER, 0, false); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, false), GR_LINE_COLOR_STROKE); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, false), GR_LINE_COLOR_STROKE); diff --git a/src/gradient-drag.h b/src/gradient-drag.h index 460d1396e..22c0dbfb6 100644 --- a/src/gradient-drag.h +++ b/src/gradient-drag.h @@ -2,7 +2,7 @@ #define __GRADIENT_DRAG_H__ /* - * On-canvas gradient dragging + * On-canvas gradient dragging * * Authors: * bulia byak <bulia@users.sf.net> @@ -78,7 +78,7 @@ struct GrDragger { void updateKnotShape(); void updateTip(); - + void select(); void deselect(); bool isSelected(); @@ -131,9 +131,9 @@ public: // FIXME: make more of this private! void deleteSelected (bool just_one = false); guint32 getColor(); - - bool keep_selection; - + + bool keep_selection; + GrDragger *getDraggerFor (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke); void grabKnot (GrDragger *dragger, gint x, gint y, guint32 etime); @@ -165,7 +165,7 @@ public: // FIXME: make more of this private! void selected_reverse_vector (); -private: +private: void deselect_all(); void addLine (SPItem *item, Geom::Point p1, Geom::Point p2, guint32 rgba); @@ -175,6 +175,8 @@ private: void addDraggersRadial (SPRadialGradient *rg, SPItem *item, bool fill_or_stroke); void addDraggersLinear (SPLinearGradient *lg, SPItem *item, bool fill_or_stroke); + bool styleSet( const SPCSSAttr *css ); + Inkscape::Selection *selection; sigc::connection sel_changed_connection; sigc::connection sel_modified_connection; diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index 4f70521e0..aad4502ca 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -81,9 +81,10 @@ void Inkscape::GuideSnapper::_addSnappedLinesOrigin(SnappedConstraints &sc, Geom } -void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const +void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const { SnappedPoint dummy = SnappedPoint(snapped_point, source, source_num, Inkscape::SNAPTARGET_GUIDE, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), constrained_snap, true); + dummy.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating sc.points.push_back(dummy); } diff --git a/src/guide-snapper.h b/src/guide-snapper.h index 5de1b56a4..4e5e5d1d1 100644 --- a/src/guide-snapper.h +++ b/src/guide-snapper.h @@ -36,7 +36,7 @@ private: LineList _getSnapLines(Geom::Point const &p) const; void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, Geom::Point const normal_to_line, Geom::Point const point_on_line) const; void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; - void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; + void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const; }; } diff --git a/src/inkscape.cpp b/src/inkscape.cpp index d876be6ec..c10581a91 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -595,6 +595,8 @@ inkscape_crash_handler (int /*signum*/) strftime (sptstr, 256, "%Y_%m_%d_%H_%M_%S", sptm); gint count = 0; + gchar *curdir = g_get_current_dir(); // This one needs to be freed explicitly + gchar *inkscapedir = g_path_get_dirname(INKSCAPE->argv0); // Needs to be freed GSList *savednames = NULL; GSList *failednames = NULL; for (std::map<SPDocument*,int>::iterator iter = inkscape->document_set.begin(); @@ -604,55 +606,57 @@ inkscape_crash_handler (int /*signum*/) Inkscape::XML::Node *repr; repr = sp_document_repr_root (doc); if (doc->isModifiedSinceSave()) { - const gchar *docname, *d0, *d; - gchar n[64], c[1024]; - FILE *file; + const gchar *docname; /* originally, the document name was retrieved from * the sodipod:docname attribute */ docname = doc->name; if (docname) { - /* fixme: Quick hack to remove emergency file suffix */ - d0 = strrchr ((char*)docname, '.'); + /* Removes an emergency save suffix if present: /(.*)\.[0-9_]*\.[0-9_]*\.[~\.]*$/\1/ */ + const char* d0 = strrchr ((char*)docname, '.'); if (d0 && (d0 > docname)) { - d0 = strrchr ((char*)(d0 - 1), '.'); - if (d0 && (d0 > docname)) { - d = d0; - while (isdigit (*d) || (*d == '.') || (*d == '_')) d += 1; - if (*d) { - memcpy (n, docname, MIN (d0 - docname - 1, 64)); - n[63] = '\0'; - docname = n; - } + const char* d = d0; + unsigned int dots = 0; + while ((isdigit (*d) || *d=='_' || *d=='.') && d>docname && dots<2) { + d -= 1; + if (*d=='.') dots++; + } + if (*d=='.' && d>docname && dots==2) { + char n[64]; + size_t len = MIN (d - docname, 63); + memcpy (n, docname, len); + n[len] = '\0'; + docname = n; } } } - if (!docname || !*docname) docname = "emergency"; - // try saving to the profile location + + // Emergency filename + char c[1024]; g_snprintf (c, 1024, "%.256s.%s.%d.svg", docname, sptstr, count); - gchar * location = homedir_path(c); - Inkscape::IO::dump_fopen_call(location, "E"); - file = Inkscape::IO::fopen_utf8name(location, "w"); - g_snprintf (c, 1024, "%s", location); // we want the complete path to be stored in c (for reporting purposes) - g_free(location); - if (!file) { - // try saving to /tmp - g_snprintf (c, 1024, "/tmp/inkscape-%.256s.%s.%d.svg", docname, sptstr, count); - Inkscape::IO::dump_fopen_call(c, "G"); - file = Inkscape::IO::fopen_utf8name(c, "w"); - } - if (!file) { - // try saving to the current directory - gchar *curdir = g_get_current_dir(); - g_snprintf (c, 1024, "inkscape-%.256s.%s.%d.svg", docname, sptstr, count); - Inkscape::IO::dump_fopen_call(c, "F"); - file = Inkscape::IO::fopen_utf8name(c, "w"); - // store the complete path in c so that it can be reported later - gchar * location = g_build_filename(curdir, c, NULL); - g_snprintf (c, 1024, "%s", location); - g_free(location); + + // Find a location + const char* locations[] = { + doc->base, + g_get_home_dir(), + g_get_tmp_dir(), + curdir, + inkscapedir + }; + FILE *file = 0; + for(size_t i=0; i<sizeof(locations)/sizeof(*locations); i++) { + if (!locations[i]) continue; // It seems to be okay, but just in case + gchar * filename = g_build_filename(locations[i], c, NULL); + Inkscape::IO::dump_fopen_call(filename, "E"); + file = Inkscape::IO::fopen_utf8name(filename, "w"); + if (file) { + g_snprintf (c, 1024, "%s", filename); // we want the complete path to be stored in c (for reporting purposes) + break; + } } + + // Save if (file) { sp_repr_save_stream (repr->document(), file, SP_SVG_NS_URI); savednames = g_slist_prepend (savednames, g_strdup (c)); @@ -663,6 +667,8 @@ inkscape_crash_handler (int /*signum*/) count++; } } + g_free(curdir); + g_free(inkscapedir); savednames = g_slist_reverse (savednames); failednames = g_slist_reverse (failednames); diff --git a/src/interface.cpp b/src/interface.cpp index 1a6da5635..b33443d1b 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -53,6 +53,7 @@ #include "io/sys.h" #include "dialogs/dialog-events.h" #include "message-context.h" +#include "ui/uxmanager.h" // Added for color drag-n-drop #if ENABLE_LCMS @@ -634,20 +635,28 @@ sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape:: } // end of sp_ui_menu_append_item_from_verb -static void -checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data) +static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ) { - gchar const *pref = (gchar const *) user_data; - Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + Glib::ustring prefPath; - Glib::ustring pref_path; if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) { - pref_path = "/focus/"; + prefPath = "/focus/"; } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) { - pref_path = "/fullscreen/"; + prefPath = "/fullscreen/"; } else { - pref_path = "/window/"; + prefPath = "/window/"; } + + return prefPath; +} + +static void +checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data) +{ + gchar const *pref = (gchar const *) user_data; + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + + Glib::ustring pref_path = getLayoutPrefPath( view ); pref_path += pref; pref_path += "/state"; @@ -666,16 +675,12 @@ checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_dat gchar const *pref = (gchar const *) user_data; Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); - Glib::ustring pref_path; - if ((static_cast<SPDesktop*>(view))->is_fullscreen()) { - pref_path = "/fullscreen/"; - } else { - pref_path = "/window/"; - } + Glib::ustring pref_path = getLayoutPrefPath( view ); pref_path += pref; + pref_path += "/state"; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool ison = prefs->getBool(pref_path + "/state", true); + bool ison = prefs->getBool(pref_path, true); g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data); gtk_check_menu_item_set_active(menuitem, ison); @@ -684,6 +689,20 @@ checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_dat return FALSE; } +static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData) +{ + if ( gtk_check_menu_item_get_active(menuitem) ) { + gint taskNum = GPOINTER_TO_INT(userData); + taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum; + + Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view"); + + // note: this will change once more options are in the task set support: + Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum ); + } +} + + /** * \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline) */ @@ -731,15 +750,9 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View * gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data), Inkscape::Verb *verb) { - GtkWidget *item; - - unsigned int shortcut = 0; - SPAction *action = NULL; - - if (verb) { - shortcut = sp_shortcut_get_primary(verb); - action = verb->get_action(view); - } + unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0; + SPAction *action = (verb) ? verb->get_action(view) : 0; + GtkWidget *item = gtk_check_menu_item_new(); if (verb && shortcut) { gchar c[256]; @@ -761,12 +774,10 @@ sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View * gtk_widget_show_all(hb); - item = gtk_check_menu_item_new(); gtk_container_add((GtkContainer *) item, hb); } else { GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label); gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5); - item = gtk_check_menu_item_new(); gtk_container_add((GtkContainer *) item, l); } #if 0 @@ -887,6 +898,39 @@ sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) checkitem_toggled, checkitem_update, 0); } + +void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view) +{ + gchar const* data[] = { + _("Default"), _("Default interface setup"), + _("Custom"), _("Set the custom task"), + _("Wide"), _("Setup for widescreen work."), + 0, 0 + }; + + GSList *group = 0; + int count = 0; + gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) ); + for (gchar const **strs = data; strs[0]; strs += 2, count++) + { + GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] ); + group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) ); + if ( count == active ) + { + gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE ); + } + + g_object_set_data( G_OBJECT(item), "view", view ); + g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) ); + g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) ); + g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 ); + + gtk_widget_show( item ); + gtk_menu_shell_append( GTK_MENU_SHELL(menu), item ); + } +} + + /** @brief Observer that updates the recent list's max document count */ class MaxRecentObserver : public Inkscape::Preferences::Observer { public: @@ -1012,6 +1056,10 @@ sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI: sp_ui_checkboxes_menus(GTK_MENU(menu), view); continue; } + if (!strcmp(menu_pntr->name(), "task-checkboxes")) { + addTaskMenuItems(GTK_MENU(menu), view); + continue; + } } } @@ -1319,7 +1367,7 @@ sp_ui_drag_data_received(GtkWidget *widget, for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); if ( color.descr == grad->getId() ) { - if ( grad->has_stops ) { + if ( grad->hasStops() ) { matches = grad; break; } diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index 2d0d5eb02..be61125c4 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -101,7 +101,7 @@ KnotHolderEntity::snap_knot_position(Geom::Point const &p) } Geom::Point -KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint) +KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint) { Geom::Matrix const i2d (sp_item_i2d_affine(item)); Geom::Point s = p * i2d; @@ -123,7 +123,7 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape: } else { // constrainedSnap() will first project the point p onto the constraint line and then try to snap along that line. // This way the constraint is already enforced, no need to worry about that later on - Inkscape::Snapper::ConstraintLine transformed_constraint = Inkscape::Snapper::ConstraintLine(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d); + Inkscape::Snapper::SnapConstraint transformed_constraint = Inkscape::Snapper::SnapConstraint(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d); m.constrainedSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE, transformed_constraint); } diff --git a/src/knot-holder-entity.h b/src/knot-holder-entity.h index c8fd29ddf..aba93798a 100644 --- a/src/knot-holder-entity.h +++ b/src/knot-holder-entity.h @@ -58,7 +58,7 @@ public: //private: Geom::Point snap_knot_position(Geom::Point const &p); - Geom::Point snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint); + Geom::Point snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint); SPKnot *knot; SPItem *item; diff --git a/src/libnr/nr-compose-transform.cpp b/src/libnr/nr-compose-transform.cpp index e7c286569..05852bf07 100644 --- a/src/libnr/nr-compose-transform.cpp +++ b/src/libnr/nr-compose-transform.cpp @@ -65,11 +65,14 @@ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int x, y; if (alpha == 0) return; + if (alpha>255) { + g_warning("In transform PPN alpha=%u>255",alpha); + } - // Both alpha and color components are stored temporarily with a range of [0,255^2], so more supersampling and we get an overflow - if (xd+yd>16) { - xd = 8; - yd = 8; + // The color component is stored temporarily with a range of [0,255^3], so more supersampling and we get an overflow (fortunately Inkscape's preferences also doesn't allow a higher setting) + if (xd+yd>8) { + xd = 4; + yd = 4; } xsize = (1 << xd); @@ -122,38 +125,42 @@ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, if ((sy >= 0) && (sy < sh)) { const unsigned char *s; s = spx + sy * srs + sx * 4; - r += NR_PREMUL_112 (s[0], s[3]); + r += NR_PREMUL_112 (s[0], s[3]); // s in [0,255] g += NR_PREMUL_112 (s[1], s[3]); b += NR_PREMUL_112 (s[2], s[3]); a += s[3]; + // a=sum(s3) + // r,g,b in [0,sum(s3)*255] } } } a = (a*alpha + rounding_fix) >> dbits; + // a=sum(s3)*alpha/size=avg(s3)*alpha // Compare to nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P if (a != 0) { - r = (r + rounding_fix) >> dbits; - g = (g + rounding_fix) >> dbits; - b = (b + rounding_fix) >> dbits; + r = (r*alpha + rounding_fix) >> dbits; + g = (g*alpha + rounding_fix) >> dbits; + b = (b*alpha + rounding_fix) >> dbits; + // r,g,b in [0,avg(s3)*alpha*255]=[0,a*255] if (a == 255*255) { /* Full coverage, demul src */ - d[0] = NR_NORMALIZE_21(r); - d[1] = NR_NORMALIZE_21(g); - d[2] = NR_NORMALIZE_21(b); + d[0] = NR_NORMALIZE_31(r); + d[1] = NR_NORMALIZE_31(g); + d[2] = NR_NORMALIZE_31(b); d[3] = NR_NORMALIZE_21(a); } else if (d[3] == 0) { /* Only foreground, demul src */ - d[0] = NR_DEMUL_221(r,a); - d[1] = NR_DEMUL_221(g,a); - d[2] = NR_DEMUL_221(b,a); + d[0] = NR_DEMUL_321(r,a); + d[1] = NR_DEMUL_321(g,a); + d[2] = NR_DEMUL_321(b,a); d[3] = NR_NORMALIZE_21(a); } else { unsigned int ca; /* Full composition */ ca = NR_COMPOSEA_213(a, d[3]); - d[0] = NR_COMPOSEPNN_221131 (r, a, d[0], d[3], ca); - d[1] = NR_COMPOSEPNN_221131 (g, a, d[1], d[3], ca); - d[2] = NR_COMPOSEPNN_221131 (b, a, d[2], d[3], ca); + d[0] = NR_COMPOSEPNN_321131 (r, a, d[0], d[3], ca); + d[1] = NR_COMPOSEPNN_321131 (g, a, d[1], d[3], ca); + d[2] = NR_COMPOSEPNN_321131 (b, a, d[2], d[3], ca); d[3] = NR_NORMALIZE_31(ca); } } @@ -272,19 +279,19 @@ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h } a = (a*alpha + rounding_fix) >> dbits; if (a != 0) { - r = (r + rounding_fix) >> dbits; - g = (g + rounding_fix) >> dbits; - b = (b + rounding_fix) >> dbits; + r = (r*alpha + rounding_fix) >> dbits; + g = (g*alpha + rounding_fix) >> dbits; + b = (b*alpha + rounding_fix) >> dbits; if ((a == 255*255) || (d[3] == 0)) { /* Transparent BG, premul src */ - d[0] = NR_NORMALIZE_21(r); - d[1] = NR_NORMALIZE_21(g); - d[2] = NR_NORMALIZE_21(b); + d[0] = NR_NORMALIZE_31(r); + d[1] = NR_NORMALIZE_31(g); + d[2] = NR_NORMALIZE_31(b); d[3] = NR_NORMALIZE_21(a); } else { - d[0] = NR_COMPOSEPPP_2211 (r, a, d[0]); - d[1] = NR_COMPOSEPPP_2211 (g, a, d[1]); - d[2] = NR_COMPOSEPPP_2211 (b, a, d[2]); + d[0] = NR_COMPOSEPPP_3211 (r, a, d[0]); + d[1] = NR_COMPOSEPPP_3211 (g, a, d[1]); + d[2] = NR_COMPOSEPPP_3211 (b, a, d[2]); d[3] = NR_COMPOSEA_211(a, d[3]); } } @@ -309,11 +316,14 @@ void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, in int i; if (alpha == 0) return; + if (alpha>255) { + g_warning("In transform PPN alpha=%u>255",alpha); + } - // Both alpha and color components are stored temporarily with a range of [0,255^2], so more supersampling and we get an overflow - if (xd+yd>16) { - xd = 8; - yd = 8; + // The color component is stored temporarily with a range of [0,255^3], so more supersampling and we get an overflow (fortunately Inkscape's preferences also doesn't allow a higher setting) + if (xd+yd>8) { + xd = 4; + yd = 4; } dbits = xd + yd; diff --git a/src/libnr/nr-pixops.h b/src/libnr/nr-pixops.h index 417e5e2c9..7eafd1a9d 100644 --- a/src/libnr/nr-pixops.h +++ b/src/libnr/nr-pixops.h @@ -53,6 +53,7 @@ static inline unsigned int NR_COMPOSENNP_11113(unsigned int fc, unsigned int fa, static inline unsigned int NR_COMPOSENNP_11111(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba) { return NR_NORMALIZE_31(NR_COMPOSENNP_11113(fc, fa, bc, ba)); } // Operation: (1 - fa) * bc * ba + fc +static inline unsigned int NR_COMPOSEPNP_32114(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba) { return (255*255 - fa) * ba * bc + 255 * fc; } static inline unsigned int NR_COMPOSEPNP_22114(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba) { return (255*255 - fa) * ba * bc + 255*255 * fc; } static inline unsigned int NR_COMPOSEPNP_11113(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba) { return (255 - fa) * ba * bc + 255*255 * fc; } static inline unsigned int NR_COMPOSEPNP_22111(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba) { return NR_NORMALIZE_41(NR_COMPOSEPNP_22114(fc, fa, bc, ba)); } @@ -65,6 +66,7 @@ static inline unsigned int NR_COMPOSENNN_111121(unsigned int fc, unsigned int fa // Operation: ((1 - fa) * bc * ba + fc)/a // Reuses non-normalized versions of NR_COMPOSEPNP +static inline unsigned int NR_COMPOSEPNN_321131(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba, unsigned int a) { return DIV_ROUND(NR_COMPOSEPNP_32114(fc, fa, bc, ba), a); } static inline unsigned int NR_COMPOSEPNN_221131(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba, unsigned int a) { return DIV_ROUND(NR_COMPOSEPNP_22114(fc, fa, bc, ba), a); } static inline unsigned int NR_COMPOSEPNN_111121(unsigned int fc, unsigned int fa, unsigned int bc, unsigned int ba, unsigned int a) { return DIV_ROUND(NR_COMPOSEPNP_11113(fc, fa, bc, ba), a); } @@ -81,13 +83,15 @@ static inline unsigned int NR_COMPOSENPP_1111(unsigned int fc, unsigned int fa, // Operation: (1 - fa) * bc + fc // (1-fa)*bc+fc = bc-fa*bc+fc = (bc+fc)-fa*bc // This rewritten form results in faster code (found out through testing) -static inline unsigned int NR_COMPOSEPPP_2224(unsigned int fc, unsigned int fa, unsigned int bc) { return 255*255*(bc+fc) - fa*bc; } +static inline unsigned int NR_COMPOSEPPP_2224(unsigned int fc, unsigned int fa, unsigned int bc) { return 255*255*(bc+fc) - fa*bc; } // Note that this can temporarily overflow (but it probably doesn't cause problems) // NR_COMPOSEPPP_2224 assumes that fa and fc have a common component (fa=a*x and fc=c*x), because then the maximum value is: // (255*255-255*x)*255*255 + 255*x*255*255 = 255*255*( (255*255-255*x) + 255*x ) = 255*255*255*( (255-x)+x ) = 255*255*255*255 +static inline unsigned int NR_COMPOSEPPP_3213(unsigned int fc, unsigned int fa, unsigned int bc) { return 255*255*bc + fc - fa*bc; } static inline unsigned int NR_COMPOSEPPP_2213(unsigned int fc, unsigned int fa, unsigned int bc) { return 255*(255*bc+fc) - fa*bc; } static inline unsigned int NR_COMPOSEPPP_1213(unsigned int fc, unsigned int fa, unsigned int bc) { return 255*255*(bc+fc) - fa*bc; } static inline unsigned int NR_COMPOSEPPP_1112(unsigned int fc, unsigned int fa, unsigned int bc) { return 255*(bc+fc) - fa*bc; } static inline unsigned int NR_COMPOSEPPP_2221(unsigned int fc, unsigned int fa, unsigned int bc) { return NR_NORMALIZE_41(NR_COMPOSEPPP_2224(fc, fa, bc)); } +static inline unsigned int NR_COMPOSEPPP_3211(unsigned int fc, unsigned int fa, unsigned int bc) { return NR_NORMALIZE_31(NR_COMPOSEPPP_3213(fc, fa, bc)); } static inline unsigned int NR_COMPOSEPPP_2211(unsigned int fc, unsigned int fa, unsigned int bc) { return NR_NORMALIZE_31(NR_COMPOSEPPP_2213(fc, fa, bc)); } static inline unsigned int NR_COMPOSEPPP_1211(unsigned int fc, unsigned int fa, unsigned int bc) { return NR_NORMALIZE_21(NR_COMPOSEPPP_1213(fc, fa, bc)); } static inline unsigned int NR_COMPOSEPPP_1111(unsigned int fc, unsigned int fa, unsigned int bc) { return NR_NORMALIZE_21(NR_COMPOSEPPP_1112(fc, fa, bc)); } diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index b958e899d..bdf700346 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -412,7 +412,7 @@ void font_instance::InitTheFace() SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE); SelectObject(daddy->hScreenDC,theFace); #else - theFace=pango_ft2_font_get_face(pFont); + theFace=pango_ft2_font_get_face(pFont); // Deprecated, use pango_fc_font_lock_face() instead if ( theFace ) { FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol); } @@ -662,6 +662,7 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading) ascent=fabs(otm.otmAscent*scale); descent=fabs(otm.otmDescent*scale); leading=fabs(otm.otmLineGap*scale); + //otmSubscriptSize, otmSubscriptOffset, otmSuperscriptSize, otmSuperscriptOffset, #else if ( theFace->units_per_EM == 0 ) { return false; // bitmap font diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index f6b9688bb..192596ecf 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -76,7 +76,14 @@ class Layout::Calculator /** for y= attributes in tspan elements et al, we do the adjustment by moving each glyph individually by this number. The spec means that this is maintained across - paragraphs. */ + paragraphs. + + To do non-flow text layout, only the first "y" attribute is normally used. If there is only one + "y" attribute in a <tspan> other than the first <tspan>, it is ignored. This allows Inkscape to + insert a new line anywhere. On output, the Inkscape determined "y" is written out so other SVG + viewers know where to place the <tspans>. + */ + double _y_offset; /** to stop pango from hinting its output, the font factory creates all fonts very large. @@ -117,8 +124,9 @@ class Layout::Calculator unsigned input_index; /// index into Layout::_input_stream Glib::ustring::const_iterator input_stream_first_character; double font_size; - LineHeight line_height; + LineHeight line_height; /// This is not the CSS line-height attribute! double line_height_multiplier; /// calculated from the font-height css property + double baseline_shift; /// calculated from the baseline-shift css property unsigned text_bytes; unsigned char_index_in_para; /// the index of the first character in this span in the paragraph, for looking up char_attributes SVGLength x, y, dx, dy, rotate; // these are reoriented copies of the <tspan> attributes. We change span when we encounter one. @@ -153,10 +161,14 @@ class Layout::Calculator unsigned whitespace_count; bool ends_with_whitespace; double each_whitespace_width; + double letter_spacing; // Save so we can subtract from width at end of line (for center justification) + double word_spacing; void setZero(); }; - /** The definition of a chunk used here is the same as that used in Layout. */ + /** The definition of a chunk used here is the same as that used in Layout: + A collection of contiguous broken spans on the same line. (One chunk per line + unless shape splits line into several sections... then one chunk per section. */ struct ChunkInfo { std::vector<BrokenSpan> broken_spans; double scanrun_width; @@ -340,12 +352,21 @@ class Layout::Calculator span->end.increment(); - if (span->width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol + // Width should not include letter_spacing (or word_spacing) after last letter at end of line. + // word_spacing is attached to white space that is already removed from line end (?) + double test_width = span->width - text_source->style->letter_spacing.computed; + + // Save letter_spacing and word_spacing for subtraction later if span is last span in line. + span->letter_spacing = text_source->style->letter_spacing.computed; + span->word_spacing = text_source->style->word_spacing.computed; + + if (test_width > maximum_width && !char_attributes.is_white) { // whitespaces don't matter, we can put as many as we want at eol TRACE(("span %d exceeded scanrun; width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); return false; } } while (span->end.char_byte != 0); // while we haven't wrapped to the next span + TRACE(("fitted span %d width = %f chars = %d\n", span->start.iter_span - para.unbroken_spans.begin(), span->width, char_count)); return true; } @@ -369,7 +390,7 @@ class Layout::Calculator case RIGHT: return it_chunk->x - it_chunk->text_width; case CENTER: - return it_chunk->x - it_chunk->text_width / 2; + return it_chunk->x - it_chunk->text_width/ 2; } } @@ -399,6 +420,7 @@ class Layout::Calculator */ void _outputLine(ParagraphInfo const ¶, LineHeight const &line_height, std::vector<ChunkInfo> const &chunk_info) { + TRACE(("Start _outputLine\n")); if (chunk_info.empty()) { TRACE(("line too short to fit anything on it, go to next\n")); return; @@ -418,19 +440,45 @@ class Layout::Calculator // add the chunk to the list Layout::Chunk new_chunk; new_chunk.in_line = _flow._lines.size() - 1; + TRACE((" New chunk: in_line: %d\n", new_chunk.in_line)); new_chunk.left_x = _getChunkLeftWithAlignment(para, it_chunk, &add_to_each_whitespace); + // we may also have y move orders to deal with here (dx, dy and rotate are done per span) - if (!it_chunk->broken_spans.empty() // this one only happens for empty paragraphs - && it_chunk->broken_spans.front().start.char_byte == 0 - && it_chunk->broken_spans.front().start.iter_span->y._set) { - // if this is the start of a line, we should change the baseline rather than each glyph individually - if (_flow._characters.empty() || _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { - new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed; - _flow._lines.back().baseline_y = new_line.baseline_y; + + // Comment added: 1 June 2010: + // The first line in a normal <text> object is placed by the read-in "y" value. The rest are + // determined by Inkscape. This is to allow insertation of new lines in the middle + // of a <text> object. New "y" values are then stored in each <tspan> that represents + // a new line. The line spacing should depend only on font-size and line-height (and not + // on any y-kerning). Line spacing is already handled by the calling routine. Note that + // this may render improperly any SVG with <tspan>s created/edited by other programs. + if (!it_chunk->broken_spans.empty() // Not empty paragraph + && it_chunk->broken_spans.front().start.char_byte == 0 ) { // Beginning of unbroken span + + // If empty or new line + if( _flow._characters.empty() || + _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { + + // If <tspan> "y" attribute is set, use it (initial "y" attributes in + // <tspans> other than the first have already been stripped). + if( it_chunk->broken_spans.front().start.iter_span->y._set ) { + + // Use set "y" attribute + new_line.baseline_y = it_chunk->broken_spans.front().start.iter_span->y.computed; + + // Save baseline + _flow._lines.back().baseline_y = new_line.baseline_y; + + // Save new <tspan> y coordinate + _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent); + + } + + // Reset relative y_offset ("dy" attribute is relative but should be reset at + // the beginning of each <tspan>.) _y_offset = 0.0; - _scanline_maker->setNewYCoordinate(new_line.baseline_y - line_height.ascent); - } else - _y_offset = it_chunk->broken_spans.front().start.iter_span->y.computed - new_line.baseline_y; + + } } _flow._chunks.push_back(new_chunk); @@ -455,7 +503,8 @@ class Layout::Calculator UnbrokenSpan const &unbroken_span = *it_span->start.iter_span; if (it_span->start.char_byte == 0) { - // start of an unbroken span, we might have dx, dy or rotate still to process (x and y are done per chunk) + // Start of an unbroken span, we might have dx, dy or rotate still to process + // (x and y are done per chunk) if (unbroken_span.dx._set) x += unbroken_span.dx.computed; if (unbroken_span.dy._set) _y_offset += unbroken_span.dy.computed; if (unbroken_span.rotate._set) glyph_rotate = unbroken_span.rotate.computed * (M_PI/180); @@ -473,7 +522,7 @@ class Layout::Calculator new_span.in_chunk = _flow._chunks.size() - 1; new_span.line_height = unbroken_span.line_height; new_span.in_input_stream_item = unbroken_span.input_index; - new_span.baseline_shift = _y_offset; + new_span.baseline_shift = 0.0; new_span.block_progression = _block_progression; if ((_flow._input_stream[unbroken_span.input_index]->Type() == TEXT_SOURCE) && (new_span.font = para.pango_items[unbroken_span.pango_item_index].font)) { @@ -556,11 +605,16 @@ class Layout::Calculator if (_block_progression == LEFT_TO_RIGHT || _block_progression == RIGHT_TO_LEFT) { new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier + new_span.line_height.ascent; - new_glyph.y = _y_offset + (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; + new_glyph.y = _y_offset - + unbroken_span.baseline_shift + + (unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset - + unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * 0.5) * font_size_multiplier; new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, true); } else { new_glyph.x = x + unbroken_span.glyph_string->glyphs[glyph_index].geometry.x_offset * font_size_multiplier; - new_glyph.y = _y_offset + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; + new_glyph.y = _y_offset - + unbroken_span.baseline_shift + + unbroken_span.glyph_string->glyphs[glyph_index].geometry.y_offset * font_size_multiplier; new_glyph.width = unbroken_span.glyph_string->glyphs[glyph_index].geometry.width * font_size_multiplier; if ((new_glyph.width == 0) && (para.pango_items[unbroken_span.pango_item_index].font)) new_glyph.width = new_span.font_size * para.pango_items[unbroken_span.pango_item_index].font->Advance(unbroken_span.glyph_string->glyphs[glyph_index].glyph, false); @@ -936,6 +990,7 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total(); } + /** * Split the paragraph into spans. Also call pango_shape() on them. * @@ -973,6 +1028,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const } } else if (_flow._input_stream[input_index]->Type() == TEXT_SOURCE && pango_item_index < para->pango_items.size()) { Layout::InputStreamTextSource const *text_source = static_cast<Layout::InputStreamTextSource const *>(_flow._input_stream[input_index]); + unsigned char_index_in_source = 0; unsigned span_start_byte_in_source = 0; @@ -989,6 +1045,7 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const unsigned const text_source_bytes = ( text_source->text_end.base() - text_source->text_begin.base() - span_start_byte_in_source ); + TRACE(("New Span\n")); UnbrokenSpan new_span; new_span.text_bytes = std::min(text_source_bytes, pango_item_bytes); new_span.input_stream_first_character = Glib::ustring::const_iterator(text_source->text_begin.base() + span_start_byte_in_source); @@ -1080,6 +1137,11 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const } new_span.pango_item_index = pango_item_index; _computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier); + + // At some point we may want to calculate baseline_shift here (to take advantage + // of otm features like superscript baseline), but for now we use style baseline_shift. + new_span.baseline_shift = text_source->style->baseline_shift.computed; + // TODO: metrics for vertical text TRACE(("add text span %d \"%s\"\n", para->unbroken_spans.size(), text_source->text->raw().substr(span_start_byte_in_source, new_span.text_bytes).c_str())); TRACE((" %d glyphs\n", new_span.glyph_string->num_glyphs)); @@ -1337,6 +1399,13 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, chunk_info->back().whitespace_count--; } + if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty() ) { + // for justification we need to discard line-spacing and word-spacing at end of the chunk + chunk_info->back().broken_spans.back().width -= chunk_info->back().broken_spans.back().letter_spacing; + chunk_info->back().text_width -= chunk_info->back().broken_spans.back().letter_spacing; + TRACE(("width after subtracting last letter_spacing: %f\n", chunk_info->back().broken_spans.back().width)); + } + return true; } @@ -1367,7 +1436,7 @@ bool Layout::Calculator::calculate() _createFirstScanlineMaker(); ParagraphInfo para; - LineHeight line_height; // needs to be maintained across paragraphs to be able to deal with blank paras (this is wrong) + LineHeight line_height; // needs to be maintained across paragraphs to be able to deal with blank paras for(para.first_input_index = 0 ; para.first_input_index < _flow._input_stream.size() ; ) { // jump to the next wrap shape if this is a SHAPE_BREAK control code if (_flow._input_stream[para.first_input_index]->Type() == CONTROL_CODE) { @@ -1408,7 +1477,7 @@ bool Layout::Calculator::calculate() break; // out of shapes to wrap in to _outputLine(para, line_height, line_chunk_info); - _scanline_maker->completeLine(); + _scanline_maker->completeLine(); // Increments y by line height } while (span_pos.iter_span != para.unbroken_spans.end()); TRACE(("para %d end\n\n", _flow._paragraphs.size() - 1)); diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index fc40643c8..5ceece66b 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -63,7 +63,7 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &/*bbox_to_snap*/, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const */*it*/) const { @@ -75,20 +75,46 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, const LineList lines = _getSnapLines(p.getPoint()); for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) { - if (Geom::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint - // constraint line - Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint(); - Geom::Line line1(point_on_line, point_on_line + c.getDirection()); - - // grid/guide line - Geom::Point const p1 = i->second; // point at guide/grid line - Geom::Point const p2 = p1 + Geom::rot90(i->first); // 2nd point at guide/grid line - Geom::Line line2(p1, p2); + Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint(); + Geom::Line gridguide_line(i->second, i->second + Geom::rot90(i->first)); + if (c.isCircular()) { + // Find the intersections between the line and the circular constraint + // First, project the origin of the circle onto the line + Geom::Point const origin = c.getPoint(); + Geom::Point const p_proj = Geom::projection(origin, gridguide_line); + Geom::Point v_orig = c.getDirection(); // vector from the origin to the original (untransformed) point + Geom::Point v_proj = p_proj - origin; + Geom::Coord dist = Geom::L2(v_proj); // distance from circle origin to constraint line + Geom::Coord radius = c.getRadius(); + Geom::Coord radians = NR_HUGE; + if (dist == radius) { + // Only one point of intersection; + // Calculate the rotation in radians... + radians = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj)); + _addSnappedPoint(sc, p_proj, Geom::L2(p.getPoint() - p_proj), p.getSourceType(), p.getSourceNum(), true, radians); + } else if (dist < radius) { + // Two points of intersection, symmetrical with respect to the projected point + // Calculate half the length of the linesegment between the two points of intersection + Geom::Coord l = sqrt(radius*radius - dist*dist); + Geom::Coord d = Geom::L2(gridguide_line.versor()); // length of versor, needed to normalize the versor + if (d > 0) { + Geom::Point v = l*gridguide_line.versor()/d; + v_proj = p_proj + v - origin; + radians = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj)); + _addSnappedPoint(sc, p_proj + v, Geom::L2(p.getPoint() - (p_proj + v)), p.getSourceType(), p.getSourceNum(), true, radians); + v_proj = p_proj - v - origin; + radians = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj)); + _addSnappedPoint(sc, p_proj - v, Geom::L2(p.getPoint() - (p_proj - v)), p.getSourceType(), p.getSourceNum(), true, radians); + } + } + } else { + // Find the intersections between the line and the linear constraint + Geom::Line constraint_line(point_on_line, point_on_line + c.getDirection()); Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default try { - inters = Geom::intersection(line1, line2); + inters = Geom::intersection(constraint_line, gridguide_line); } catch (Geom::InfiniteSolutions e) { @@ -97,23 +123,14 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, } if (inters) { - Geom::Point t = line1.pointAt((*inters).ta); + Geom::Point t = constraint_line.pointAt((*inters).ta); const Geom::Coord dist = Geom::L2(t - p.getPoint()); if (dist < getSnapperTolerance()) { // When doing a constrained snap, we're already at an intersection. // This snappoint is therefore fully constrained, so there's no need // to look for additional intersections; just return the snapped point // and forget about the line - _addSnappedPoint(sc, t, dist, p.getSourceType(), p.getSourceNum(), true); - // For any line that's within range, we will also look at it's "point on line" p1. For guides - // this point coincides with its origin; for grids this is of no use, but we cannot - // discern between grids and guides here - Geom::Coord const dist_p1 = Geom::L2(p1 - p.getPoint()); - if (dist_p1 < getSnapperTolerance()) { - _addSnappedLinesOrigin(sc, p1, dist_p1, p.getSourceType(), p.getSourceNum(), true); - // Only relevant for guides; grids don't have an origin per line - // Therefore _addSnappedLinesOrigin() will only be implemented for guides - } + _addSnappedPoint(sc, t, dist, p.getSourceType(), p.getSourceNum(), true, 1); } } } diff --git a/src/line-snapper.h b/src/line-snapper.h index 1aa3526cc..403c8cbba 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -34,7 +34,7 @@ public: void constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const *it) const; protected: @@ -54,7 +54,7 @@ private: // Will only be implemented for guide lines, because grid lines don't have an origin virtual void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const; - virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap) const = 0; + virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, long source_num, bool constrained_snap, Geom::Coord angle) const = 0; }; } diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index f761a6a7c..6266fade0 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -29,7 +29,7 @@ #include "message-stack.h" #include "desktop.h" #include "knotholder.h" - +#include "sp-lpe-item.h" #include "live_effects/lpeobject.h" #include "live_effects/parameter/parameter.h" #include <glibmm/ustring.h> diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index a8d34a233..681bae387 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -15,8 +15,6 @@ #include <glibmm/ustring.h> #include <2geom/forward.h> #include "ui/widget/registry.h" -#include "sp-lpe-item.h" -//#include "knotholder.h" #include "parameter/bool.h" #include "effect-enum.h" @@ -30,6 +28,7 @@ struct LivePathEffectObject; class SPLPEItem; class KnotHolder; class KnotHolderEntity; +class SPPath; namespace Gtk { class Widget; diff --git a/src/live_effects/lpe-angle_bisector.cpp b/src/live_effects/lpe-angle_bisector.cpp index 71a34a1e2..aee4f48a1 100644 --- a/src/live_effects/lpe-angle_bisector.cpp +++ b/src/live_effects/lpe-angle_bisector.cpp @@ -25,6 +25,8 @@ #include <2geom/path.h> #include <2geom/sbasis-to-bezier.h> +#include "sp-lpe-item.h" + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-circle_3pts.cpp b/src/live_effects/lpe-circle_3pts.cpp index d600ef046..951a3b7c8 100644 --- a/src/live_effects/lpe-circle_3pts.cpp +++ b/src/live_effects/lpe-circle_3pts.cpp @@ -17,6 +17,7 @@ // You might need to include other 2geom files. You can add them here: #include <2geom/path.h> +#include <2geom/circle.h> namespace Inkscape { namespace LivePathEffect { @@ -30,24 +31,6 @@ LPECircle3Pts::~LPECircle3Pts() { } -static void _circle(Geom::Point center, double radius, std::vector<Geom::Path> &path_out) { - using namespace Geom; - - Geom::Path pb; - - D2<SBasis> B; - Linear bo = Linear(0, 2 * M_PI); - - B[0] = cos(bo,4); - B[1] = sin(bo,4); - - B = B * radius + center; - - pb.append(SBasisCurve(B)); - - path_out.push_back(pb); -} - static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, std::vector<Geom::Path> &path_out) { using namespace Geom; @@ -64,7 +47,8 @@ static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point con Point M = D + v * lambda; double radius = L2(M - A); - _circle(M, radius, path_out); + Geom::Circle c(M, radius); + c.getPath(path_out); } std::vector<Geom::Path> diff --git a/src/live_effects/lpe-circle_with_radius.cpp b/src/live_effects/lpe-circle_with_radius.cpp index 574a9c004..71611e18b 100644 --- a/src/live_effects/lpe-circle_with_radius.cpp +++ b/src/live_effects/lpe-circle_with_radius.cpp @@ -18,6 +18,7 @@ #include <2geom/sbasis.h> #include <2geom/bezier-to-sbasis.h> #include <2geom/d2.h> +#include <2geom/circle.h> using namespace Geom; @@ -38,22 +39,6 @@ LPECircleWithRadius::~LPECircleWithRadius() } -void _circle(Geom::Point center, double radius, std::vector<Geom::Path> &path_out) { - Geom::Path pb; - - D2<SBasis> B; - Linear bo = Linear(0, 2 * M_PI); - - B[0] = cos(bo,4); - B[1] = sin(bo,4); - - B = B * radius + center; - - pb.append(SBasisCurve(B)); - - path_out.push_back(pb); -} - std::vector<Geom::Path> LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in) { @@ -64,7 +49,8 @@ LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in) double radius = Geom::L2(pt - center); - _circle(center, radius, path_out); + Geom::Circle c(center, radius); + c.getPath(path_out); return path_out; } diff --git a/src/live_effects/lpe-constructgrid.cpp b/src/live_effects/lpe-constructgrid.cpp index 4725573d7..4c6555f2d 100644 --- a/src/live_effects/lpe-constructgrid.cpp +++ b/src/live_effects/lpe-constructgrid.cpp @@ -31,8 +31,8 @@ LPEConstructGrid::LPEConstructGrid(LivePathEffectObject *lpeobject) : nr_x.param_make_integer(); nr_y.param_make_integer(); - nr_x.param_set_range(1, NR_HUGE); - nr_y.param_set_range(1, NR_HUGE); + nr_x.param_set_range(1, 1e10); + nr_y.param_set_range(1, 1e10); } LPEConstructGrid::~LPEConstructGrid() diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp index 89fdef2f0..e1e21107c 100644 --- a/src/live_effects/lpe-curvestitch.cpp +++ b/src/live_effects/lpe-curvestitch.cpp @@ -192,6 +192,7 @@ LPECurveStitch::resetDefaults(SPItem * item) /** /todo check whether this special case is necessary. It seems to "bug" editing behavior: * scaling an object with transforms preserved behaves differently from scaling with * transforms optimized (difference caused by this special method). + * special casing is probably needed, because rotation should not be propagated to the strokepath. */ void LPECurveStitch::transform_multiply(Geom::Matrix const& postmul, bool set) diff --git a/src/live_effects/lpe-extrude.cpp b/src/live_effects/lpe-extrude.cpp index c861515aa..5f63d0567 100644 --- a/src/live_effects/lpe-extrude.cpp +++ b/src/live_effects/lpe-extrude.cpp @@ -18,6 +18,8 @@ #include <2geom/transforms.h> #include <algorithm> +#include "sp-item.h" + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp index 00f7ec193..b1337d5fb 100644 --- a/src/live_effects/lpe-gears.cpp +++ b/src/live_effects/lpe-gears.cpp @@ -220,7 +220,7 @@ LPEGears::LPEGears(LivePathEffectObject *lpeobject) : */ teeth.param_make_integer(); - teeth.param_set_range(3, NR_HUGE); + teeth.param_set_range(3, 1e10); registerParameter( dynamic_cast<Parameter *>(&teeth) ); registerParameter( dynamic_cast<Parameter *>(&phi) ); } diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp index b3aa2df45..b8e9b8cf9 100644 --- a/src/live_effects/lpe-knot.cpp +++ b/src/live_effects/lpe-knot.cpp @@ -56,17 +56,19 @@ public: static std::vector<Geom::Interval> complementOf(Geom::Interval I, std::vector<Geom::Interval> domain){ std::vector<Geom::Interval> ret; - double min = domain.front().min(); - double max = domain.back().max(); - Geom::Interval I1 = Geom::Interval(min,I.min()); - Geom::Interval I2 = Geom::Interval(I.max(),max); - - for (unsigned i = 0; i<domain.size(); i++){ - boost::optional<Geom::Interval> I1i = intersect(domain.at(i),I1); - if (I1i && !I1i->isSingular()) ret.push_back(I1i.get()); - boost::optional<Geom::Interval> I2i = intersect(domain.at(i),I2); - if (I2i && !I2i->isSingular()) ret.push_back(I2i.get()); - } + if (!domain.empty()) { + double min = domain.front().min(); + double max = domain.back().max(); + Geom::Interval I1 = Geom::Interval(min,I.min()); + Geom::Interval I2 = Geom::Interval(I.max(),max); + + for (unsigned i = 0; i<domain.size(); i++){ + boost::optional<Geom::Interval> I1i = intersect(domain.at(i),I1); + if (I1i && !I1i->isSingular()) ret.push_back(I1i.get()); + boost::optional<Geom::Interval> I2i = intersect(domain.at(i),I2); + if (I2i && !I2i->isSingular()) ret.push_back(I2i.get()); + } + } return ret; } diff --git a/src/live_effects/lpeobject.cpp b/src/live_effects/lpeobject.cpp index ec0dee0be..aa916318d 100644 --- a/src/live_effects/lpeobject.cpp +++ b/src/live_effects/lpeobject.cpp @@ -245,6 +245,7 @@ livepatheffect_on_repr_attr_changed ( Inkscape::XML::Node * /*repr*/, /** * If this has other users, create a new private duplicate and return it * returns 'this' when no forking was necessary (and therefore no duplicate was made) + * Check out sp_lpe_item_fork_path_effects_if_necessary ! */ LivePathEffectObject * LivePathEffectObject::fork_private_if_necessary(unsigned int nr_of_allowed_users) @@ -252,11 +253,12 @@ LivePathEffectObject::fork_private_if_necessary(unsigned int nr_of_allowed_users if (SP_OBJECT_HREFCOUNT(this) > nr_of_allowed_users) { SPDocument *doc = SP_OBJECT_DOCUMENT(this); Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); + Inkscape::XML::Node *dup_repr = SP_OBJECT_REPR (this)->duplicate(xml_doc); - Inkscape::XML::Node *repr = SP_OBJECT_REPR (this)->duplicate(xml_doc); - SP_OBJECT_REPR (SP_DOCUMENT_DEFS (doc))->addChild(repr, NULL); - LivePathEffectObject *lpeobj_new = (LivePathEffectObject *) doc->getObjectByRepr(repr); - Inkscape::GC::release(repr); + SP_OBJECT_REPR (SP_DOCUMENT_DEFS (doc))->addChild(dup_repr, NULL); + LivePathEffectObject *lpeobj_new = LIVEPATHEFFECT( doc->getObjectByRepr(dup_repr) ); + + Inkscape::GC::release(dup_repr); return lpeobj_new; } return this; diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index 0a9aa2751..9c0ca1767 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -108,8 +108,9 @@ static char const menus_skeleton[] = " <verb verb-id=\"ViewModeNormal\" radio=\"yes\" default=\"yes\"/>\n" " <verb verb-id=\"ViewModeNoFilters\" radio=\"yes\"/>\n" " <verb verb-id=\"ViewModeOutline\" radio=\"yes\"/>\n" -" <verb verb-id=\"ViewModePrintColorsPreview\" radio=\"yes\"/>\n" -" <verb verb-id=\"DialogPrintColorsPreview\" />\n" +// Better location in menu needs to be found +//" <verb verb-id=\"ViewModePrintColorsPreview\" radio=\"yes\"/>\n" +//" <verb verb-id=\"DialogPrintColorsPreview\" />\n" " </submenu>\n" " <separator/>\n" " <verb verb-id=\"ToggleGrid\" />\n" @@ -133,6 +134,8 @@ static char const menus_skeleton[] = " <verb verb-id=\"ViewNew\" />\n" " <separator/>\n" " <verb verb-id=\"FullScreen\" />\n" +" <separator/>\n" +" <task-checkboxes/>\n" // Not quite ready to be in the menus. // " <verb verb-id=\"FocusToggle\" />\n" " </submenu>\n" diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 1d81aa7f5..83b01013c 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -141,7 +141,7 @@ RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*orig //In general we cannot just snap this radius to an arbitrary point, as we have only a single //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap //the radius then we should have a constrained snap. snap_knot_position() is unconstrained - Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0))); + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0))); if (state & GDK_CONTROL_MASK) { gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; @@ -191,7 +191,7 @@ RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*orig //In general we cannot just snap this radius to an arbitrary point, as we have only a single //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap //the radius then we should have a constrained snap. snap_knot_position() is unconstrained - Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1))); + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1))); if (state & GDK_CONTROL_MASK) { // When holding control then rx will be kept equal to ry, // resulting in a perfect circle (and not an ellipse) @@ -280,13 +280,13 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or // snap to horizontal or diagonal if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->height.computed = MAX(h_orig + minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->height.computed = MAX(h_orig, 0); @@ -297,13 +297,13 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or // snap to vertical or diagonal if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->width.computed = MAX(w_orig + miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->width.computed = MAX(w_orig, 0); @@ -370,14 +370,14 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin // snap to horizontal or diagonal if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y); rect->height.computed = MAX(h_orig - minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->y.computed = MIN(origin[Geom::Y], opposite_y); @@ -389,14 +389,14 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin // snap to vertical or diagonal if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x); rect->width.computed = MAX(w_orig - miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->x.computed = MIN(origin[Geom::X], opposite_x); diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 536affb82..f96335c4a 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -16,6 +16,7 @@ #include <2geom/point.h> #include <2geom/rect.h> #include <2geom/line.h> +#include <2geom/circle.h> #include "document.h" #include "sp-namedview.h" #include "sp-image.h" @@ -91,6 +92,12 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, return; } + if (_snapmanager->getDesktop() == NULL) { + g_warning("desktop == NULL, so we cannot snap; please inform the developpers of this bug"); + // Apparently the etup() method from the SnapManager class hasn't been called before trying to snap. + } + + if (first_point) { _candidates->clear(); } @@ -99,7 +106,6 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, bbox_to_snap_incl.expandBy(getSnapperTolerance()); // see? for (SPObject* o = sp_object_first_child(parent); o != NULL; o = SP_OBJECT_NEXT(o)) { - g_assert(_snapmanager->getDesktop() != NULL); if (SP_IS_ITEM(o) && !(_snapmanager->getDesktop()->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) { // Snapping to items in a locked layer is allowed // Don't snap to hidden objects, unless they're a clipped path or a mask @@ -441,6 +447,9 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, } } + int num_path = 0; + int num_segm = 0; + for (std::vector<Inkscape::SnapCandidatePath >::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { bool const being_edited = node_tool_active && (*it_p).currently_being_edited; //if true then this pathvector it_pv is currently being edited in the node tool @@ -482,11 +491,13 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, if (!being_edited || (c1 && c2)) { Geom::Coord const dist = Geom::distance(sp_doc, p_doc); if (dist < getSnapperTolerance()) { - sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, p.getSourceType(), p.getSourceNum(), it_p->target_type, it_p->target_bbox)); + sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, num_path, num_segm, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, p.getSourceType(), p.getSourceNum(), it_p->target_type, it_p->target_bbox)); } } } + num_segm++; } // End of: for (Geom::PathVector::iterator ....) + num_path++; } } @@ -512,7 +523,7 @@ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::ve void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, - ConstraintLine const &c) const + SnapConstraint const &c) const { _collectPaths(p, p.getSourceNum() == 0); @@ -527,36 +538,54 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, direction_vector = Geom::unit_vector(direction_vector); } - // The intersection point of the constraint line with any path, - // must lie within two points on the constraintline: p_min_on_cl and p_max_on_cl - // The distance between those points is twice the snapping tolerance + // The intersection point of the constraint line with any path, must lie within two points on the + // SnapConstraint: p_min_on_cl and p_max_on_cl. The distance between those points is twice the snapping tolerance Geom::Point const p_proj_on_cl = p.getPoint(); // projection has already been taken care of in constrainedSnap in the snapmanager; Geom::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector); Geom::Point const p_max_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector); + Geom::Coord tolerance = getSnapperTolerance(); + + // PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have + // to convert the snapper coordinates from the desktop coordinates to document coordinates - Geom::Path cl; - std::vector<Geom::Path> clv; - cl.start(p_min_on_cl); - cl.appendNew<Geom::LineSegment>(p_max_on_cl); - clv.push_back(cl); + std::vector<Geom::Path> constraint_path; + if (c.isCircular()) { + Geom::Circle constraint_circle(_snapmanager->getDesktop()->dt2doc(c.getPoint()), c.getRadius()); + constraint_circle.getPath(constraint_path); + } else { + Geom::Path constraint_line; + constraint_line.start(p_min_on_cl); + constraint_line.appendNew<Geom::LineSegment>(p_max_on_cl); + constraint_path.push_back(constraint_line); + } for (std::vector<Inkscape::SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { if (k->path_vector) { - Geom::CrossingSet cs = Geom::crossings(clv, *(k->path_vector)); - if (cs.size() > 0) { - // We need only the first element of cs, because cl is only a single straight linesegment - // This first element contains a vector filled with crossings of cl with k->first - for (std::vector<Geom::Crossing>::const_iterator m = cs[0].begin(); m != cs[0].end(); m++) { - if ((*m).ta >= 0 && (*m).ta <= 1 ) { - // Reconstruct the point of intersection - Geom::Point p_inters = p_min_on_cl + ((*m).ta) * (p_max_on_cl - p_min_on_cl); - // When it's within snapping range, then return it - // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1) - Geom::Coord dist = Geom::L2(_snapmanager->getDesktop()->dt2doc(p_proj_on_cl) - p_inters); - SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox); + Geom::CrossingSet cs = Geom::crossings(constraint_path, *(k->path_vector)); + unsigned int index = 0; + for (Geom::CrossingSet::const_iterator i = cs.begin(); i != cs.end(); i++) { + if (index >= constraint_path.size()) { + break; + } + for (Geom::Crossings::const_iterator m = (*i).begin(); m != (*i).end(); m++) { + //std::cout << "ta = " << (*m).ta << " | tb = " << (*m).tb << std::endl; + // Reconstruct the point of intersection + Geom::Point p_inters = constraint_path[index].pointAt((*m).ta); + // .. and convert it to desktop coordinates + p_inters = _snapmanager->getDesktop()->doc2dt(p_inters); + Geom::Coord dist = Geom::L2(p_proj_on_cl - p_inters); + SnappedPoint s = SnappedPoint(p_inters, p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox);; + if (dist <= tolerance) { // If the intersection is within snapping range, then we might snap to it + if (c.isCircular()) { + Geom::Point v_orig = c.getDirection(); // vector from the origin to the original (untransformed) point + Geom::Point v_inters = p_inters - c.getPoint(); + Geom::Coord radians = atan2(Geom::dot(Geom::rot90(v_orig), v_inters), Geom::dot(v_orig, v_inters)); + s.setTransformation(Geom::Point(radians, radians)); + } sc.points.push_back(s); } } + index++; } } } @@ -612,7 +641,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const *it) const { if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) { @@ -667,7 +696,7 @@ void Inkscape::ObjectSnapper::guideFreeSnap(SnappedConstraints &sc, void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal, - ConstraintLine const &/*c*/) const + SnapConstraint const &/*c*/) const { /* Get a list of all the SPItems that we will try to snap to */ std::vector<SPItem*> cand; @@ -693,7 +722,7 @@ void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc, bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const { bool snap_to_something = _snapmanager->snapprefs.getSnapToItemPath() - || _snapmanager->snapprefs.getSnapToItemNode() + || _snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapToPageBorder() @@ -706,7 +735,7 @@ bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const bool Inkscape::ObjectSnapper::GuidesMightSnap() const // almost the same as ThisSnapperMightSnap above, but only looking at points (and not paths) { - bool snap_to_something = _snapmanager->snapprefs.getSnapToItemNode() + bool snap_to_something = _snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() || _snapmanager->snapprefs.getSnapToPageBorder() || (_snapmanager->snapprefs.getSnapModeBBox() && _snapmanager->snapprefs.getSnapToBBoxNode()) || (_snapmanager->snapprefs.getSnapModeBBox() && (_snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints())) diff --git a/src/object-snapper.h b/src/object-snapper.h index caf643f73..454a18545 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -46,7 +46,7 @@ public: void guideConstrainedSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal, - ConstraintLine const &c) const; + SnapConstraint const &c) const; bool ThisSnapperMightSnap() const; bool GuidesMightSnap() const; @@ -63,7 +63,7 @@ public: void constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const *it) const; private: @@ -98,7 +98,7 @@ private: void _snapPathsConstrained(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, // in desktop coordinates - ConstraintLine const &c) const; + SnapConstraint const &c) const; bool isUnselectedNode(Geom::Point const &point, std::vector<Inkscape::SnapCandidatePoint> const *unselected_nodes) const; diff --git a/src/pen-context.cpp b/src/pen-context.cpp index 5b9f6808a..4a21d7bcb 100644 --- a/src/pen-context.cpp +++ b/src/pen-context.cpp @@ -1470,7 +1470,7 @@ void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt } } else { // Create a horizontal or vertical constraint line - Inkscape::Snapper::ConstraintLine cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0)); + Inkscape::Snapper::SnapConstraint cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0)); // Snap along the constraint line; if we didn't snap then still the constraint will be applied SnapManager &m = pc->desktop->namedview->snap_manager; diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 73e7bd2ac..dd925490e 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -17,6 +17,7 @@ static char const preferences_skeleton[] = " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n" " xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\">\n" " <group id=\"window\">\n" +" <group id=\"task\" />\n" " <group id=\"menu\" state=\"1\"/>\n" " <group id=\"commands\" state=\"1\"/>\n" " <group id=\"snaptoolbox\" state=\"1\"/>\n" @@ -28,6 +29,7 @@ static char const preferences_skeleton[] = " <group id=\"scrollbars\" state=\"1\"/>\n" " </group>\n" " <group id=\"fullscreen\">\n" +" <group id=\"task\" />\n" " <group id=\"menu\" state=\"1\"/>\n" " <group id=\"commands\" state=\"1\"/>\n" " <group id=\"snaptoolbox\" state=\"1\"/>\n" @@ -39,6 +41,7 @@ static char const preferences_skeleton[] = " <group id=\"scrollbars\" state=\"1\"/>\n" " </group>\n" " <group id=\"focus\">\n" +" <group id=\"task\" />\n" " <group id=\"menu\" state=\"0\"/>\n" " <group id=\"commands\" state=\"0\"/>\n" " <group id=\"snaptoolbox\" state=\"0\"/>\n" @@ -340,6 +343,7 @@ static char const preferences_skeleton[] = " </group>\n" " <group id=\"workarounds\"\n" " colorsontop=\"0\"\n" +" floatallowed=\"0\"\n" " partialdynamic=\"0\"/>\n" " <group id=\"threading\" numthreads=\"1\"/>\n" " </group>\n" @@ -397,6 +401,10 @@ static char const preferences_skeleton[] = " show=\"1\"\n" " id=\"size16\" />\n" " <group\n" +" value=\"22\"\n" +" show=\"0\"\n" +" id=\"size22\" />\n" +" <group\n" " value=\"24\"\n" " show=\"1\"\n" " id=\"size24\" />\n" diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index cc153aa71..f1262e9ca 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -285,9 +285,9 @@ void sp_selection_delete_impl(GSList const *items, bool propagate = true, bool p sp_object_ref((SPObject *)i->data, NULL); } for (GSList const *i = items; i != NULL; i = i->next) { - SPItem *item = (SPItem *) i->data; - SP_OBJECT(item)->deleteObject(propagate, propagate_descendants); - sp_object_unref((SPObject *)item, NULL); + SPItem *item = reinterpret_cast<SPItem *>(i->data); + item->deleteObject(propagate, propagate_descendants); + sp_object_unref(item, NULL); } } @@ -442,7 +442,7 @@ void sp_edit_clear_all(SPDesktop *dt) GSList *items = sp_item_group_item_list(SP_GROUP(dt->currentLayer())); while (items) { - SP_OBJECT(items->data)->deleteObject(); + reinterpret_cast<SPObject*>(items->data)->deleteObject(); items = g_slist_remove(items, items->data); } @@ -453,7 +453,7 @@ void sp_edit_clear_all(SPDesktop *dt) GSList * get_all_items(GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, GSList const *exclude) { - for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (SPObject *child = sp_object_first_child(from) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_ITEM(child) && !desktop->isLayer(SP_ITEM(child)) && (!onlysensitive || !SP_ITEM(child)->isLocked()) && @@ -559,7 +559,7 @@ void sp_edit_invert_in_all_layers(SPDesktop *desktop) } void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc) { - + p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); // Remember the position and parent of the topmost object. @@ -638,9 +638,9 @@ void sp_selection_group(SPDesktop *desktop) } GSList const *l = (GSList *) selection->reprList(); - + GSList *p = g_slist_copy((GSList *) l); - + selection->clear(); Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); @@ -821,7 +821,7 @@ sp_selection_raise(SPDesktop *desktop) // Iterate over all objects in the selection (starting from top). if (selected) { while (rev) { - SPObject *child = SP_OBJECT(rev->data); + SPObject *child = reinterpret_cast<SPObject*>(rev->data); // for each selected object, find the next sibling for (SPObject *newref = child->next; newref; newref = newref->next) { // if the sibling is an item AND overlaps our selection, @@ -918,7 +918,7 @@ sp_selection_lower(SPDesktop *desktop) // Iterate over all objects in the selection (starting from top). if (selected) { while (rev) { - SPObject *child = SP_OBJECT(rev->data); + SPObject *child = reinterpret_cast<SPObject*>(rev->data); // for each selected object, find the prev sibling for (SPObject *newref = prev_sibling(child); newref; newref = prev_sibling(newref)) { // if the sibling is an item AND overlaps our selection, @@ -1022,16 +1022,16 @@ SPCSSAttr * take_style_from_item(SPItem *item) { // write the complete cascaded style, context-free - SPCSSAttr *css = sp_css_attr_from_object(SP_OBJECT(item), SP_STYLE_FLAG_ALWAYS); + SPCSSAttr *css = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); if (css == NULL) return NULL; - if ((SP_IS_GROUP(item) && SP_OBJECT(item)->children) || - (SP_IS_TEXT(item) && SP_OBJECT(item)->children && SP_OBJECT(item)->children->next == NULL)) { + if ((SP_IS_GROUP(item) && item->children) || + (SP_IS_TEXT(item) && item->children && item->children->next == NULL)) { // if this is a text with exactly one tspan child, merge the style of that tspan as well // If this is a group, merge the style of its topmost (last) child with style for (SPObject *last_element = item->lastChild(); last_element != NULL; last_element = SP_OBJECT_PREV(last_element)) { - if (SP_OBJECT_STYLE(last_element) != NULL) { + if ( last_element->style ) { SPCSSAttr *temp = sp_css_attr_from_object(last_element, SP_STYLE_FLAG_IFSET); if (temp) { sp_repr_css_merge(css, temp); @@ -1339,7 +1339,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons // we're moving both a clone and its original or any ancestor in clone chain? bool transform_clone_with_original = selection_contains_original(item, selection); // ...both a text-on-path and its path? - bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))) )); + bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(item))) )); // ...both a flowtext and its frame? bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame(NULL))); // (only the first frame is checked so far) // ...both an offset and its source? @@ -1374,7 +1374,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons * Same for linked offset if we are also moving its source: do not move it. */ if (transform_textpath_with_path || transform_offset_with_source) { // Restore item->transform field from the repr, in case it was changed by seltrans. - sp_object_read_attr(SP_OBJECT(item), "transform"); + sp_object_read_attr(item, "transform"); } else if (transform_flowtext_with_frame) { // apply the inverse of the region's transform to the <use> so that the flow remains // the same (even though the output itself gets transformed) @@ -1392,7 +1392,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons // transform and its move compensation are both cancelled out. // restore item->transform field from the repr, in case it was changed by seltrans - sp_object_read_attr(SP_OBJECT(item), "transform"); + sp_object_read_attr(item, "transform"); // calculate the matrix we need to apply to the clone to cancel its induced transform from its original Geom::Matrix parent2dt = sp_item_i2d_affine(SP_ITEM(SP_OBJECT_PARENT(item))); @@ -1434,7 +1434,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons // center by the same matrix (only necessary for non-translations) if (set_i2d && item->isCenterSet() && !(affine.isTranslation() || affine.isIdentity())) { item->setCenter(old_center * affine); - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); } } } @@ -2096,7 +2096,7 @@ sp_selection_relink(SPDesktop *desktop) continue; SP_OBJECT_REPR(item)->setAttribute("xlink:href", newref); - SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); relinked = true; } @@ -2134,10 +2134,10 @@ sp_selection_unlink(SPDesktop *desktop) SPItem *item = (SPItem *) items->data; if (SP_IS_TEXT(item)) { - SPObject *tspan = sp_tref_convert_to_tspan(SP_OBJECT(item)); + SPObject *tspan = sp_tref_convert_to_tspan(item); if (tspan) { - SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } // Set unlink to true, and fall into the next if which @@ -2155,7 +2155,7 @@ sp_selection_unlink(SPDesktop *desktop) if (SP_IS_USE(item)) { unlink = sp_use_unlink(SP_USE(item)); } else /*if (SP_IS_TREF(use))*/ { - unlink = SP_ITEM(sp_tref_convert_to_tspan(SP_OBJECT(item))); + unlink = SP_ITEM(sp_tref_convert_to_tspan(item)); } unlinked = true; @@ -2200,7 +2200,7 @@ sp_select_clone_original(SPDesktop *desktop) } else if (SP_IS_OFFSET(item) && SP_OFFSET(item)->sourceHref) { original = sp_offset_get_source(SP_OFFSET(item)); } else if (SP_IS_TEXT_TEXTPATH(item)) { - original = sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))); + original = sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(item))); } else if (SP_IS_FLOWTEXT(item)) { original = SP_FLOWTEXT(item)->get_frame(NULL); // first frame only } else { // it's an object that we don't know what to do with @@ -2302,7 +2302,7 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) if (apply) { // delete objects so that their clones don't get alerted; this object will be restored shortly for (GSList *i = items; i != NULL; i = i->next) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); item->deleteObject(false); } } @@ -2341,7 +2341,7 @@ static void sp_selection_to_guides_recursive(SPItem *item, bool deleteitem, bool sp_item_convert_item_to_guides(item); if (deleteitem) { - SP_OBJECT(item)->deleteObject(true); + item->deleteObject(true); } } } @@ -2426,7 +2426,7 @@ sp_selection_tile(SPDesktop *desktop, bool apply) if (apply) { // delete objects so that their clones don't get alerted; this object will be restored shortly for (GSList *i = items; i != NULL; i = i->next) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); item->deleteObject(false); } } @@ -2503,12 +2503,12 @@ sp_selection_untile(SPDesktop *desktop) SPItem *item = (SPItem *) items->data; - SPStyle *style = SP_OBJECT_STYLE(item); + SPStyle *style = item->style; if (!style || !style->fill.isPaintserver()) continue; - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); + SPPaintServer *server = item->style->getFillPaintServer(); if (!SP_IS_PATTERN(server)) continue; @@ -2520,7 +2520,7 @@ sp_selection_untile(SPDesktop *desktop) Geom::Matrix pat_transform = to_2geom(pattern_patternTransform(SP_PATTERN(server))); pat_transform *= item->transform; - for (SPObject *child = sp_object_first_child(SP_OBJECT(pattern)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (SPObject *child = sp_object_first_child(pattern) ; child != NULL; child = child->next ) { Inkscape::XML::Node *copy = SP_OBJECT_REPR(child)->duplicate(xml_doc); SPItem *i = SP_ITEM(desktop->currentLayer()->appendChildRepr(copy)); @@ -2860,7 +2860,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la GSList *items = g_slist_copy((GSList *) selection->itemList()); items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position); - + // See lp bug #542004 selection->clear(); @@ -2869,7 +2869,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la GSList *apply_to_items = NULL; GSList *items_to_delete = NULL; GSList *items_to_select = NULL; - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool topmost = prefs->getBool("/options/maskobject/topmost", true); bool remove_original = prefs->getBool("/options/maskobject/remove", true); @@ -2878,13 +2878,13 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la if (apply_to_layer) { // all selected items are used for mask, which is applied to a layer apply_to_items = g_slist_prepend(apply_to_items, desktop->currentLayer()); - + for (GSList *i = items; i != NULL; i = i->next) { Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc); mask_items = g_slist_prepend(mask_items, dup); - SPObject *item = SP_OBJECT(i->data); - if (remove_original) { + SPObject *item = reinterpret_cast<SPObject*>(i->data); + if (remove_original) { items_to_delete = g_slist_prepend(items_to_delete, item); } else { @@ -2898,7 +2898,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la mask_items = g_slist_prepend(mask_items, dup); if (remove_original) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); items_to_delete = g_slist_prepend(items_to_delete, item); } @@ -2917,7 +2917,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la mask_items = g_slist_prepend(mask_items, dup); if (remove_original) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); items_to_delete = g_slist_prepend(items_to_delete, item); } } @@ -2942,7 +2942,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la reprs_to_group = g_slist_reverse(reprs_to_group); sp_selection_group_impl(reprs_to_group, group, xml_doc, doc); - + reprs_to_group = NULL; // apply clip/mask only to newly created group @@ -3012,14 +3012,14 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la g_slist_free(apply_to_items); for (GSList *i = items_to_delete; NULL != i; i = i->next) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); item->deleteObject(false); items_to_select = g_slist_remove(items_to_select, item); } g_slist_free(items_to_delete); - + items_to_select = g_slist_reverse(items_to_select); - + selection->addList(items_to_select); g_slist_free(items_to_select); @@ -3053,11 +3053,11 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { GSList *items = g_slist_copy((GSList *) selection->itemList()); selection->clear(); - + GSList *items_to_ungroup = NULL; GSList *items_to_select = g_slist_copy(items); - items_to_select = g_slist_reverse(items_to_select); - + items_to_select = g_slist_reverse(items_to_select); + // SPObject* refers to a group containing the clipped path or mask itself, // whereas SPItem* refers to the item being clipped or masked @@ -3143,7 +3143,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } g_slist_free(items_to_ungroup); - + // rebuild selection items_to_select = g_slist_reverse(items_to_select); selection->addList(items_to_select); diff --git a/src/selection.cpp b/src/selection.cpp index 1e14591fa..96c66e0c5 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -424,14 +424,15 @@ boost::optional<Geom::Point> Selection::center() const { } /** - * Compute the list of points in the selection that are to be considered for snapping. + * Compute the list of points in the selection that are to be considered for snapping from. */ std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferences const *snapprefs) const { GSList const *items = const_cast<Selection *>(this)->itemList(); SnapPreferences snapprefs_dummy = *snapprefs; // create a local copy of the snapping prefs snapprefs_dummy.setIncludeItemCenter(false); // locally disable snapping to the item center - + snapprefs_dummy.setSnapToItemNode(true); // consider any type of nodes as a snap source + snapprefs_dummy.setSnapSmoothNodes(true); // i.e. disregard the smooth / cusp node preference std::vector<Inkscape::SnapCandidatePoint> p; for (GSList const *iter = items; iter != NULL; iter = iter->next) { SPItem *this_item = SP_ITEM(iter->data); @@ -446,13 +447,18 @@ std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferenc return p; } - +// TODO: both getSnapPoints and getSnapPointsConvexHull are called, subsequently. Can we do this more efficient? +// Why do we need to include the transformation center in one case and not the other? std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPointsConvexHull(SnapPreferences const *snapprefs) const { GSList const *items = const_cast<Selection *>(this)->itemList(); + SnapPreferences snapprefs_dummy = *snapprefs; // create a local copy of the snapping prefs + snapprefs_dummy.setSnapToItemNode(true); // consider any type of nodes as a snap source + snapprefs_dummy.setSnapSmoothNodes(true); // i.e. disregard the smooth / cusp node preference + std::vector<Inkscape::SnapCandidatePoint> p; for (GSList const *iter = items; iter != NULL; iter = iter->next) { - sp_item_snappoints(SP_ITEM(iter->data), p, snapprefs); + sp_item_snappoints(SP_ITEM(iter->data), p, &snapprefs_dummy); } std::vector<Inkscape::SnapCandidatePoint> pHull; diff --git a/src/seltrans.cpp b/src/seltrans.cpp index aef608420..9c83dd63e 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -289,13 +289,9 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s // Next, get all points to consider for snapping SnapManager const &m = _desktop->namedview->snap_manager; - Inkscape::SnapPreferences local_snapprefs = m.snapprefs; - local_snapprefs.setSnapToItemNode(true); // We should get at least the cusp nodes here. This might - // have been turned off because (for example) the user only want paths as a snap target, not nodes - // but as a snap source we still need some nodes though! _snap_points.clear(); - _snap_points = selection->getSnapPoints(&local_snapprefs); - std::vector<Inkscape::SnapCandidatePoint> snap_points_hull = selection->getSnapPointsConvexHull(&local_snapprefs); + _snap_points = selection->getSnapPoints(&m.snapprefs); + std::vector<Inkscape::SnapCandidatePoint> snap_points_hull = selection->getSnapPointsConvexHull(&m.snapprefs); if (_snap_points.size() > 200) { /* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes An average user would rarely ever try to snap such a large number of nodes anyway, because @@ -1213,7 +1209,7 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P SnapManager &m = _desktop->namedview->snap_manager; m.setup(_desktop, false, _items_const); - Inkscape::Snapper::ConstraintLine const constraint(component_vectors[dim_b]); + Inkscape::Snapper::SnapConstraint const constraint(component_vectors[dim_b]); // When skewing, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapSkew" for details Geom::Point const s(skew[dim_a], scale[dim_a]); Inkscape::SnappedPoint sn = m.constrainedSnapSkew(_snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b)); @@ -1280,7 +1276,10 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state) if (fabs(h2) < 1e-15) return FALSE; Geom::Point q2 = d2 / h2; // normalized new vector to handle - double radians; + Geom::Rotate r1(q1); + Geom::Rotate r2(q2); + + double radians = atan2(Geom::dot(Geom::rot90(d1), d2), Geom::dot(d1, d2));; if (state & GDK_CONTROL_MASK) { // Snap to defined angle increments double cos_t = Geom::dot(q1, q2); @@ -1289,15 +1288,25 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state) if (snaps) { radians = ( M_PI / snaps ) * floor( radians * snaps / M_PI + .5 ); } - q1 = Geom::Point(1, 0); - q2 = Geom::Point(cos(radians), sin(radians)); + r1 = Geom::Rotate(0); //q1 = Geom::Point(1, 0); + r2 = Geom::Rotate(radians); //q2 = Geom::Point(cos(radians), sin(radians)); } else { - radians = atan2(Geom::dot(Geom::rot90(d1), d2), - Geom::dot(d1, d2)); + SnapManager &m = _desktop->namedview->snap_manager; + m.setup(_desktop, false, _items_const); + // When rotating, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapRotate" for details + Inkscape::SnappedPoint sn = m.constrainedSnapRotate(_snap_points, _point, radians, _origin); + + if (sn.getSnapped()) { + _desktop->snapindicator->set_new_snaptarget(sn); + // We snapped something, so change the rotation to reflect it + radians = sn.getTransformation()[0]; + r1 = Geom::Rotate(0); + r2 = Geom::Rotate(radians); + } else { + _desktop->snapindicator->remove_snaptarget(); + } } - Geom::Rotate const r1(q1); - Geom::Rotate const r2(q2); // Calculate the relative affine _relative_affine = r2 * r1.inverse(); @@ -1464,14 +1473,14 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) // the constraint-line once. The constraint lines are parallel, but might not be colinear. // Therefore we will have to set the point through which the constraint-line runs // individually for each point to be snapped; this will be handled however by _snapTransformed() - s.push_back(m.constrainedSnapTranslation(_bbox_points_for_translating, + s.push_back(m.constrainedSnapTranslate(_bbox_points_for_translating, _point, - Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + Inkscape::Snapper::SnapConstraint(component_vectors[dim]), dxy)); - s.push_back(m.constrainedSnapTranslation(_snap_points, + s.push_back(m.constrainedSnapTranslate(_snap_points, _point, - Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + Inkscape::Snapper::SnapConstraint(component_vectors[dim]), dxy)); } else { // !control @@ -1481,8 +1490,8 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) g_get_current_time(&starttime); */ /* Snap to things with no constraint */ - s.push_back(m.freeSnapTranslation(_bbox_points_for_translating, _point, dxy)); - s.push_back(m.freeSnapTranslation(_snap_points, _point, dxy)); + s.push_back(m.freeSnapTranslate(_bbox_points_for_translating, _point, dxy)); + s.push_back(m.freeSnapTranslate(_snap_points, _point, dxy)); /*g_get_current_time(&endtime); double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0; @@ -1508,7 +1517,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) if (control) { // If we didn't snap, then we should still constrain horizontally or vertically // (When we did snap, then this constraint has already been enforced by - // calling constrainedSnapTranslation() above) + // calling constrainedSnapTranslate() above) if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) { dxy[Geom::Y] = 0; } else { diff --git a/src/seltrans.h b/src/seltrans.h index 8b2810621..0183683ff 100644 --- a/src/seltrans.h +++ b/src/seltrans.h @@ -24,6 +24,7 @@ #include "selcue.h" #include "message-context.h" #include <vector> +#include "sp-item.h" struct SPKnot; class SPDesktop; diff --git a/src/snap-enums.h b/src/snap-enums.h index cf09ba45b..64be34637 100644 --- a/src/snap-enums.h +++ b/src/snap-enums.h @@ -58,7 +58,7 @@ enum SnapSourceType { //------------------------------------------------------------------- // For the same reason, nodes will not snap to bbox points SNAPSOURCE_NODE_CATEGORY = 512, // will be used as a flag and must therefore be a power of two - SNAPSOURCE_NODE_SMOOTH, + SNAPSOURCE_NODE_SMOOTH, // Symmetrical nodes are also considered to be smooth; there's no dedicated type for symm. nodes SNAPSOURCE_NODE_CUSP, SNAPSOURCE_LINE_MIDPOINT, SNAPSOURCE_PATH_INTERSECTION, @@ -66,10 +66,10 @@ enum SnapSourceType { SNAPSOURCE_CONVEX_HULL_CORNER, SNAPSOURCE_ELLIPSE_QUADRANT_POINT, SNAPSOURCE_NODE_HANDLE, // eg. nodes in the path editor, handles of stars or rectangles, etc. (tied to a stroke) + SNAPSOURCE_OBJECT_MIDPOINT, //------------------------------------------------------------------- // Other points (e.g. guides, gradient knots) will snap to both bounding boxes and nodes SNAPSOURCE_OTHER_CATEGORY = 1024, // will be used as a flag and must therefore be a power of two - SNAPSOURCE_OBJECT_MIDPOINT, SNAPSOURCE_ROTATION_CENTER, SNAPSOURCE_CENTER, // of ellipse SNAPSOURCE_GUIDE, diff --git a/src/snap.cpp b/src/snap.cpp index c47f93ff1..265b7c19a 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -335,7 +335,7 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point c void SnapManager::constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap) const { Inkscape::SnappedPoint const s = constrainedSnap(Inkscape::SnapCandidatePoint(p, source_type, 0), constraint, bbox_to_snap); @@ -359,13 +359,19 @@ void SnapManager::constrainedSnapReturnByRef(Geom::Point &p, */ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint const &p, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap) const { // First project the mouse pointer onto the constraint Geom::Point pp = constraint.projection(p.getPoint()); - Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::L2(pp - p.getPoint()), 0, false, true, false); + Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, NR_HUGE, 0, false, true, false); + if (constraint.isCircular()) { + Geom::Point v_orig = constraint.getDirection(); // vector from the origin to the original (untransformed) point + Geom::Point v_proj = pp - constraint.getPoint(); // vector from the origin to the projected point + Geom::Coord angle = atan2(Geom::dot(Geom::rot90(v_orig), v_proj), Geom::dot(v_orig, v_proj)); + no_snap.setTransformation(Geom::Point(angle, angle)); // Store the rotation (in radians), needed in case of snapping while rotating + } if (!someSnapperMightSnap()) { // Always return point on constraint @@ -464,7 +470,7 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) // Snap to nodes or paths SnappedConstraints sc; - Inkscape::Snapper::ConstraintLine cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); + Inkscape::Snapper::SnapConstraint cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); if (object.ThisSnapperMightSnap()) { object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL); } @@ -509,7 +515,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer, bool constrained, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Transformation transformation_type, Geom::Point const &transformation, Geom::Point const &origin, @@ -559,8 +565,17 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point g_assert(best_snapped_point.getAtIntersection() == false); - std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin(); + // Warnings for the devs + if (constrained && transformation_type == SCALE && !uniform) { + g_warning("Non-uniform constrained scaling is not supported!"); + } + + if (!constrained && transformation_type == ROTATE) { + // We do not yet allow for simultaneous rotation and scaling + g_warning("Unconstrained rotation is not supported!"); + } + std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin(); // std::cout << std::endl; bool first_free_snap = true; @@ -568,27 +583,28 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( /* Snap it */ Inkscape::SnappedPoint snapped_point; - Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - Geom::Point const b = ((*i).getPoint() - origin); // vector to original point + Inkscape::Snapper::SnapConstraint dedicated_constraint = constraint; + Geom::Point const b = ((*i).getPoint() - origin); // vector to original point (not the transformed point! required for rotations!) if (constrained) { - if ((transformation_type == SCALE || transformation_type == STRETCH) && uniform) { + if (((transformation_type == SCALE || transformation_type == STRETCH) && uniform)) { // When uniformly scaling, each point will have its own unique constraint line, // running from the scaling origin to the original untransformed point. We will // calculate that line here - dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, b); + dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b); + } else if (transformation_type == ROTATE) { + // Geom::L2(b) is the radius of the circular constraint + dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b, Geom::L2(b)); } else if (transformation_type == STRETCH) { // when non-uniform stretching { - dedicated_constraint = Inkscape::Snapper::ConstraintLine((*i).getPoint(), component_vectors[dim]); - } else if (transformation_type == TRANSLATION) { + dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), component_vectors[dim]); + } else if (transformation_type == TRANSLATE) { // When doing a constrained translation, all points will move in the same direction, i.e. // either horizontally or vertically. The lines along which they move are therefore all - // parallel, but might not be colinear. Therefore we will have to set the point through - // which the constraint-line runs here, for each point individually. - dedicated_constraint.setPoint((*i).getPoint()); + // parallel, but might not be colinear. Therefore we will have to specify the point through + // which the constraint-line runs here, for each point individually. (we could also have done this + // earlier on, e.g. in seltrans.cpp but we're being lazy there and don't want to add an iteration loop) + dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), constraint.getDirection()); } // else: leave the original constraint, e.g. for skewing - if (transformation_type == SCALE && !uniform) { - g_warning("Non-uniform constrained scaling is not supported!"); - } snapped_point = constrainedSnap(*j, dedicated_constraint, bbox); } else { bool const c1 = fabs(b[Geom::X]) < 1e-6; @@ -597,7 +613,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // When scaling, a point aligned either horizontally or vertically with the origin can only // move in that specific direction; therefore it should only snap in that direction, otherwise // we will get snapped points with an invalid transformation - dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]); + dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, component_vectors[c1]); snapped_point = constrainedSnap(*j, dedicated_constraint, bbox); } else { // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping @@ -627,7 +643,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( //Geom::Point const b = (*i - origin); // vector to original point switch (transformation_type) { - case TRANSLATION: + case TRANSLATE: result = snapped_point.getPoint() - (*i).getPoint(); /* Consider the case in which a box is almost aligned with a grid in both * horizontal and vertical directions. The distance to the intersection of @@ -693,6 +709,12 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( snapped_point.setSnapDistance(std::abs(result[0] - transformation[0])); snapped_point.setSecondSnapDistance(NR_HUGE); break; + case ROTATE: + result = snapped_point.getTransformation(); + // Store the metric for this transformation as a virtual distance (we're storing an angle) + snapped_point.setSnapDistance(std::abs(result[0] - transformation[0])); + snapped_point.setSecondSnapDistance(NR_HUGE); + break; default: g_assert_not_reached(); } @@ -738,22 +760,21 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, +Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Point const &tr) const { if (p.size() == 1) { - Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + Geom::Point pt = _transformPoint(p.at(0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); } /** * \brief Apply a translation to a set of points and try to snap along a constraint * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param constraint The direction or line along which snapping must occur. @@ -761,24 +782,23 @@ Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::Sn * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, +Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &tr) const { if (p.size() == 1) { - Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + Geom::Point pt = _transformPoint(p.at(0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(p, pointer, true, constraint, TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + return _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); } /** * \brief Apply a scaling to a set of points and try to snap freely in 2 degrees-of-freedom * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param s Proposed scaling; the final scaling can only be calculated after snapping has occurred @@ -803,7 +823,6 @@ Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCand /** * \brief Apply a scaling to a set of points and snap such that the aspect ratio of the selection is preserved * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param s Proposed scaling; the final scaling can only be calculated after snapping has occurred @@ -828,7 +847,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::S /** * \brief Apply a stretch to a set of points and snap such that the direction of the stretch is preserved * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param s Proposed stretch; the final stretch can only be calculated after snapping has occurred @@ -856,7 +874,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape: /** * \brief Apply a skew to a set of points and snap such that the direction of the skew is preserved * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param constraint The direction or line along which snapping must occur. @@ -868,7 +885,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape: Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &s, Geom::Point const &o, Geom::Dim2 d) const @@ -893,6 +910,36 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::Sn } /** + * \brief Apply a rotation to a set of points and snap, without scaling + * + * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. + * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). + * \param angle Proposed rotation (in radians); the final rotation can only be calculated after snapping has occurred + * \param o Origin of the rotation + * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. + */ + +Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p, + Geom::Point const &pointer, + Geom::Coord const &angle, + Geom::Point const &o) const +{ + // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if + // the transformation of the bounding box is equal to the transformation of the individual nodes. This is + // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew, + // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping + // of bounding boxes is not allowed here. + + if (p.size() == 1) { + Geom::Point pt = _transformPoint(p.at(0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); + } + + return _snapTransformed(p, pointer, true, Geom::Point(0,0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false); + +} + +/** * \brief Given a set of possible snap targets, find the best target (which is not necessarily * also the nearest target), and show the snap indicator if requested * @@ -1116,7 +1163,7 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p, /* Work out the transformed version of this point */ Geom::Point transformed; switch (transformation_type) { - case TRANSLATION: + case TRANSLATE: transformed = p.getPoint() + transformation; break; case SCALE: @@ -1141,6 +1188,10 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p, // Apply that scale factor here transformed[1-dim] = (p.getPoint() - origin)[1 - dim] * transformation[1] + origin[1 - dim]; break; + case ROTATE: + // for rotations: transformation[0] stores the angle in radians + transformed = (p.getPoint() - origin) * Geom::Rotate(transformation[0]) + origin; + break; default: g_assert_not_reached(); } diff --git a/src/snap.h b/src/snap.h index 8a5688bea..26e599cc6 100644 --- a/src/snap.h +++ b/src/snap.h @@ -67,10 +67,11 @@ class SnapManager { public: enum Transformation { - TRANSLATION, + TRANSLATE, SCALE, STRETCH, - SKEW + SKEW, + ROTATE }; SnapManager(SPNamedView const *v); @@ -113,23 +114,23 @@ public: // point, by overwriting p, if snapping has occurred; otherwise p is untouched void constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapCandidatePoint const &p, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const; void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const; - Inkscape::SnappedPoint freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, + Inkscape::SnappedPoint freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Point const &tr) const; - Inkscape::SnappedPoint constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, + Inkscape::SnappedPoint constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &tr) const; Inkscape::SnappedPoint freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p, @@ -151,11 +152,16 @@ public: Inkscape::SnappedPoint constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor Geom::Point const &o, Geom::Dim2 d) const; + Inkscape::SnappedPoint constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p, + Geom::Point const &pointer, + Geom::Coord const &angle, + Geom::Point const &o) const; + Inkscape::GuideSnapper guide; ///< guide snapper Inkscape::ObjectSnapper object; ///< snapper to other objects Inkscape::SnapPreferences snapprefs; @@ -181,14 +187,11 @@ private: SPDesktop const *_desktop; bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored - //TODO: Make _unselected_nodes type safe; in the line above int is used for Inkscape::SnapTargetType, but if I remember - //correctly then in other cases the int is being used for Inkscape::SnapSourceType, or for both. How to make - //this type safe? Inkscape::SnappedPoint _snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer, bool constrained, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Transformation transformation_type, Geom::Point const &transformation, Geom::Point const &origin, diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp index e7df3cfad..77bc8280c 100644 --- a/src/snapped-curve.cpp +++ b/src/snapped-curve.cpp @@ -12,8 +12,10 @@ #include <2geom/crossing.h> #include <2geom/path-intersection.h> -Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox) +Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, int num_path, int num_segm, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox) { + _num_path = num_path; + _num_segm = num_segm; _distance = snapped_distance; _tolerance = std::max(snapped_tolerance, 1.0); _always_snap = always_snap; @@ -32,6 +34,8 @@ Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coo Inkscape::SnappedCurve::SnappedCurve() { + _num_path = 0; + _num_segm = 0; _distance = NR_HUGE; _tolerance = 1; _always_snap = false; @@ -68,6 +72,16 @@ Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &cur for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) { Geom::Point p_ix = this->_curve->pointAt((*i).ta); Geom::Coord dist = Geom::distance(p_ix, p); + + // Test if we have two segments (curves) from the same path.. + if (this->_num_path == curve._num_path) { + // Never try to intersect a segment with itself + if (this->_num_segm == curve._num_segm) continue; + // Two subsequent segments (curves) in a path will have a common node; this node is not considered to be an intersection + if (this->_num_segm == curve._num_segm + 1 && (*i).ta == 0 && (*i).tb == 1) continue; + if (this->_num_segm + 1 == curve._num_segm && (*i).ta == 1 && (*i).tb == 0) continue; + } + if (dist < best_dist) { best_dist = dist; best_p = p_ix; diff --git a/src/snapped-curve.h b/src/snapped-curve.h index 21124c678..595f84411 100644 --- a/src/snapped-curve.h +++ b/src/snapped-curve.h @@ -24,12 +24,14 @@ class SnappedCurve : public SnappedPoint { public: SnappedCurve(); - SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox); + SnappedCurve(Geom::Point const &snapped_point, int num_path, int num_segm, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox); ~SnappedCurve(); Inkscape::SnappedPoint intersect(SnappedCurve const &curve, Geom::Point const &p, Geom::Matrix dt2doc) const; //intersect with another SnappedCurve private: Geom::Curve const *_curve; + int _num_path; // Unique id of the path to which this segment belongs too + int _num_segm; // Sequence number of this segment in the path }; } diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp index 352f2dd1e..48efa10e6 100644 --- a/src/snapped-point.cpp +++ b/src/snapped-point.cpp @@ -138,7 +138,6 @@ bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &oth // there's more than one). It is not useful when trying to find the best snapped target point. // (both the snap distance and the pointer distance are measured in document pixels, not in screen pixels) if (weighted) { - Geom::Coord const dist_pointer_other = other_one.getPointerDistance(); Geom::Coord const dist_pointer_this = getPointerDistance(); // Weight factor: controls which node should be preferred for snapping, which is either @@ -171,10 +170,10 @@ bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &oth // When snapping to a constraint line only, which is not really a snap but merely a projection // to the constraint line, then give this snap a very low priority. Basically, any other snap will do if (other_one.getTarget() == SNAPTARGET_CONSTRAINT) { - dist_other += NR_HUGE/2; + dist_other += 1e6; } if (getTarget() == SNAPTARGET_CONSTRAINT) { - dist_this += NR_HUGE/2; + dist_this += 1e6; } // If it's closer @@ -194,13 +193,21 @@ bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &oth // But don't fall back... bool c4n = d && other_one.getAtIntersection() && !getAtIntersection(); - // or, if it's just as close then consider the second distance + // or, if it's just as close then consider the second distance ... bool c5a = (dist_other == dist_this); bool c5b = (other_one.getSecondSnapDistance() < getSecondSnapDistance()) && (getSecondSnapDistance() < NR_HUGE); + // ... or prefer free snaps over constrained snaps + bool c5c = !other_one.getConstrainedSnap() && getConstrainedSnap(); + + bool other_is_better = (c1 || c2 || c3 || c4 || (c5a && (c5b || c5c))) && !c2n && (!c3n || c2) && !c4n; + + /* + std::cout << other_one.getPoint() << " (Other one, dist = " << dist_other << ") vs. " << getPoint() << " (this one, dist = " << dist_this << ") ---> "; + std::cout << "c1 = " << c1 << " | c2 = " << c2 << " | c2n = " << c2n << " | c3 = " << c3 << " | c3n = " << c3n << " | c4 = " << c4 << " | c4n = " << c4n << " | c5a = " << c5a << " | c5b = " << c5b << " | c5c = " << c5c << std::endl; + std::cout << "Other one provides a better snap: " << other_is_better << std::endl; + */ - // std::cout << other_one.getPoint() << " (Other one, dist = " << dist_other << ") vs. " << getPoint() << " (this one, dist = " << dist_this << ") ---> "; - // std::cout << "c1 = " << c1 << " | c2 = " << c2 << " | c2n = " << c2n << " | c3 = " << c3 << " | c3n = " << c3n << " | c4 = " << c4 << " | c4n = " << c4n << " | c5a = " << c5a << " | c5b = " << c5b << std::endl; - return (c1 || c2 || c3 || c4 || (c5a && c5b)) && !c2n && (!c3n || c2) && !c4n; + return other_is_better; } /* diff --git a/src/snapper.h b/src/snapper.h index b5bb17de9..d8214db80 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -15,6 +15,7 @@ #include <map> #include <list> #include <boost/optional.hpp> +#include <glib.h> // for g_assert #include "snapped-point.h" #include "snapped-line.h" @@ -63,18 +64,29 @@ public: std::vector<SPItem const *> const */*it*/, std::vector<SnapCandidatePoint> */*unselected_nodes*/) const {}; - class ConstraintLine + // Class for storing the constraint for constrained snapping; can be + // - a line (infinite line with origin, running through _point pointing in _direction) + // - a direction (infinite line without origin, i.e. only a direction vector, stored in _direction) + // - a circle (_point denotes the center, _radius doesn't need an explanation, _direction contains + // the vector from the origin to the original untransformed point); + class SnapConstraint { + private: + enum SnapConstraintType {LINE, DIRECTION, CIRCLE}; + public: - ConstraintLine(Geom::Point const &d) : _has_point(false), _direction(d) {} - ConstraintLine(Geom::Point const &p, Geom::Point const &d) : _has_point(true), _point(p), _direction(d) {} - ConstraintLine(Geom::Line const &l) : _has_point(true), _point(l.origin()), _direction(l.versor()) {} + // Constructs a direction constraint, e.g. horizontal or vertical but without a specified point + SnapConstraint(Geom::Point const &d) : _direction(d), _type(DIRECTION) {} + // Constructs a linear constraint + SnapConstraint(Geom::Point const &p, Geom::Point const &d) : _point(p), _direction(d), _type(LINE) {} + SnapConstraint(Geom::Line const &l) : _point(l.origin()), _direction(l.versor()), _type(LINE) {} + // Constructs a circular constraint + SnapConstraint(Geom::Point const &p, Geom::Point const &d, Geom::Coord const &r) : _point(p), _direction(d), _radius(r), _type(CIRCLE) {} - bool hasPoint() const { - return _has_point; - } + bool hasPoint() const {return _type != DIRECTION;} Geom::Point getPoint() const { + g_assert(_type != DIRECTION); return _point; } @@ -82,28 +94,45 @@ public: return _direction; } - void setPoint(Geom::Point const &p) { - _point = p; - _has_point = true; + Geom::Coord getRadius() const { + g_assert(_type == CIRCLE); + return _radius; } - Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraintline - Geom::Point const p1_on_cl = _has_point ? _point : p; - Geom::Point const p2_on_cl = p1_on_cl + _direction; - return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); + bool isCircular() const { return _type == CIRCLE; } + bool isLinear() const { return _type == LINE; } + bool isDirection() const { return _type == DIRECTION; } + + Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraint + if (_type == CIRCLE) { + // project on to a circular constraint + Geom::Point v_orig = p - _point; + Geom::Coord l = Geom::L2(v_orig); + if (l > 0) { + return _point + _radius * v_orig/l; // Length of _direction is equal to the radius + } else { + // point to be projected is exactly at the center of the circle, so any point on the circle is a projection + return _point + Geom::Point(_radius, 0); + } + } else { + // project on to a linear constraint + Geom::Point const p1_on_cl = (_type == LINE) ? _point : p; + Geom::Point const p2_on_cl = p1_on_cl + _direction; + return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); + } } private: - - bool _has_point; Geom::Point _point; Geom::Point _direction; + Geom::Coord _radius; + SnapConstraintType _type; }; virtual void constrainedSnap(SnappedConstraints &/*sc*/, Inkscape::SnapCandidatePoint const &/*p*/, Geom::OptRect const &/*bbox_to_snap*/, - ConstraintLine const &/*c*/, + SnapConstraint const &/*c*/, std::vector<SPItem const *> const */*it*/) const {}; protected: diff --git a/src/sp-gradient-fns.h b/src/sp-gradient-fns.h index dafa1646f..aabc3eda7 100644 --- a/src/sp-gradient-fns.h +++ b/src/sp-gradient-fns.h @@ -13,22 +13,6 @@ class SPGradient; -#define SP_GRADIENT_STATE_IS_SET(g) (SP_GRADIENT(g)->state != SP_GRADIENT_STATE_UNKNOWN) -#define SP_GRADIENT_IS_VECTOR(g) (SP_GRADIENT(g)->state == SP_GRADIENT_STATE_VECTOR) -#define SP_GRADIENT_IS_PRIVATE(g) (SP_GRADIENT(g)->state == SP_GRADIENT_STATE_PRIVATE) -#define SP_GRADIENT_HAS_STOPS(g) (SP_GRADIENT(g)->has_stops) -#define SP_GRADIENT_SPREAD(g) (SP_GRADIENT(g)->spread) -#define SP_GRADIENT_UNITS(g) (SP_GRADIENT(g)->units) - -/** Forces vector to be built, if not present (i.e. changed) */ -void sp_gradient_ensure_vector(SPGradient *gradient); - -/** Ensures that color array is populated */ -void sp_gradient_ensure_colors(SPGradient *gradient); - -void sp_gradient_set_units(SPGradient *gr, SPGradientUnits units); -void sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread); - SPGradientSpread sp_gradient_get_spread (SPGradient *gradient); /* Gradient repr methods */ diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 3d4d69672..34912d1da 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -1,5 +1,3 @@ -#define __SP_GRADIENT_C__ - /** \file * SPGradient, SPStop, SPLinearGradient, SPRadialGradient. */ @@ -38,6 +36,7 @@ #include "svg/css-ostringstream.h" #include "attributes.h" #include "document-private.h" +#include "sp-gradient.h" #include "gradient-chemistry.h" #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" @@ -62,6 +61,29 @@ static Inkscape::XML::Node *sp_stop_write(SPObject *object, Inkscape::XML::Docum static SPObjectClass *stop_parent_class; +class SPGradientImpl +{ + friend class SPGradient; + + static void classInit(SPGradientClass *klass); + + static void init(SPGradient *gr); + static void build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); + static void release(SPObject *object); + static void modified(SPObject *object, guint flags); + static Inkscape::XML::Node *write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags); + + static void gradientRefModified(SPObject *href, guint flags, SPGradient *gradient); + static void gradientRefChanged(SPObject *old_ref, SPObject *ref, SPGradient *gr); + + static void childAdded(SPObject *object, + Inkscape::XML::Node *child, + Inkscape::XML::Node *ref); + static void removeChild(SPObject *object, Inkscape::XML::Node *child); + + static void setGradientAttr(SPObject *object, unsigned key, gchar const *value); +}; + /** * Registers SPStop class and returns its type. */ @@ -150,6 +172,7 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value) if (streq(p, "currentColor")) { stop->currentColor = true; } else { + // TODO need to properly read full color, including icc guint32 const color = sp_svg_read_color(p, 0); stop->specified_color.set( color ); } @@ -240,6 +263,43 @@ sp_stop_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML: return repr; } + +bool SPGradient::hasStops() const +{ + return has_stops; +} + +bool SPGradient::isUnitsSet() const +{ + return units_set; +} + +SPGradientUnits SPGradient::getUnits() const +{ + return units; +} + +bool SPGradient::isSpreadSet() const +{ + return spread_set; +} + +SPGradientSpread SPGradient::getSpread() const +{ + return spread; +} + +void SPGradient::setSwatch( bool swatch ) +{ + if ( swatch != isSwatch() ) { + this->swatch = swatch; // to make isSolid() work, this happens first + gchar const* paintVal = swatch ? (isSolid() ? "solid" : "gradient") : 0; + sp_object_setAttribute( this, "osb:paint", paintVal, 0 ); + + requestModified( SP_OBJECT_MODIFIED_FLAG ); + } +} + /** * Return stop's color as 32bit value. */ @@ -291,48 +351,23 @@ sp_stop_get_color(SPStop const *const stop) * Gradient */ -static void sp_gradient_class_init(SPGradientClass *klass); -static void sp_gradient_init(SPGradient *gr); - -static void sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static void sp_gradient_release(SPObject *object); -static void sp_gradient_set(SPObject *object, unsigned key, gchar const *value); -static void sp_gradient_child_added(SPObject *object, - Inkscape::XML::Node *child, - Inkscape::XML::Node *ref); -static void sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child); -static void sp_gradient_modified(SPObject *object, guint flags); -static Inkscape::XML::Node *sp_gradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, - guint flags); - -static void gradient_ref_modified(SPObject *href, guint flags, SPGradient *gradient); - -static bool sp_gradient_invalidate_vector(SPGradient *gr); -static void sp_gradient_rebuild_vector(SPGradient *gr); - -static void gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gradient); - -SPGradientSpread sp_gradient_get_spread(SPGradient *gradient); -SPGradientUnits sp_gradient_get_units(SPGradient *gradient); - static SPPaintServerClass *gradient_parent_class; /** * Registers SPGradient class and returns its type. */ -GType -sp_gradient_get_type() +GType SPGradient::getType() { static GType gradient_type = 0; if (!gradient_type) { GTypeInfo gradient_info = { sizeof(SPGradientClass), NULL, NULL, - (GClassInitFunc) sp_gradient_class_init, + (GClassInitFunc) SPGradientImpl::classInit, NULL, NULL, sizeof(SPGradient), 16, - (GInstanceInitFunc) sp_gradient_init, + (GInstanceInitFunc) SPGradientImpl::init, NULL, /* value_table */ }; gradient_type = g_type_register_static(SP_TYPE_PAINT_SERVER, "SPGradient", @@ -344,30 +379,28 @@ sp_gradient_get_type() /** * SPGradient vtable initialization. */ -static void -sp_gradient_class_init(SPGradientClass *klass) +void SPGradientImpl::classInit(SPGradientClass *klass) { SPObjectClass *sp_object_class = (SPObjectClass *) klass; gradient_parent_class = (SPPaintServerClass *)g_type_class_ref(SP_TYPE_PAINT_SERVER); - sp_object_class->build = sp_gradient_build; - sp_object_class->release = sp_gradient_release; - sp_object_class->set = sp_gradient_set; - sp_object_class->child_added = sp_gradient_child_added; - sp_object_class->remove_child = sp_gradient_remove_child; - sp_object_class->modified = sp_gradient_modified; - sp_object_class->write = sp_gradient_write; + sp_object_class->build = SPGradientImpl::build; + sp_object_class->release = SPGradientImpl::release; + sp_object_class->set = SPGradientImpl::setGradientAttr; + sp_object_class->child_added = SPGradientImpl::childAdded; + sp_object_class->remove_child = SPGradientImpl::removeChild; + sp_object_class->modified = SPGradientImpl::modified; + sp_object_class->write = SPGradientImpl::write; } /** * Callback for SPGradient object initialization. */ -static void -sp_gradient_init(SPGradient *gr) +void SPGradientImpl::init(SPGradient *gr) { gr->ref = new SPGradientReference(SP_OBJECT(gr)); - gr->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(gradient_ref_changed), gr)); + gr->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(SPGradientImpl::gradientRefChanged), gr)); /** \todo * Fixme: reprs being rearranged (e.g. via the XML editor) @@ -397,16 +430,20 @@ sp_gradient_init(SPGradient *gr) /** * Virtual build: set gradient attributes from its associated repr. */ -static void -sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +void SPGradientImpl::build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) { SPGradient *gradient = SP_GRADIENT(object); - if (((SPObjectClass *) gradient_parent_class)->build) + // Work-around in case a swatch had been marked for immediate collection: + if ( repr->attribute("osb:paint") && repr->attribute("inkscape:collect") ) { + repr->setAttribute("inkscape:collect", 0); + } + + if (((SPObjectClass *) gradient_parent_class)->build) { (* ((SPObjectClass *) gradient_parent_class)->build)(object, document, repr); + } - SPObject *ochild; - for ( ochild = sp_object_first_child(object) ; ochild ; ochild = SP_OBJECT_NEXT(ochild) ) { + for ( SPObject *ochild = sp_object_first_child(object); ochild; ochild = ochild->next ) { if (SP_IS_STOP(ochild)) { gradient->has_stops = TRUE; break; @@ -417,6 +454,7 @@ sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *r sp_object_read_attr(object, "gradientTransform"); sp_object_read_attr(object, "spreadMethod"); sp_object_read_attr(object, "xlink:href"); + sp_object_read_attr(object, "osb:paint"); /* Register ourselves */ sp_document_add_resource(document, "gradient", object); @@ -425,8 +463,7 @@ sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *r /** * Virtual release of SPGradient members before destruction. */ -static void -sp_gradient_release(SPObject *object) +void SPGradientImpl::release(SPObject *object) { SPGradient *gradient = (SPGradient *) object; @@ -460,8 +497,7 @@ sp_gradient_release(SPObject *object) /** * Set gradient attribute to value. */ -static void -sp_gradient_set(SPObject *object, unsigned key, gchar const *value) +void SPGradientImpl::setGradientAttr(SPObject *object, unsigned key, gchar const *value) { SPGradient *gr = SP_GRADIENT(object); @@ -519,9 +555,31 @@ sp_gradient_set(SPObject *object, unsigned key, gchar const *value) gr->ref->detach(); } break; + case SP_ATTR_OSB_SWATCH: + { + bool newVal = (value != 0); + bool modified = false; + if (newVal != gr->swatch) { + gr->swatch = newVal; + modified = true; + } + if (newVal) { + // Might need to flip solid/gradient + Glib::ustring paintVal = ( gr->hasStops() && (gr->getStopCount() == 0) ) ? "solid" : "gradient"; + if ( paintVal != value ) { + sp_object_setAttribute( gr, "osb:paint", paintVal.c_str(), 0 ); + modified = true; + } + } + if (modified) { + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + } + } + break; default: - if (((SPObjectClass *) gradient_parent_class)->set) + if (((SPObjectClass *) gradient_parent_class)->set) { ((SPObjectClass *) gradient_parent_class)->set(object, key, value); + } break; } } @@ -529,8 +587,7 @@ sp_gradient_set(SPObject *object, unsigned key, gchar const *value) /** * Gets called when the gradient is (re)attached to another gradient. */ -static void -gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gr) +void SPGradientImpl::gradientRefChanged(SPObject *old_ref, SPObject *ref, SPGradient *gr) { if (old_ref) { gr->modified_connection.disconnect(); @@ -538,38 +595,46 @@ gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gr) if ( SP_IS_GRADIENT(ref) && ref != gr ) { - gr->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&gradient_ref_modified), gr)); + gr->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&SPGradientImpl::gradientRefModified), gr)); } // Per SVG, all unset attributes must be inherited from linked gradient. // So, as we're now (re)linked, we assign linkee's values to this gradient if they are not yet set - // but without setting the _set flags. // FIXME: do the same for gradientTransform too - if (!gr->units_set) - gr->units = sp_gradient_get_units (gr); - if (!gr->spread_set) - gr->spread = sp_gradient_get_spread (gr); + if (!gr->units_set) { + gr->units = gr->fetchUnits(); + } + if (!gr->spread_set) { + gr->spread = gr->fetchSpread(); + } /// \todo Fixme: what should the flags (second) argument be? */ - gradient_ref_modified(ref, 0, gr); + gradientRefModified(ref, 0, gr); } /** * Callback for child_added event. */ -static void -sp_gradient_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref) +void SPGradientImpl::childAdded(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref) { SPGradient *gr = SP_GRADIENT(object); - sp_gradient_invalidate_vector(gr); + gr->invalidateVector(); - if (((SPObjectClass *) gradient_parent_class)->child_added) + if (((SPObjectClass *) gradient_parent_class)->child_added) { (* ((SPObjectClass *) gradient_parent_class)->child_added)(object, child, ref); + } SPObject *ochild = sp_object_get_child_by_repr(object, child); if ( ochild && SP_IS_STOP(ochild) ) { gr->has_stops = TRUE; + if ( gr->getStopCount() > 0 ) { + gchar const * attr = gr->repr->attribute("osb:paint"); + if ( attr && strcmp(attr, "gradient") ) { + sp_object_setAttribute( gr, "osb:paint", "gradient", 0 ); + } + } } /// \todo Fixme: should we schedule "modified" here? @@ -579,15 +644,15 @@ sp_gradient_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape:: /** * Callback for remove_child event. */ -static void -sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child) +void SPGradientImpl::removeChild(SPObject *object, Inkscape::XML::Node *child) { SPGradient *gr = SP_GRADIENT(object); - sp_gradient_invalidate_vector(gr); + gr->invalidateVector(); - if (((SPObjectClass *) gradient_parent_class)->remove_child) + if (((SPObjectClass *) gradient_parent_class)->remove_child) { (* ((SPObjectClass *) gradient_parent_class)->remove_child)(object, child); + } gr->has_stops = FALSE; SPObject *ochild; @@ -598,6 +663,14 @@ sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child) } } + if ( gr->getStopCount() == 0 ) { + g_message("Check on remove"); + gchar const * attr = gr->repr->attribute("osb:paint"); + if ( attr && strcmp(attr, "solid") ) { + sp_object_setAttribute( gr, "osb:paint", "solid", 0 ); + } + } + /* Fixme: should we schedule "modified" here? */ object->requestModified(SP_OBJECT_MODIFIED_FLAG); } @@ -605,17 +678,16 @@ sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child) /** * Callback for modified event. */ -static void -sp_gradient_modified(SPObject *object, guint flags) +void SPGradientImpl::modified(SPObject *object, guint flags) { SPGradient *gr = SP_GRADIENT(object); if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) { - sp_gradient_invalidate_vector(gr); + gr->invalidateVector(); } if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { - sp_gradient_ensure_colors(gr); + gr->ensureColors(); } if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; @@ -663,20 +735,21 @@ int SPGradient::getStopCount() const /** * Write gradient attributes to repr. */ -static Inkscape::XML::Node * -sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +Inkscape::XML::Node *SPGradientImpl::write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { SPGradient *gr = SP_GRADIENT(object); - if (((SPObjectClass *) gradient_parent_class)->write) + if (((SPObjectClass *) gradient_parent_class)->write) { (* ((SPObjectClass *) gradient_parent_class)->write)(object, xml_doc, repr, flags); + } if (flags & SP_OBJECT_WRITE_BUILD) { GSList *l = NULL; for (SPObject *child = sp_object_first_child(object); child; child = SP_OBJECT_NEXT(child)) { - Inkscape::XML::Node *crepr; - crepr = child->updateRepr(xml_doc, NULL, flags); - if (crepr) l = g_slist_prepend(l, crepr); + Inkscape::XML::Node *crepr = child->updateRepr(xml_doc, NULL, flags); + if (crepr) { + l = g_slist_prepend(l, crepr); + } } while (l) { repr->addChild((Inkscape::XML::Node *) l->data, NULL); @@ -710,7 +783,7 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape:: if ((flags & SP_OBJECT_WRITE_ALL) || gr->spread_set) { /* FIXME: Ensure that gr->spread is the inherited value - * if !gr->spread_set. Not currently happening: see sp_gradient_modified. + * if !gr->spread_set. Not currently happening: see SPGradient::modified. */ switch (gr->spread) { case SP_GRADIENT_SPREAD_REFLECT: @@ -725,6 +798,16 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape:: } } + if ( (flags & SP_OBJECT_WRITE_EXT) && gr->isSwatch() ) { + if ( gr->isSolid() ) { + repr->setAttribute( "osb:paint", "solid" ); + } else { + repr->setAttribute( "osb:paint", "gradient" ); + } + } else { + repr->setAttribute( "osb:paint", 0 ); + } + return repr; } @@ -733,40 +816,34 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape:: * * \pre SP_IS_GRADIENT(gradient). */ -void -sp_gradient_ensure_vector(SPGradient *gradient) +void SPGradient::ensureVector() { - g_return_if_fail(gradient != NULL); - g_return_if_fail(SP_IS_GRADIENT(gradient)); - - if (!gradient->vector.built) { - sp_gradient_rebuild_vector(gradient); + if ( !vector.built ) { + rebuildVector(); } } /** * Set units property of gradient and emit modified. */ -void -sp_gradient_set_units(SPGradient *gr, SPGradientUnits units) +void SPGradient::setUnits(SPGradientUnits units) { - if (units != gr->units) { - gr->units = units; - gr->units_set = TRUE; - SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG); + if (units != this->units) { + this->units = units; + units_set = TRUE; + requestModified(SP_OBJECT_MODIFIED_FLAG); } } /** * Set spread property of gradient and emit modified. */ -void -sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread) +void SPGradient::setSpread(SPGradientSpread spread) { - if (spread != gr->spread) { - gr->spread = spread; - gr->spread_set = TRUE; - SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG); + if (spread != this->spread) { + this->spread = spread; + spread_set = TRUE; + requestModified(SP_OBJECT_MODIFIED_FLAG); } } @@ -817,16 +894,15 @@ chase_hrefs(SPGradient *const src, bool (*match)(SPGradient const *)) */ static bool has_stopsFN(SPGradient const *gr) { - return SP_GRADIENT_HAS_STOPS(gr); + return gr->hasStops(); } /** * True if gradient has spread set. */ -static bool -has_spread_set(SPGradient const *gr) +static bool has_spread_set(SPGradient const *gr) { - return gr->spread_set; + return gr->isSpreadSet(); } /** @@ -835,7 +911,7 @@ has_spread_set(SPGradient const *gr) static bool has_units_set(SPGradient const *gr) { - return gr->units_set; + return gr->isUnitsSet(); } @@ -854,12 +930,9 @@ SPGradient *SPGradient::getVector(bool force_vector) * * \pre SP_IS_GRADIENT(gradient). */ -SPGradientSpread -sp_gradient_get_spread(SPGradient *gradient) +SPGradientSpread SPGradient::fetchSpread() { - g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_SPREAD_PAD); - - SPGradient const *src = chase_hrefs(gradient, has_spread_set); + SPGradient const *src = chase_hrefs(this, has_spread_set); return ( src ? src->spread : SP_GRADIENT_SPREAD_PAD ); // pad is the default @@ -870,12 +943,9 @@ sp_gradient_get_spread(SPGradient *gradient) * * \pre SP_IS_GRADIENT(gradient). */ -SPGradientUnits -sp_gradient_get_units(SPGradient *gradient) +SPGradientUnits SPGradient::fetchUnits() { - g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX); - - SPGradient const *src = chase_hrefs(gradient, has_units_set); + SPGradient const *src = chase_hrefs(this, has_units_set); return ( src ? src->units : SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ); // bbox is the default @@ -950,30 +1020,28 @@ sp_gradient_repr_write_vector(SPGradient *gr) } -static void -gradient_ref_modified(SPObject */*href*/, guint /*flags*/, SPGradient *gradient) +void SPGradientImpl::gradientRefModified(SPObject */*href*/, guint /*flags*/, SPGradient *gradient) { - if (sp_gradient_invalidate_vector(gradient)) { + if ( gradient->invalidateVector() ) { SP_OBJECT(gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); - /* Conditional to avoid causing infinite loop if there's a cycle in the href chain. */ + // Conditional to avoid causing infinite loop if there's a cycle in the href chain. } } -/** Return true iff change made. */ -static bool -sp_gradient_invalidate_vector(SPGradient *gr) +/** Return true if change made. */ +bool SPGradient::invalidateVector() { bool ret = false; - if (gr->color != NULL) { - g_free(gr->color); - gr->color = NULL; + if (color != NULL) { + g_free(color); + color = NULL; ret = true; } - if (gr->vector.built) { - gr->vector.built = false; - gr->vector.stops.clear(); + if (vector.built) { + vector.built = false; + vector.stops.clear(); ret = true; } @@ -981,11 +1049,10 @@ sp_gradient_invalidate_vector(SPGradient *gr) } /** Creates normalized color vector */ -static void -sp_gradient_rebuild_vector(SPGradient *gr) +void SPGradient::rebuildVector() { gint len = 0; - for ( SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ; + for ( SPObject *child = sp_object_first_child(SP_OBJECT(this)) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_STOP(child)) { @@ -993,36 +1060,36 @@ sp_gradient_rebuild_vector(SPGradient *gr) } } - gr->has_stops = (len != 0); + has_stops = (len != 0); - gr->vector.stops.clear(); + vector.stops.clear(); - SPGradient *ref = gr->ref->getObject(); - if ( !gr->has_stops && ref ) { + SPGradient *reffed = ref->getObject(); + if ( !hasStops() && reffed ) { /* Copy vector from referenced gradient */ - gr->vector.built = true; // Prevent infinite recursion. - sp_gradient_ensure_vector(ref); - if (!ref->vector.stops.empty()) { - gr->vector.built = ref->vector.built; - gr->vector.stops.assign(ref->vector.stops.begin(), ref->vector.stops.end()); + vector.built = true; // Prevent infinite recursion. + reffed->ensureVector(); + if (!reffed->vector.stops.empty()) { + vector.built = reffed->vector.built; + vector.stops.assign(reffed->vector.stops.begin(), reffed->vector.stops.end()); return; } } - for (SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ; + for (SPObject *child = sp_object_first_child(SP_OBJECT(this)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_STOP(child)) { SPStop *stop = SP_STOP(child); SPGradientStop gstop; - if (gr->vector.stops.size() > 0) { + if (vector.stops.size() > 0) { // "Each gradient offset value is required to be equal to or greater than the // previous gradient stop's offset value. If a given gradient stop's offset // value is not equal to or greater than all previous offset values, then the // offset value is adjusted to be equal to the largest of all previous offset // values." - gstop.offset = MAX(stop->offset, gr->vector.stops.back().offset); + gstop.offset = MAX(stop->offset, vector.stops.back().offset); } else { gstop.offset = stop->offset; } @@ -1035,12 +1102,12 @@ sp_gradient_rebuild_vector(SPGradient *gr) gstop.color = sp_stop_get_color(stop); gstop.opacity = stop->opacity; - gr->vector.stops.push_back(gstop); + vector.stops.push_back(gstop); } } // Normalize per section 13.2.4 of SVG 1.1. - if (gr->vector.stops.size() == 0) { + if (vector.stops.size() == 0) { /* "If no stops are defined, then painting shall occur as if 'none' were specified as the * paint style." */ @@ -1049,72 +1116,71 @@ sp_gradient_rebuild_vector(SPGradient *gr) gstop.offset = 0.0; gstop.color.set( 0x00000000 ); gstop.opacity = 0.0; - gr->vector.stops.push_back(gstop); + vector.stops.push_back(gstop); } { SPGradientStop gstop; gstop.offset = 1.0; gstop.color.set( 0x00000000 ); gstop.opacity = 0.0; - gr->vector.stops.push_back(gstop); + vector.stops.push_back(gstop); } } else { /* "If one stop is defined, then paint with the solid color fill using the color defined * for that gradient stop." */ - if (gr->vector.stops.front().offset > 0.0) { + if (vector.stops.front().offset > 0.0) { // If the first one is not at 0, then insert a copy of the first at 0. SPGradientStop gstop; gstop.offset = 0.0; - gstop.color = gr->vector.stops.front().color; - gstop.opacity = gr->vector.stops.front().opacity; - gr->vector.stops.insert(gr->vector.stops.begin(), gstop); + gstop.color = vector.stops.front().color; + gstop.opacity = vector.stops.front().opacity; + vector.stops.insert(vector.stops.begin(), gstop); } - if (gr->vector.stops.back().offset < 1.0) { + if (vector.stops.back().offset < 1.0) { // If the last one is not at 1, then insert a copy of the last at 1. SPGradientStop gstop; gstop.offset = 1.0; - gstop.color = gr->vector.stops.back().color; - gstop.opacity = gr->vector.stops.back().opacity; - gr->vector.stops.push_back(gstop); + gstop.color = vector.stops.back().color; + gstop.opacity = vector.stops.back().opacity; + vector.stops.push_back(gstop); } } - gr->vector.built = true; + vector.built = true; } /** * The gradient's color array is newly created and set up from vector. */ -void -sp_gradient_ensure_colors(SPGradient *gr) +void SPGradient::ensureColors() { - if (!gr->vector.built) { - sp_gradient_rebuild_vector(gr); + if (!vector.built) { + rebuildVector(); } - g_return_if_fail(!gr->vector.stops.empty()); + g_return_if_fail(!vector.stops.empty()); /// \todo Where is the memory freed? - if (!gr->color) { - gr->color = g_new(guchar, 4 * NCOLORS); + if (!color) { + color = g_new(guchar, 4 * NCOLORS); } - // This assumes that gr->vector is a zero-order B-spline (box function) approximation of the "true" gradient. + // This assumes that vector is a zero-order B-spline (box function) approximation of the "true" gradient. // This means that the "true" gradient must be prefiltered using a zero order B-spline and then sampled. // Furthermore, the first element corresponds to offset="0" and the last element to offset="1". double remainder[4] = {0,0,0,0}; double remainder_for_end[4] = {0,0,0,0}; // Used at the end - switch(gr->spread) { + switch(spread) { case SP_GRADIENT_SPREAD_PAD: - remainder[0] = 0.5*gr->vector.stops[0].color.v.c[0]; // Half of the first cell uses the color of the first stop - remainder[1] = 0.5*gr->vector.stops[0].color.v.c[1]; - remainder[2] = 0.5*gr->vector.stops[0].color.v.c[2]; - remainder[3] = 0.5*gr->vector.stops[0].opacity; - remainder_for_end[0] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].color.v.c[0]; // Half of the first cell uses the color of the last stop - remainder_for_end[1] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].color.v.c[1]; - remainder_for_end[2] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].color.v.c[2]; - remainder_for_end[3] = 0.5*gr->vector.stops[gr->vector.stops.size() - 1].opacity; + remainder[0] = 0.5*vector.stops[0].color.v.c[0]; // Half of the first cell uses the color of the first stop + remainder[1] = 0.5*vector.stops[0].color.v.c[1]; + remainder[2] = 0.5*vector.stops[0].color.v.c[2]; + remainder[3] = 0.5*vector.stops[0].opacity; + remainder_for_end[0] = 0.5*vector.stops[vector.stops.size() - 1].color.v.c[0]; // Half of the first cell uses the color of the last stop + remainder_for_end[1] = 0.5*vector.stops[vector.stops.size() - 1].color.v.c[1]; + remainder_for_end[2] = 0.5*vector.stops[vector.stops.size() - 1].color.v.c[2]; + remainder_for_end[3] = 0.5*vector.stops[vector.stops.size() - 1].opacity; break; case SP_GRADIENT_SPREAD_REFLECT: case SP_GRADIENT_SPREAD_REPEAT: @@ -1123,17 +1189,17 @@ sp_gradient_ensure_colors(SPGradient *gr) default: g_error("Spread type not supported!"); }; - for (unsigned int i = 0; i < gr->vector.stops.size() - 1; i++) { - double r0 = gr->vector.stops[i].color.v.c[0]; - double g0 = gr->vector.stops[i].color.v.c[1]; - double b0 = gr->vector.stops[i].color.v.c[2]; - double a0 = gr->vector.stops[i].opacity; - double r1 = gr->vector.stops[i+1].color.v.c[0]; - double g1 = gr->vector.stops[i+1].color.v.c[1]; - double b1 = gr->vector.stops[i+1].color.v.c[2]; - double a1 = gr->vector.stops[i+1].opacity; - double o0 = gr->vector.stops[i].offset * (NCOLORS-1); - double o1 = gr->vector.stops[i + 1].offset * (NCOLORS-1); + for (unsigned int i = 0; i < vector.stops.size() - 1; i++) { + double r0 = vector.stops[i].color.v.c[0]; + double g0 = vector.stops[i].color.v.c[1]; + double b0 = vector.stops[i].color.v.c[2]; + double a0 = vector.stops[i].opacity; + double r1 = vector.stops[i+1].color.v.c[0]; + double g1 = vector.stops[i+1].color.v.c[1]; + double b1 = vector.stops[i+1].color.v.c[2]; + double a1 = vector.stops[i+1].opacity; + double o0 = vector.stops[i].offset * (NCOLORS-1); + double o1 = vector.stops[i + 1].offset * (NCOLORS-1); unsigned int ob = (unsigned int) floor(o0+.5); // These are the first and last element that might be affected by this interval. unsigned int oe = (unsigned int) floor(o1+.5); // These need to be computed the same to ensure that ob will be covered by the next interval if oe==ob @@ -1156,10 +1222,10 @@ sp_gradient_ensure_colors(SPGradient *gr) double df = 1. / (o1-o0); for (unsigned int j = ob+1; j < oe; j++) { f += df; - gr->color[4 * j + 0] = (unsigned char) floor(255*(r0 + f*(r1-r0)) + .5); - gr->color[4 * j + 1] = (unsigned char) floor(255*(g0 + f*(g1-g0)) + .5); - gr->color[4 * j + 2] = (unsigned char) floor(255*(b0 + f*(b1-b0)) + .5); - gr->color[4 * j + 3] = (unsigned char) floor(255*(a0 + f*(a1-a0)) + .5); + color[4 * j + 0] = (unsigned char) floor(255*(r0 + f*(r1-r0)) + .5); + color[4 * j + 1] = (unsigned char) floor(255*(g0 + f*(g1-g0)) + .5); + color[4 * j + 2] = (unsigned char) floor(255*(b0 + f*(b1-b0)) + .5); + color[4 * j + 3] = (unsigned char) floor(255*(a0 + f*(a1-a0)) + .5); } // Now handle the beginning @@ -1171,13 +1237,13 @@ sp_gradient_ensure_colors(SPGradient *gr) // = (ob+.5-o0)*(c0+(ob+.5-o0)*df*(c1-c0)/2) double dt = ob+.5-o0; f = 0.5*dt*df; - if (ob==0 && gr->spread==SP_GRADIENT_SPREAD_REFLECT) { + if (ob==0 && spread==SP_GRADIENT_SPREAD_REFLECT) { // The first half of the first cell is just a mirror image of the second half, so simply multiply it by 2. - gr->color[4 * ob + 0] = (unsigned char) floor(2*255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); - gr->color[4 * ob + 1] = (unsigned char) floor(2*255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); - gr->color[4 * ob + 2] = (unsigned char) floor(2*255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); - gr->color[4 * ob + 3] = (unsigned char) floor(2*255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); - } else if (ob==0 && gr->spread==SP_GRADIENT_SPREAD_REPEAT) { + color[4 * ob + 0] = (unsigned char) floor(2*255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); + color[4 * ob + 1] = (unsigned char) floor(2*255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); + color[4 * ob + 2] = (unsigned char) floor(2*255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); + color[4 * ob + 3] = (unsigned char) floor(2*255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); + } else if (ob==0 && spread==SP_GRADIENT_SPREAD_REPEAT) { // The first cell is the same as the last cell, so save whatever is in the second half here and deal with the rest later. remainder_for_end[0] = remainder[0] + dt*(r0 + f*(r1-r0)); remainder_for_end[1] = remainder[1] + dt*(g0 + f*(g1-g0)); @@ -1185,10 +1251,10 @@ sp_gradient_ensure_colors(SPGradient *gr) remainder_for_end[3] = remainder[3] + dt*(a0 + f*(a1-a0)); } else { // The first half of the cell was already in remainder. - gr->color[4 * ob + 0] = (unsigned char) floor(255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); - gr->color[4 * ob + 1] = (unsigned char) floor(255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); - gr->color[4 * ob + 2] = (unsigned char) floor(255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); - gr->color[4 * ob + 3] = (unsigned char) floor(255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); + color[4 * ob + 0] = (unsigned char) floor(255*(remainder[0] + dt*(r0 + f*(r1-r0))) + .5); + color[4 * ob + 1] = (unsigned char) floor(255*(remainder[1] + dt*(g0 + f*(g1-g0))) + .5); + color[4 * ob + 2] = (unsigned char) floor(255*(remainder[2] + dt*(b0 + f*(b1-b0))) + .5); + color[4 * ob + 3] = (unsigned char) floor(255*(remainder[3] + dt*(a0 + f*(a1-a0))) + .5); } // Now handle the end, which should end up in remainder @@ -1204,26 +1270,26 @@ sp_gradient_ensure_colors(SPGradient *gr) remainder[3] = dt*(a0 + f*(a1-a0)); } } - switch(gr->spread) { + switch(spread) { case SP_GRADIENT_SPREAD_PAD: - gr->color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); - gr->color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); - gr->color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); - gr->color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); + color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); + color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); + color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); + color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); break; case SP_GRADIENT_SPREAD_REFLECT: // The second half is the same as the first half, so multiply by 2. - gr->color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(2*255*remainder[0] + .5); - gr->color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(2*255*remainder[1] + .5); - gr->color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(2*255*remainder[2] + .5); - gr->color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(2*255*remainder[3] + .5); + color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(2*255*remainder[0] + .5); + color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(2*255*remainder[1] + .5); + color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(2*255*remainder[2] + .5); + color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(2*255*remainder[3] + .5); break; case SP_GRADIENT_SPREAD_REPEAT: // The second half is the same as the second half of the first cell (which was saved in remainder_for_end). - gr->color[0] = gr->color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); - gr->color[1] = gr->color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); - gr->color[2] = gr->color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); - gr->color[3] = gr->color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); + color[0] = color[4 * (NCOLORS-1) + 0] = (unsigned char) floor(255*(remainder[0]+remainder_for_end[0]) + .5); + color[1] = color[4 * (NCOLORS-1) + 1] = (unsigned char) floor(255*(remainder[1]+remainder_for_end[1]) + .5); + color[2] = color[4 * (NCOLORS-1) + 2] = (unsigned char) floor(255*(remainder[2]+remainder_for_end[2]) + .5); + color[3] = color[4 * (NCOLORS-1) + 3] = (unsigned char) floor(255*(remainder[3]+remainder_for_end[3]) + .5); break; } } @@ -1250,7 +1316,7 @@ sp_gradient_render_vector_line_rgba(SPGradient *const gradient, guchar *buf, g_return_if_fail(span > 0); if (!gradient->color) { - sp_gradient_ensure_colors(gradient); + gradient->ensureColors(); } gint idx = (pos * 1024 << 8) / span; @@ -1359,7 +1425,7 @@ sp_gradient_render_vector_block_rgb(SPGradient *gradient, guchar *buf, Geom::Matrix sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Matrix const &ctm, Geom::Rect const &bbox) { - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { return ( Geom::Scale(bbox.dimensions()) * Geom::Translate(bbox.min()) * Geom::Matrix(ctm) ); @@ -1371,7 +1437,7 @@ sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Matrix const &ctm, Geom:: Geom::Matrix sp_gradient_get_gs2d_matrix(SPGradient const *gr, Geom::Matrix const &ctm, Geom::Rect const &bbox) { - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { return ( gr->gradientTransform * Geom::Scale(bbox.dimensions()) * Geom::Translate(bbox.min()) @@ -1386,7 +1452,7 @@ sp_gradient_set_gs2d_matrix(SPGradient *gr, Geom::Matrix const &ctm, Geom::Rect const &bbox, Geom::Matrix const &gs2d) { gr->gradientTransform = gs2d * ctm.inverse(); - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) { gr->gradientTransform = ( gr->gradientTransform * Geom::Translate(-bbox.min()) * Geom::Scale(bbox.dimensions()).inverse() ); @@ -1408,6 +1474,11 @@ struct SPLGPainter { SPLinearGradient *lg; NRLGradientRenderer lgr; + + static SPPainter * painter_new(SPPaintServer *ps, + Geom::Matrix const &full_transform, + Geom::Matrix const &parent_transform, + NRRect const *bbox); }; static void sp_lineargradient_class_init(SPLinearGradientClass *klass); @@ -1420,10 +1491,6 @@ static void sp_lineargradient_set(SPObject *object, unsigned key, gchar const *v static Inkscape::XML::Node *sp_lineargradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); -static SPPainter *sp_lineargradient_painter_new(SPPaintServer *ps, - Geom::Matrix const &full_transform, - Geom::Matrix const &parent_transform, - NRRect const *bbox); static void sp_lineargradient_painter_free(SPPaintServer *ps, SPPainter *painter); static void sp_lg_fill(SPPainter *painter, NRPixBlock *pb); @@ -1467,7 +1534,7 @@ static void sp_lineargradient_class_init(SPLinearGradientClass *klass) sp_object_class->set = sp_lineargradient_set; sp_object_class->write = sp_lineargradient_write; - ps_class->painter_new = sp_lineargradient_painter_new; + ps_class->painter_new = SPLGPainter::painter_new; ps_class->painter_free = sp_lineargradient_painter_free; } @@ -1573,16 +1640,17 @@ sp_lineargradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inks * \todo (point 1 above) fixme: I do not know how to deal with start > 0 * and end < 1. */ -static SPPainter * -sp_lineargradient_painter_new(SPPaintServer *ps, - Geom::Matrix const &full_transform, - Geom::Matrix const &/*parent_transform*/, - NRRect const *bbox) +SPPainter * SPLGPainter::painter_new(SPPaintServer *ps, + Geom::Matrix const &full_transform, + Geom::Matrix const &/*parent_transform*/, + NRRect const *bbox) { SPLinearGradient *lg = SP_LINEARGRADIENT(ps); SPGradient *gr = SP_GRADIENT(ps); - if (!gr->color) sp_gradient_ensure_colors(gr); + if (!gr->color) { + gr->ensureColors(); + } SPLGPainter *lgp = g_new(SPLGPainter, 1); @@ -1623,7 +1691,7 @@ sp_lineargradient_painter_new(SPPaintServer *ps, } // TODO: remove color2px_nr after converting to 2geom NR::Matrix color2px_nr = from_2geom(color2px); - nr_lgradient_renderer_setup(&lgp->lgr, gr->color, sp_gradient_get_spread(gr), &color2px_nr, + nr_lgradient_renderer_setup(&lgp->lgr, gr->color, gr->fetchSpread(), &color2px_nr, lg->x1.computed, lg->y1.computed, lg->x2.computed, lg->y2.computed); @@ -1665,7 +1733,7 @@ sp_lg_fill(SPPainter *painter, NRPixBlock *pb) SPLGPainter *lgp = (SPLGPainter *) painter; if (lgp->lg->color == NULL) { - sp_gradient_ensure_colors (lgp->lg); + lgp->lg->ensureColors(); lgp->lgr.vector = lgp->lg->color; } @@ -1683,6 +1751,11 @@ struct SPRGPainter { SPPainter painter; SPRadialGradient *rg; NRRGradientRenderer rgr; + + static SPPainter *painter_new(SPPaintServer *ps, + Geom::Matrix const &full_transform, + Geom::Matrix const &parent_transform, + NRRect const *bbox); }; static void sp_radialgradient_class_init(SPRadialGradientClass *klass); @@ -1694,11 +1767,6 @@ static void sp_radialgradient_build(SPObject *object, static void sp_radialgradient_set(SPObject *object, unsigned key, gchar const *value); static Inkscape::XML::Node *sp_radialgradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); - -static SPPainter *sp_radialgradient_painter_new(SPPaintServer *ps, - Geom::Matrix const &full_transform, - Geom::Matrix const &parent_transform, - NRRect const *bbox); static void sp_radialgradient_painter_free(SPPaintServer *ps, SPPainter *painter); static void sp_rg_fill(SPPainter *painter, NRPixBlock *pb); @@ -1742,7 +1810,7 @@ static void sp_radialgradient_class_init(SPRadialGradientClass *klass) sp_object_class->set = sp_radialgradient_set; sp_object_class->write = sp_radialgradient_write; - ps_class->painter_new = sp_radialgradient_painter_new; + ps_class->painter_new = SPRGPainter::painter_new; ps_class->painter_free = sp_radialgradient_painter_free; } @@ -1856,16 +1924,17 @@ sp_radialgradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inks /** * Create radial gradient context. */ -static SPPainter * -sp_radialgradient_painter_new(SPPaintServer *ps, - Geom::Matrix const &full_transform, - Geom::Matrix const &/*parent_transform*/, - NRRect const *bbox) +SPPainter *SPRGPainter::painter_new(SPPaintServer *ps, + Geom::Matrix const &full_transform, + Geom::Matrix const &/*parent_transform*/, + NRRect const *bbox) { SPRadialGradient *rg = SP_RADIALGRADIENT(ps); SPGradient *gr = SP_GRADIENT(ps); - if (!gr->color) sp_gradient_ensure_colors(gr); + if (!gr->color) { + gr->ensureColors(); + } SPRGPainter *rgp = g_new(SPRGPainter, 1); @@ -1899,7 +1968,7 @@ sp_radialgradient_painter_new(SPPaintServer *ps, } // TODO: remove gs2px_nr after converting to 2geom NR::Matrix gs2px_nr = from_2geom(gs2px); - nr_rgradient_renderer_setup(&rgp->rgr, gr->color, sp_gradient_get_spread(gr), + nr_rgradient_renderer_setup(&rgp->rgr, gr->color, gr->fetchSpread(), &gs2px_nr, rg->cx.computed, rg->cy.computed, rg->fx.computed, rg->fy.computed, @@ -1943,7 +2012,7 @@ sp_rg_fill(SPPainter *painter, NRPixBlock *pb) SPRGPainter *rgp = (SPRGPainter *) painter; if (rgp->rg->color == NULL) { - sp_gradient_ensure_colors (rgp->rg); + rgp->rg->ensureColors(); rgp->rgr.vector = rgp->rg->color; } diff --git a/src/sp-gradient.h b/src/sp-gradient.h index e7488673d..b05cb5fb8 100644 --- a/src/sp-gradient.h +++ b/src/sp-gradient.h @@ -29,14 +29,12 @@ struct SPGradientReference; -#define SP_TYPE_GRADIENT (sp_gradient_get_type()) +#define SP_TYPE_GRADIENT (SPGradient::getType()) #define SP_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_GRADIENT, SPGradient)) #define SP_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_GRADIENT, SPGradientClass)) #define SP_IS_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_GRADIENT)) #define SP_IS_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_GRADIENT)) -GType sp_gradient_get_type(); - typedef enum { SP_GRADIENT_TYPE_UNKNOWN, SP_GRADIENT_TYPE_LINEAR, @@ -78,20 +76,24 @@ struct SPGradient : public SPPaintServer { /** State in Inkscape gradient system */ guint state : 2; +private: /** gradientUnits attribute */ SPGradientUnits units; guint units_set : 1; +public: /** gradientTransform attribute */ Geom::Matrix gradientTransform; guint gradientTransform_set : 1; +private: /** spreadMethod attribute */ SPGradientSpread spread; guint spread_set : 1; /** Gradient stops */ guint has_stops : 1; +public: /** Composed vector */ SPGradientVector vector; @@ -101,10 +103,20 @@ struct SPGradient : public SPPaintServer { sigc::connection modified_connection; + bool hasStops() const; SPStop* getFirstStop(); int getStopCount() const; + + bool isUnitsSet() const; + SPGradientUnits getUnits() const; + void setUnits(SPGradientUnits units); + + + bool isSpreadSet() const; + SPGradientSpread getSpread() const; + /** * Returns private vector of given gradient (the gradient at the end of the href chain which has * stops), optionally normalizing it. @@ -113,6 +125,32 @@ struct SPGradient : public SPPaintServer { * \pre There exists a gradient in the chain that has stops. */ SPGradient *getVector(bool force_private = false); + + static GType getType(); + + /** Forces vector to be built, if not present (i.e. changed) */ + void ensureVector(); + + /** Ensures that color array is populated */ + void ensureColors(); + + /** + * Set spread property of gradient and emit modified. + */ + void setSpread(SPGradientSpread spread); + + SPGradientSpread fetchSpread(); + SPGradientUnits fetchUnits(); + + void setSwatch(bool swatch = true); + +private: + bool invalidateVector(); + void rebuildVector(); + + friend class SPGradientImpl; + friend class SPLGPainter; + friend class SPRGPainter; }; /** diff --git a/src/sp-item.cpp b/src/sp-item.cpp index c4411e47d..325009fe5 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -191,16 +191,17 @@ bool SPItem::isVisibleAndUnlocked(unsigned display_key) const { } bool SPItem::isLocked() const { - for (SPObject *o = SP_OBJECT(this); o != NULL; o = SP_OBJECT_PARENT(o)) { - if (SP_IS_ITEM(o) && !(SP_ITEM(o)->sensitive)) + for (SPObject const *o = this; o != NULL; o = o->parent) { + if (SP_IS_ITEM(o) && !(SP_ITEM(o)->sensitive)) { return true; + } } return false; } void SPItem::setLocked(bool locked) { - SP_OBJECT_REPR(this)->setAttribute("sodipodi:insensitive", - ( locked ? "1" : NULL )); + this->repr->setAttribute("sodipodi:insensitive", + ( locked ? "1" : NULL )); updateRepr(); } @@ -250,7 +251,7 @@ void SPItem::resetEvaluated() { requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); } } if ( StatusSet == _evaluated_status ) { - SPObject const *const parent = SP_OBJECT_PARENT(this); + SPObject const *const parent = this->parent; if (SP_IS_SWITCH(parent)) { SP_SWITCH(parent)->resetChildEvaluated(); } @@ -294,7 +295,7 @@ SPItem::setExplicitlyHidden(bool const val) { void SPItem::setCenter(Geom::Point object_centre) { // for getBounds() to work - sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(this)); + sp_document_ensure_up_to_date( this->document ); Geom::OptRect bbox = getBounds(sp_item_i2d_affine(this)); if (bbox) { @@ -319,7 +320,7 @@ bool SPItem::isCenterSet() { Geom::Point SPItem::getCenter() const { // for getBounds() to work - sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(this)); + sp_document_ensure_up_to_date( this->document ); Geom::OptRect bbox = getBounds(sp_item_i2d_affine(this)); if (bbox) { @@ -342,21 +343,21 @@ void SPItem::raiseToTop() { using Inkscape::Algorithms::find_last_if; SPObject *topmost=find_last_if<SPObject::SiblingIterator>( - SP_OBJECT_NEXT(this), NULL, &is_item + this->next, NULL, &is_item ); if (topmost) { - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - sp_repr_parent(repr)->changeOrder(repr, SP_OBJECT_REPR(topmost)); + Inkscape::XML::Node *repr = this->repr; + sp_repr_parent(repr)->changeOrder( repr, topmost->repr ); } } void SPItem::raiseOne() { SPObject *next_higher=std::find_if<SPObject::SiblingIterator>( - SP_OBJECT_NEXT(this), NULL, &is_item + this->next, NULL, &is_item ); if (next_higher) { - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - Inkscape::XML::Node *ref=SP_OBJECT_REPR(next_higher); + Inkscape::XML::Node *repr = this->repr; + Inkscape::XML::Node *ref = next_higher->repr; sp_repr_parent(repr)->changeOrder(repr, ref); } } @@ -367,15 +368,15 @@ void SPItem::lowerOne() { MutableList<SPObject &> next_lower=std::find_if( reverse_list<SPObject::SiblingIterator>( - SP_OBJECT_PARENT(this)->firstChild(), this + this->parent->firstChild(), this ), MutableList<SPObject &>(), &is_item ); if (next_lower) { ++next_lower; - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - Inkscape::XML::Node *ref=( next_lower ? SP_OBJECT_REPR(&*next_lower) : NULL ); + Inkscape::XML::Node *repr = this->repr; + Inkscape::XML::Node *ref = ( next_lower ? next_lower->repr : NULL ); sp_repr_parent(repr)->changeOrder(repr, ref); } } @@ -387,15 +388,15 @@ void SPItem::lowerToBottom() { MutableList<SPObject &> bottom=find_last_if( reverse_list<SPObject::SiblingIterator>( - SP_OBJECT_PARENT(this)->firstChild(), this + this->parent->firstChild(), this ), MutableList<SPObject &>(), &is_item ); if (bottom) { ++bottom; - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - Inkscape::XML::Node *ref=( bottom ? SP_OBJECT_REPR(&*bottom) : NULL ); + Inkscape::XML::Node *repr = this->repr; + Inkscape::XML::Node *ref = ( bottom ? bottom->repr : NULL ); sp_repr_parent(repr)->changeOrder(repr, ref); } } @@ -578,7 +579,7 @@ clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item) nr_arena_item_set_clip(v->arenaitem, ai); nr_arena_item_unref(ai); sp_clippath_set_bbox(SP_CLIPPATH(clip), NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox); - SP_OBJECT(clip)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + clip->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } } @@ -606,7 +607,7 @@ mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) nr_arena_item_set_mask(v->arenaitem, ai); nr_arena_item_unref(ai); sp_mask_set_bbox(SP_MASK(mask), NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox); - SP_OBJECT(mask)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } } @@ -681,7 +682,7 @@ sp_item_write(SPObject *const object, Inkscape::XML::Document *xml_doc, Inkscape Inkscape::XML::Node *crepr; GSList *l; l = NULL; - for (child = sp_object_first_child(object); child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (child = sp_object_first_child(object); child != NULL; child = child->next ) { if (!SP_IS_TITLE(child) && !SP_IS_DESC(child)) continue; crepr = child->updateRepr(xml_doc, NULL, flags); if (crepr) l = g_slist_prepend (l, crepr); @@ -692,7 +693,7 @@ sp_item_write(SPObject *const object, Inkscape::XML::Document *xml_doc, Inkscape l = g_slist_remove (l, l->data); } } else { - for (child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (child = sp_object_first_child(object) ; child != NULL; child = child->next ) { if (!SP_IS_TITLE(child) && !SP_IS_DESC(child)) continue; child->updateRepr(flags); } @@ -788,8 +789,8 @@ sp_item_invoke_bbox_full(SPItem const *item, Geom::OptRect &bbox, Geom::Matrix c // unless this is geometric bbox, extend by filter area and crop the bbox by clip path, if any if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) { - if (SP_OBJECT_STYLE(item) && SP_OBJECT_STYLE(item)->filter.href) { - SPObject *filter = SP_OBJECT_STYLE(item)->getFilter(); + if ( item->style && item->style->filter.href ) { + SPObject *filter = item->style->getFilter(); if (filter && SP_IS_FILTER(filter)) { // default filer area per the SVG spec: double x = -0.1; @@ -912,14 +913,14 @@ unsigned sp_item_pos_in_parent(SPItem *item) g_assert(item != NULL); g_assert(SP_IS_ITEM(item)); - SPObject *parent = SP_OBJECT_PARENT(item); + SPObject *parent = item->parent; g_assert(parent != NULL); g_assert(SP_IS_OBJECT(parent)); - SPObject *object = SP_OBJECT(item); + SPObject *object = item; - unsigned pos=0; - for ( SPObject *iter = sp_object_first_child(parent) ; iter ; iter = SP_OBJECT_NEXT(iter)) { + unsigned pos = 0; + for ( SPObject *iter = sp_object_first_child(parent) ; iter ; iter = iter->next) { if ( iter == object ) { return pos; } @@ -989,8 +990,8 @@ void sp_item_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidateP // Get the snappoints of clipping paths and mask, if any std::list<SPObject const *> clips_and_masks; - clips_and_masks.push_back(SP_OBJECT(item->clip_ref->getObject())); - clips_and_masks.push_back(SP_OBJECT(item->mask_ref->getObject())); + clips_and_masks.push_back(item->clip_ref->getObject()); + clips_and_masks.push_back(item->mask_ref->getObject()); SPDesktop *desktop = inkscape_active_desktop(); for (std::list<SPObject const *>::const_iterator o = clips_and_masks.begin(); o != clips_and_masks.end(); o++) { @@ -1020,9 +1021,9 @@ sp_item_invoke_print(SPItem *item, SPPrintContext *ctx) if (!item->isHidden()) { if (((SPItemClass *) G_OBJECT_GET_CLASS(item))->print) { if (!item->transform.isIdentity() - || SP_OBJECT_STYLE(item)->opacity.value != SP_SCALE24_MAX) + || item->style->opacity.value != SP_SCALE24_MAX) { - sp_print_bind(ctx, item->transform, SP_SCALE24_TO_FLOAT(SP_OBJECT_STYLE(item)->opacity.value)); + sp_print_bind(ctx, item->transform, SP_SCALE24_TO_FLOAT(item->style->opacity.value)); ((SPItemClass *) G_OBJECT_GET_CLASS(item))->print(item, ctx); sp_print_release(ctx); } else { @@ -1061,8 +1062,8 @@ sp_item_description(SPItem *item) g_free (s); s = snew; } - if (SP_OBJECT_STYLE(item) && SP_OBJECT_STYLE(item)->filter.href && SP_OBJECT_STYLE(item)->filter.href->getObject()) { - const gchar *label = SP_OBJECT_STYLE(item)->filter.href->getObject()->label(); + if ( item->style && item->style->filter.href && item->style->filter.href->getObject() ) { + const gchar *label = item->style->filter.href->getObject()->label(); gchar *snew; if (label) { snew = g_strdup_printf (_("%s; <i>filtered (%s)</i>"), s, _(label)); @@ -1111,7 +1112,7 @@ sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags) if (ai != NULL) { item->display = sp_item_view_new_prepend(item->display, item, flags, key, ai); nr_arena_item_set_transform(ai, item->transform); - nr_arena_item_set_opacity(ai, SP_SCALE24_TO_FLOAT(SP_OBJECT_STYLE(item)->opacity.value)); + nr_arena_item_set_opacity(ai, SP_SCALE24_TO_FLOAT(item->style->opacity.value)); nr_arena_item_set_visible(ai, !item->isHidden()); nr_arena_item_set_sensitive(ai, item->sensitive); if (item->clip_ref->getObject()) { @@ -1131,7 +1132,7 @@ sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags) NRRect bbox; sp_item_invoke_bbox(item, &bbox, Geom::identity(), TRUE); sp_clippath_set_bbox(SP_CLIPPATH(cp), clip_key, &bbox); - SP_OBJECT(cp)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + cp->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } if (item->mask_ref->getObject()) { SPMask *mask = item->mask_ref->getObject(); @@ -1150,7 +1151,7 @@ sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags) NRRect bbox; sp_item_invoke_bbox(item, &bbox, Geom::identity(), TRUE); sp_mask_set_bbox(SP_MASK(mask), mask_key, &bbox); - SP_OBJECT(mask)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } NR_ARENA_ITEM_SET_DATA(ai, item); Geom::OptRect item_bbox; @@ -1204,34 +1205,32 @@ sp_item_invoke_hide(SPItem *item, unsigned key) void sp_item_adjust_pattern (SPItem *item, Geom::Matrix const &postmul, bool set) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; - if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_PATTERN (server)) { - SPPattern *pattern = sp_pattern_clone_if_necessary (item, SP_PATTERN (server), "fill"); - sp_pattern_transform_multiply (pattern, postmul, set); + if ( style && style->fill.isPaintserver() ) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_PATTERN(server) ) { + SPPattern *pattern = sp_pattern_clone_if_necessary(item, SP_PATTERN(server), "fill"); + sp_pattern_transform_multiply(pattern, postmul, set); } } - if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_PATTERN (server)) { - SPPattern *pattern = sp_pattern_clone_if_necessary (item, SP_PATTERN (server), "stroke"); - sp_pattern_transform_multiply (pattern, postmul, set); + if ( style && style->stroke.isPaintserver() ) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_PATTERN(server) ) { + SPPattern *pattern = sp_pattern_clone_if_necessary(item, SP_PATTERN(server), "stroke"); + sp_pattern_transform_multiply(pattern, postmul, set); } } - } -void -sp_item_adjust_gradient (SPItem *item, Geom::Matrix const &postmul, bool set) +void sp_item_adjust_gradient( SPItem *item, Geom::Matrix const &postmul, bool set ) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; - if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_GRADIENT (server)) { + if ( style && style->fill.isPaintserver() ) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { /** * \note Bbox units for a gradient are generally a bad idea because @@ -1242,40 +1241,37 @@ sp_item_adjust_gradient (SPItem *item, Geom::Matrix const &postmul, bool set) * \todo FIXME: convert back to bbox units after transforming with * the item, so as to preserve the original units. */ - SPGradient *gradient = sp_gradient_convert_to_userspace (SP_GRADIENT (server), item, "fill"); + SPGradient *gradient = sp_gradient_convert_to_userspace( SP_GRADIENT(server), item, "fill" ); - sp_gradient_transform_multiply (gradient, postmul, set); + sp_gradient_transform_multiply( gradient, postmul, set ); } } - if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_GRADIENT (server)) { - SPGradient *gradient = sp_gradient_convert_to_userspace (SP_GRADIENT (server), item, "stroke"); - sp_gradient_transform_multiply (gradient, postmul, set); + if ( style && style->stroke.isPaintserver() ) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { + SPGradient *gradient = sp_gradient_convert_to_userspace( SP_GRADIENT(server), item, "stroke" ); + sp_gradient_transform_multiply( gradient, postmul, set ); } } } -void -sp_item_adjust_stroke (SPItem *item, gdouble ex) +void sp_item_adjust_stroke( SPItem *item, gdouble ex ) { - SPStyle *style = SP_OBJECT_STYLE (item); - - if (style && !style->stroke.isNone() && !NR_DF_TEST_CLOSE (ex, 1.0, NR_EPSILON)) { + SPStyle *style = item->style; + if ( style && !style->stroke.isNone() && !NR_DF_TEST_CLOSE(ex, 1.0, NR_EPSILON) ) { style->stroke_width.computed *= ex; style->stroke_width.set = TRUE; - if (style->stroke_dash.n_dash != 0) { - int i; - for (i = 0; i < style->stroke_dash.n_dash; i++) { + if ( style->stroke_dash.n_dash != 0 ) { + for (int i = 0; i < style->stroke_dash.n_dash; i++) { style->stroke_dash.dash[i] *= ex; } style->stroke_dash.offset *= ex; } - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); } } @@ -1286,7 +1282,7 @@ Geom::Matrix sp_item_transform_repr (SPItem *item) { Geom::Matrix t_old(Geom::identity()); - gchar const *t_attr = SP_OBJECT_REPR(item)->attribute("transform"); + gchar const *t_attr = item->repr->attribute("transform"); if (t_attr) { Geom::Matrix t; if (sp_svg_transform_read(t_attr, &t)) { @@ -1310,7 +1306,7 @@ sp_item_adjust_stroke_width_recursive(SPItem *item, double expansion) if (item && SP_IS_USE(item)) return; - for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { + for (SPObject *o = item->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) sp_item_adjust_stroke_width_recursive(SP_ITEM(o), expansion); } @@ -1326,7 +1322,7 @@ sp_item_adjust_rects_recursive(SPItem *item, Geom::Matrix advertized_transform) sp_rect_compensate_rxry (SP_RECT(item), advertized_transform); } - for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { + for (SPObject *o = item->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) sp_item_adjust_rects_recursive(SP_ITEM(o), advertized_transform); } @@ -1348,7 +1344,7 @@ sp_item_adjust_paint_recursive (SPItem *item, Geom::Matrix advertized_transform, // also we do not recurse into clones, because a clone's child is the ghost of its original - // we must not touch it if (!(item && (SP_IS_TEXT(item) || SP_IS_USE(item)))) { - for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { + for (SPObject *o = item->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) { // At the level of the transformed item, t_ancestors is identity; // below it, it is the accmmulated chain of transforms from this level to the top level @@ -1377,22 +1373,16 @@ sp_item_adjust_livepatheffect (SPItem *item, Geom::Matrix const &postmul, bool s SPLPEItem *lpeitem = SP_LPE_ITEM (item); if ( sp_lpe_item_has_path_effect(lpeitem) ) { + sp_lpe_item_fork_path_effects_if_necessary(lpeitem); + + // now that all LPEs are forked_if_necessary, we can apply the transform PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem); for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++) { - // If the path effect is used by 2 or more items, fork it - // so that each object has its own independent copy of the effect LivePathEffectObject *lpeobj = (*it)->lpeobject; - if (lpeobj) { - LivePathEffectObject *new_lpeobj = lpeobj->fork_private_if_necessary(); - if (new_lpeobj != lpeobj) { - sp_lpe_item_replace_path_effect(lpeitem, lpeobj, new_lpeobj); - } - - if (lpeobj->get_lpe()) { - Inkscape::LivePathEffect::Effect * effect = lpeobj->get_lpe(); - effect->transform_multiply(postmul, set); - } + if (lpeobj && lpeobj->get_lpe()) { + Inkscape::LivePathEffect::Effect * effect = lpeobj->get_lpe(); + effect->transform_multiply(postmul, set); } } } @@ -1458,7 +1448,7 @@ sp_item_write_transform(SPItem *item, Inkscape::XML::Node *repr, Geom::Matrix co !preserve && // user did not chose to preserve all transforms !item->clip_ref->getObject() && // the object does not have a clippath !item->mask_ref->getObject() && // the object does not have a mask - !(!transform.isTranslation() && SP_OBJECT_STYLE(item) && SP_OBJECT_STYLE(item)->getFilter()) + !(!transform.isTranslation() && item->style && item->style->getFilter()) // the object does not have a filter, or the transform is translation (which is supposed to not affect filters) ) { transform_attr = ((SPItemClass *) G_OBJECT_GET_CLASS(item))->set_transform(item, transform); @@ -1469,7 +1459,7 @@ sp_item_write_transform(SPItem *item, Inkscape::XML::Node *repr, Geom::Matrix co // it causes clone SPUse's copy of the original object to brought up to // date with the original. Otherwise, sp_use_bbox returns incorrect // values if called in code handling the transformed signal. - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); // send the relative transform with a _transformed_signal item->_transformed_signal.emit(&advertized_transform, item); @@ -1540,7 +1530,7 @@ i2anc_affine(SPObject const *object, SPObject const *const ancestor) { } else { ret *= SP_ITEM(object)->transform; } - object = SP_OBJECT_PARENT(object); + object = object->parent; } return ret; } @@ -1575,7 +1565,7 @@ Geom::Matrix sp_item_i2d_affine(SPItem const *item) Geom::Matrix const ret( sp_item_i2doc_affine(item) * Geom::Scale(1, -1) - * Geom::Translate(0, sp_document_height(SP_OBJECT_DOCUMENT(item))) ); + * Geom::Translate(0, sp_document_height(item->document)) ); return ret; } @@ -1585,10 +1575,10 @@ void sp_item_set_i2d_affine(SPItem *item, Geom::Matrix const &i2dt) g_return_if_fail( SP_IS_ITEM(item) ); Geom::Matrix dt2p; /* desktop to item parent transform */ - if (SP_OBJECT_PARENT(item)) { - dt2p = sp_item_i2d_affine((SPItem *) SP_OBJECT_PARENT(item)).inverse(); + if (item->parent) { + dt2p = sp_item_i2d_affine(static_cast<SPItem *>(item->parent)).inverse(); } else { - dt2p = ( Geom::Translate(0, -sp_document_height(SP_OBJECT_DOCUMENT(item))) + dt2p = ( Geom::Translate(0, -sp_document_height(item->document)) * Geom::Scale(1, -1) ); } @@ -1666,16 +1656,17 @@ sp_item_get_arenaitem(SPItem *item, unsigned key) int sp_item_repr_compare_position(SPItem *first, SPItem *second) { - return sp_repr_compare_position(SP_OBJECT_REPR(first), - SP_OBJECT_REPR(second)); + return sp_repr_compare_position(first->repr, + second->repr); } SPItem * sp_item_first_item_child (SPObject *obj) { - for ( SPObject *iter = sp_object_first_child(obj) ; iter ; iter = SP_OBJECT_NEXT(iter)) { - if (SP_IS_ITEM (iter)) + for ( SPObject *iter = sp_object_first_child(obj) ; iter ; iter = iter->next) { + if ( SP_IS_ITEM(iter) ) { return SP_ITEM (iter); + } } return NULL; } diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 1bb500dd2..71352ed98 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -772,14 +772,21 @@ bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathE return false; } -void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj, - LivePathEffectObject * new_lpeobj) +/** + * Writes a new "inkscape:path-effect" string to xml, where the old_lpeobjects are substituted by the new ones. + * Note that this method messes up the item's \c PathEffectList. + */ +void SPLPEItem::replacePathEffects( std::vector<LivePathEffectObject const *> const old_lpeobjs, + std::vector<LivePathEffectObject const *> const new_lpeobjs ) { HRefList hreflist; - for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) + for (PathEffectList::const_iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it) { - if ((*it)->lpeobject == old_lpeobj) { - const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id"); + LivePathEffectObject const * current_lpeobj = (*it)->lpeobject; + std::vector<LivePathEffectObject const *>::const_iterator found_it(std::find(old_lpeobjs.begin(), old_lpeobjs.end(), current_lpeobj)); + if ( found_it != old_lpeobjs.end() ) { + std::vector<LivePathEffectObject const *>::difference_type found_index = std::distance (old_lpeobjs.begin(), found_it); + const gchar * repr_id = SP_OBJECT_REPR(new_lpeobjs[found_index])->attribute("id"); gchar *hrefstr = g_strdup_printf("#%s", repr_id); hreflist.push_back( std::string(hrefstr) ); g_free(hrefstr); @@ -789,7 +796,49 @@ void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * } } std::string r = hreflist_write_svg(hreflist); - SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str()); + SP_OBJECT_REPR(this)->setAttribute("inkscape:path-effect", r.c_str()); +} + +/** + * Check all effects in the stack if they are used by other items, and fork them if so. + * It is not recommended to fork the effects by yourself calling LivePathEffectObject::fork_private_if_necessary, + * use this method instead. + * Returns true if one or more effects were forked; returns false if nothing was done. + */ +bool sp_lpe_item_fork_path_effects_if_necessary(SPLPEItem *lpeitem, unsigned int nr_of_allowed_users) +{ + bool forked = false; + + if ( sp_lpe_item_has_path_effect(lpeitem) ) { + // If one of the path effects is used by 2 or more items, fork it + // so that each object has its own independent copy of the effect. + // Note: replacing path effects messes up the path effect list + + // Clones of the LPEItem will increase the refcount of the lpeobjects. + // Therefore, nr_of_allowed_users should be increased with the number of clones (i.e. refs to the lpeitem) + nr_of_allowed_users += SP_OBJECT(lpeitem)->hrefcount; + + std::vector<LivePathEffectObject const *> old_lpeobjs, new_lpeobjs; + PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem); + for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + if (lpeobj) { + LivePathEffectObject *forked_lpeobj = lpeobj->fork_private_if_necessary(nr_of_allowed_users); + if (forked_lpeobj != lpeobj) { + forked = true; + old_lpeobjs.push_back(lpeobj); + new_lpeobjs.push_back(forked_lpeobj); + } + } + } + + if (forked) { + lpeitem->replacePathEffects(old_lpeobjs, new_lpeobjs); + } + } + + return forked; } // Enable or disable the path effects of the item. diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h index 4823390de..49edbf90a 100644 --- a/src/sp-lpe-item.h +++ b/src/sp-lpe-item.h @@ -35,7 +35,8 @@ namespace LivePathEffect{ typedef std::list<Inkscape::LivePathEffect::LPEObjectReference *> PathEffectList; -struct SPLPEItem : public SPItem { +class SPLPEItem : public SPItem { +public: int path_effects_enabled; PathEffectList* path_effect_list; @@ -43,6 +44,9 @@ struct SPLPEItem : public SPItem { Inkscape::LivePathEffect::LPEObjectReference* current_path_effect; std::vector<Inkscape::Display::TemporaryItem*> lpe_helperpaths; + + void replacePathEffects( std::vector<LivePathEffectObject const *> const old_lpeobjs, + std::vector<LivePathEffectObject const *> const new_lpeobjs ); }; struct SPLPEItemClass { @@ -57,8 +61,7 @@ void sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool wri bool sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve); void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset); void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj); -void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj, - LivePathEffectObject * new_lpeobj); +bool sp_lpe_item_fork_path_effects_if_necessary(SPLPEItem *lpeitem, unsigned int nr_of_allowed_users = 1); void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths); void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths); void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem); diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 420c7b4a6..fd17b3c12 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -38,6 +38,7 @@ #include "helper/sp-marshal.h" #include "xml/node-event-vector.h" #include "attributes.h" +#include "color-profile-fns.h" #include "document.h" #include "style.h" #include "sp-object-repr.h" @@ -539,29 +540,33 @@ SPObject::setLabel(gchar const *label) { /** Queues the object for orphan collection */ -void -SPObject::requestOrphanCollection() { +void SPObject::requestOrphanCollection() { g_return_if_fail(document != NULL); // do not remove style or script elements (Bug #276244) - if (SP_IS_STYLE_ELEM(this)) - return; - if (SP_IS_SCRIPT(this)) - return; - - document->queueForOrphanCollection(this); + if (SP_IS_STYLE_ELEM(this)) { + // leave it + } else if (SP_IS_SCRIPT(this)) { + // leave it + } else if (SP_IS_PAINT_SERVER(this) && static_cast<SPPaintServer*>(this)->isSwatch() ) { + // leave it + } else if (IS_COLORPROFILE(this)) { + // leave it + } else { + document->queueForOrphanCollection(this); - /** \todo - * This is a temporary hack added to make fill&stroke rebuild its - * gradient list when the defs are vacuumed. gradient-vector.cpp - * listens to the modified signal on defs, and now we give it that - * signal. Mental says that this should be made automatic by - * merging SPObjectGroup with SPObject; SPObjectGroup would issue - * this signal automatically. Or maybe just derive SPDefs from - * SPObjectGroup? - */ + /** \todo + * This is a temporary hack added to make fill&stroke rebuild its + * gradient list when the defs are vacuumed. gradient-vector.cpp + * listens to the modified signal on defs, and now we give it that + * signal. Mental says that this should be made automatic by + * merging SPObjectGroup with SPObject; SPObjectGroup would issue + * this signal automatically. Or maybe just derive SPDefs from + * SPObjectGroup? + */ - this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG); + this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG); + } } /** Sends the delete signal to all children of this object recursively */ diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index 258323a93..4b5366a46 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -21,7 +21,6 @@ #include "xml/node.h" static void sp_paint_server_class_init(SPPaintServerClass *psc); -static void sp_paint_server_init(SPPaintServer *ps); static void sp_paint_server_release(SPObject *object); @@ -30,7 +29,7 @@ static void sp_painter_stale_fill(SPPainter *painter, NRPixBlock *pb); static SPObjectClass *parent_class; static GSList *stale_painters = NULL; -GType sp_paint_server_get_type (void) +GType SPPaintServer::getType(void) { static GType type = 0; if (!type) { @@ -43,7 +42,7 @@ GType sp_paint_server_get_type (void) NULL, /* class_data */ sizeof(SPPaintServer), 16, /* n_preallocs */ - (GInstanceInitFunc) sp_paint_server_init, + (GInstanceInitFunc) SPPaintServer::init, NULL, /* value_table */ }; type = g_type_register_static(SP_TYPE_OBJECT, "SPPaintServer", &info, (GTypeFlags) 0); @@ -58,9 +57,10 @@ static void sp_paint_server_class_init(SPPaintServerClass *psc) parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT); } -static void sp_paint_server_init(SPPaintServer *ps) +void SPPaintServer::init(SPPaintServer *ps) { ps->painters = NULL; + ps->swatch = false; } static void sp_paint_server_release(SPObject *object) @@ -157,29 +157,16 @@ static void sp_painter_stale_fill(SPPainter */*painter*/, NRPixBlock *pb) bool SPPaintServer::isSwatch() const { - bool swatch = false; - if (SP_IS_GRADIENT(this)) { - SPGradient *grad = SP_GRADIENT(this); - if ( SP_GRADIENT_HAS_STOPS(grad) ) { - gchar const * attr = repr->attribute("osb:paint"); - if (attr && !strcmp(attr, "solid")) { - swatch = true; - } - } - } return swatch; } bool SPPaintServer::isSolid() const { bool solid = false; - if (SP_IS_GRADIENT(this)) { + if (swatch && SP_IS_GRADIENT(this)) { SPGradient *grad = SP_GRADIENT(this); - if ( SP_GRADIENT_HAS_STOPS(grad) && (grad->getStopCount() == 0) ) { - gchar const * attr = repr->attribute("osb:paint"); - if (attr && !strcmp(attr, "solid")) { - solid = true; - } + if ( grad->hasStops() && (grad->getStopCount() == 0) ) { + solid = true; } } return solid; diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index a76daf4d1..dc7bcc9c5 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -21,7 +21,7 @@ class SPPainter; -#define SP_TYPE_PAINT_SERVER (sp_paint_server_get_type ()) +#define SP_TYPE_PAINT_SERVER (SPPaintServer::getType()) #define SP_PAINT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_PAINT_SERVER, SPPaintServer)) #define SP_PAINT_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_PAINT_SERVER, SPPaintServerClass)) #define SP_IS_PAINT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_PAINT_SERVER)) @@ -47,8 +47,17 @@ struct SPPaintServer : public SPObject { /** List of paints */ SPPainter *painters; +protected: + bool swatch; +public: + + static GType getType(void); + bool isSwatch() const; bool isSolid() const; + +private: + static void init(SPPaintServer *ps); }; struct SPPaintServerClass { @@ -59,8 +68,6 @@ struct SPPaintServerClass { void (* painter_free) (SPPaintServer *ps, SPPainter *painter); }; -GType sp_paint_server_get_type (void); - SPPainter *sp_paint_server_painter_new (SPPaintServer *ps, Geom::Matrix const &full_transform, Geom::Matrix const &parent_transform, const NRRect *bbox); SPPainter *sp_painter_free (SPPainter *painter); diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index dadcb5f57..3064341b6 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -1227,7 +1227,24 @@ static void sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCa bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM); if (c1 || c2) { - p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); + Inkscape::SnapSourceType sst; + Inkscape::SnapTargetType stt; + switch (nodetype) { + case Geom::NODE_CUSP: + sst = Inkscape::SNAPSOURCE_NODE_CUSP; + stt = Inkscape::SNAPTARGET_NODE_CUSP; + break; + case Geom::NODE_SMOOTH: + case Geom::NODE_SYMM: + sst = Inkscape::SNAPSOURCE_NODE_SMOOTH; + stt = Inkscape::SNAPTARGET_NODE_SMOOTH; + break; + default: + sst = Inkscape::SNAPSOURCE_UNDEFINED; + stt = Inkscape::SNAPTARGET_UNDEFINED; + break; + } + p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt)); } // Consider midpoints of line segments for snapping diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 11665b890..bae625f58 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -528,6 +528,8 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio } else if (SP_IS_TSPAN(root)) { SPTSpan *tspan = SP_TSPAN(root); + // x, y attributes are stripped from some tspans as we do our own line layout + // This should be checked carefully, as it can undo line layout in imported SVG files. bool use_xy = !in_textpath && (tspan->role == SP_TSPAN_ROLE_UNSPECIFIED || !tspan->attributes.singleXYCoordinates()); tspan->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, true); } @@ -926,6 +928,50 @@ void TextTagAttributes::transform(Geom::Matrix const &matrix, double scale_x, do *it = it->computed * scale_y; } +double TextTagAttributes::getDx(unsigned index) +{ + if( attributes.dx.size() == 0 ) { + return 0.0; + } + if( index < attributes.dx.size() ) { + return attributes.dx[index].computed; + } else { + return 0.0; // attributes.dx.back().computed; + } +} + + +double TextTagAttributes::getDy(unsigned index) +{ + if( attributes.dy.size() == 0 ) { + return 0.0; + } + if( index < attributes.dy.size() ) { + return attributes.dy[index].computed; + } else { + return 0.0; // attributes.dy.back().computed; + } +} + + +void TextTagAttributes::addToDx(unsigned index, double delta) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.dx.size() < index + 1) attributes.dx.resize(index + 1, zero_length); + attributes.dx[index] = attributes.dx[index].computed + delta; +} + +void TextTagAttributes::addToDy(unsigned index, double delta) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.dy.size() < index + 1) attributes.dy.resize(index + 1, zero_length); + attributes.dy[index] = attributes.dy[index].computed + delta; +} + void TextTagAttributes::addToDxDy(unsigned index, Geom::Point const &adjust) { SVGLength zero_length; @@ -941,6 +987,19 @@ void TextTagAttributes::addToDxDy(unsigned index, Geom::Point const &adjust) } } +double TextTagAttributes::getRotate(unsigned index) +{ + if( attributes.rotate.size() == 0 ) { + return 0.0; + } + if( index < attributes.rotate.size() ) { + return attributes.rotate[index].computed; + } else { + return attributes.rotate.back().computed; + } +} + + void TextTagAttributes::addToRotate(unsigned index, double delta) { SVGLength zero_length; @@ -956,6 +1015,21 @@ void TextTagAttributes::addToRotate(unsigned index, double delta) } +void TextTagAttributes::setRotate(unsigned index, double angle) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.rotate.size() < index + 2) { + if (attributes.rotate.empty()) + attributes.rotate.resize(index + 2, zero_length); + else + attributes.rotate.resize(index + 2, attributes.rotate.back()); + } + attributes.rotate[index] = mod360(angle); +} + + /* Local Variables: mode:c++ diff --git a/src/style.cpp b/src/style.cpp index a05cef252..ffc1fb0ae 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -28,7 +28,6 @@ #include "svg/svg.h" #include "svg/svg-color.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #include "display/canvas-bpath.h" #include "attributes.h" @@ -88,11 +87,13 @@ static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *s static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str); +static void sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str); static void sp_style_read_ifilter(gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr, gchar const *key, SPStyleEnum const *dict, bool can_explicitly_inherit); static void sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key); static void sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const *key); +static void sp_style_read_pbaselineshift(SPIBaselineShift *val, Inkscape::XML::Node *repr, gchar const *key); static void sp_style_read_pfloat(SPIFloat *val, Inkscape::XML::Node *repr, gchar const *key); static gint sp_style_write_ifloat(gchar *p, gint len, gchar const *key, SPIFloat const *val, SPIFloat const *base, guint flags); @@ -102,6 +103,7 @@ static gint sp_style_write_istring(gchar *p, gint len, gchar const *key, SPIStri static gint sp_style_write_ilength(gchar *p, gint len, gchar const *key, SPILength const *val, SPILength const *base, guint flags); static gint sp_style_write_ipaint(gchar *b, gint len, gchar const *key, SPIPaint const *paint, SPIPaint const *base, guint flags); static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFontSize const *val, SPIFontSize const *base, guint flags); +static gint sp_style_write_ibaselineshift(gchar *p, gint len, gchar const *key, SPIBaselineShift const *val, SPIBaselineShift const *base, guint flags); static gint sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, SPILengthOrNormal const *const val, SPILengthOrNormal const *const base, guint const flags); static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, SPITextDecoration const *const val, SPITextDecoration const *const base, guint const flags); static gint sp_style_write_ifilter(gchar *b, gint len, gchar const *key, SPIFilter const *filter, SPIFilter const *base, guint flags); @@ -119,6 +121,9 @@ static void sp_style_filter_clear(SPStyle *style); #define SPS_READ_IFONTSIZE_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ifontsize((v), (s));} #define SPS_READ_PFONTSIZE_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pfontsize((v), (r), (k));} +#define SPS_READ_IBASELINE_SHIFT_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ibaselineshift((v), (s));} +#define SPS_READ_PBASELINE_SHIFT_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pbaselineshift((v), (r), (k));} + static void sp_style_merge_from_object_stylesheet(SPStyle *, SPObject const *); struct SPStyleEnum { @@ -259,6 +264,13 @@ static SPStyleEnum const enum_writing_mode[] = { {NULL, -1} }; +static SPStyleEnum const enum_baseline_shift[] = { + {"baseline", SP_CSS_BASELINE_SHIFT_BASELINE}, + {"sub", SP_CSS_BASELINE_SHIFT_SUB}, + {"super", SP_CSS_BASELINE_SHIFT_SUPER}, + {NULL, -1} +}; + static SPStyleEnum const enum_visibility[] = { {"hidden", SP_CSS_VISIBILITY_HIDDEN}, {"collapse", SP_CSS_VISIBILITY_COLLAPSE}, @@ -628,6 +640,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) enum_writing_mode, true); SPS_READ_PENUM_IF_UNSET(&style->text_anchor, repr, "text-anchor", enum_text_anchor, true); + SPS_READ_PBASELINE_SHIFT_IF_UNSET(&style->baseline_shift, repr, "baseline-shift"); /* opacity */ if (!style->opacity.set) { @@ -945,6 +958,9 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) case SP_PROP_TEXT_ANCHOR: SPS_READ_IENUM_IF_UNSET(&style->text_anchor, val, enum_text_anchor, true); break; + case SP_PROP_BASELINE_SHIFT: + SPS_READ_IBASELINE_SHIFT_IF_UNSET(&style->baseline_shift, val); + break; /* Text (unimplemented) */ case SP_PROP_TEXT_RENDERING: { /* Ignore the hint. */ @@ -955,9 +971,6 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) case SP_PROP_ALIGNMENT_BASELINE: g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val); break; - case SP_PROP_BASELINE_SHIFT: - g_warning("Unimplemented style property SP_PROP_BASELINE_SHIFT: value: %s", val); - break; case SP_PROP_DOMINANT_BASELINE: g_warning("Unimplemented style property SP_PROP_DOMINANT_BASELINE: value: %s", val); break; @@ -1342,6 +1355,50 @@ sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &pare } } +// Some shifts are defined relative to parent. +static void +sp_style_merge_baseline_shift_from_parent(SPIBaselineShift &child, SPIBaselineShift const &parent, + SPIFontSize const &pfont_size) +{ + /* 'baseline-shift' */ + if (!child.set || child.inherit) { + /* Inherit the computed value. Reference: http://www.w3.org/TR/SVG11/styling.html#Inheritance */ + child.computed = parent.computed; // Does this make sense (applying a shift a second time)? + } else if (child.type == SP_BASELINE_SHIFT_LITERAL) { + if( child.literal == SP_CSS_BASELINE_SHIFT_BASELINE ) { + child.computed = 0; // No change + } else if (child.literal == SP_CSS_BASELINE_SHIFT_SUB ) { + // Should use subscript position from font relative to alphabetic baseline + // OpenOffice, Adobe: -0.33, Word -0.14, LaTex about -0.2. + child.computed = -0.2 * pfont_size.computed; + } else if (child.literal == SP_CSS_BASELINE_SHIFT_SUPER ) { + // Should use superscript position from font relative to alphabetic baseline + // OpenOffice, Adobe: 0.33, Word 0.35, LaTex about 0.45. + child.computed = 0.4 * pfont_size.computed; + } else { + /* Illegal value */ + } + } else if (child.type == SP_BASELINE_SHIFT_PERCENTAGE) { + // Percentage for baseline shift is relative to computed "line-height" + // which is just font-size (see SVG1.1 'font'). + child.computed = pfont_size.computed * child.value; + } else if (child.type == SP_BASELINE_SHIFT_LENGTH) { + switch (child.unit) { + case SP_CSS_UNIT_EM: + child.computed = child.value * pfont_size.computed; + break; + case SP_CSS_UNIT_EX: + child.computed = child.value * 0.5 * pfont_size.computed; + break; + default: + /* No change */ + break; + } + } + // baseline-shifts are relative to parent baseline + child.computed += parent.computed; +} + /** * Sets computed values in \a style, which may involve inheriting from (or in some other way * calculating from) corresponding computed values of \a parent. @@ -1483,6 +1540,10 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent) style->text_anchor.computed = parent->text_anchor.computed; } + /* Baseline Shift... Some shifts are relative to parent. */ + sp_style_merge_baseline_shift_from_parent(style->baseline_shift, parent->baseline_shift, + parent->font_size); + if (style->opacity.inherit) { style->opacity.value = parent->opacity.value; } @@ -2269,6 +2330,8 @@ sp_style_write_string(SPStyle const *const style, guint const flags) p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &style->writing_mode, NULL, flags); p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &style->text_anchor, NULL, flags); + p += sp_style_write_ibaselineshift(p, c + BMAX - p, "baseline-shift", &style->baseline_shift, NULL, flags); + /// \todo fixme: Per type methods need default flag too (lauris) @@ -2433,6 +2496,7 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to) p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &from->writing_mode, &to->writing_mode, SP_STYLE_FLAG_IFDIFF); p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &from->text_anchor, &to->text_anchor, SP_STYLE_FLAG_IFDIFF); + p += sp_style_write_ibaselineshift(p, c + BMAX - p, "baseline-shift", &from->baseline_shift, &to->baseline_shift, SP_STYLE_FLAG_IFDIFF); /// \todo fixme: Per type methods need default flag too if (from->opacity.set && from->opacity.value != SP_SCALE24_MAX) { @@ -2650,6 +2714,13 @@ sp_style_clear(SPStyle *style) style->word_spacing.normal = TRUE; style->word_spacing.value = style->word_spacing.computed = 0.0; + style->baseline_shift.set = FALSE; + style->baseline_shift.type = SP_BASELINE_SHIFT_LITERAL; + style->baseline_shift.unit = SP_CSS_UNIT_NONE; + style->baseline_shift.literal = SP_CSS_BASELINE_SHIFT_BASELINE; + style->baseline_shift.value = 0.0; + style->baseline_shift.computed = 0.0; + style->text_transform.set = FALSE; style->text_transform.value = style->text_transform.computed = SP_CSS_TEXT_TRANSFORM_NONE; @@ -2662,7 +2733,6 @@ sp_style_clear(SPStyle *style) style->writing_mode.set = FALSE; style->writing_mode.value = style->writing_mode.computed = SP_CSS_WRITING_MODE_LR_TB; - style->text_anchor.set = FALSE; style->text_anchor.value = style->text_anchor.computed = SP_CSS_TEXT_ANCHOR_START; @@ -3188,17 +3258,6 @@ sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume } paint->value.color.icc = tmp; } - if (strneq(str, "device-gray(", 12) || - strneq(str, "device-rgb(", 11) || - strneq(str, "device-cmyk(", 12) || - strneq(str, "device-nchannel(", 16)) { - SVGDeviceColor* tmp = new SVGDeviceColor(); - if ( ! sp_svg_read_device_color( str, &str, tmp ) ) { - delete tmp; - tmp = 0; - } - paint->value.color.device = tmp; - } } } } @@ -3216,6 +3275,7 @@ sp_style_read_ifontsize(SPIFontSize *val, gchar const *str) val->set = TRUE; val->inherit = TRUE; } else if ((*str == 'x') || (*str == 's') || (*str == 'm') || (*str == 'l')) { + // xx-small, x-small, etc. for (unsigned i = 0; enum_font_size[i].key; i++) { if (!strcmp(str, enum_font_size[i].key)) { val->set = TRUE; @@ -3271,6 +3331,45 @@ sp_style_read_ifontsize(SPIFontSize *val, gchar const *str) } +/** + * Set SPIBaselineShift object from string. + */ +static void +sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str) +{ + if (!strcmp(str, "inherit")) { + val->set = TRUE; + val->inherit = TRUE; + } else if ((*str == 'b') || (*str == 's')) { + // baseline or sub or super + for (unsigned i = 0; enum_baseline_shift[i].key; i++) { + if (!strcmp(str, enum_baseline_shift[i].key)) { + val->set = TRUE; + val->inherit = FALSE; + val->type = SP_BASELINE_SHIFT_LITERAL; + val->literal = enum_baseline_shift[i].value; + return; + } + } + /* Invalid */ + return; + } else { + SPILength length; + sp_style_read_ilength(&length, str); + val->set = length.set; + val->inherit = length.inherit; + val->unit = length.unit; + val->value = length.value; + val->computed = length.computed; + if( val->unit == SP_CSS_UNIT_PERCENT ) { + val->type = SP_BASELINE_SHIFT_PERCENTAGE; + } else { + val->type = SP_BASELINE_SHIFT_LENGTH; + } + return; + } +} + /** * Set SPIFilter object from string. @@ -3369,6 +3468,19 @@ sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const /** + * Set SPIBaselineShift object from repr attribute. + */ +static void +sp_style_read_pbaselineshift(SPIBaselineShift *val, Inkscape::XML::Node *repr, gchar const *key) +{ + gchar const *str = repr->attribute(key); + if (str) { + sp_style_read_ibaselineshift(val, str); + } +} + + +/** * Set SPIFloat object from repr attribute. */ static void @@ -3828,6 +3940,67 @@ sp_style_write_ifontsize(gchar *p, gint const len, gchar const *key, } +/* + * baseline-shift is relative to parent. The only time it should + * not be written out is if it is zero (or not set). + */ +static bool +sp_baseline_shift_notzero(SPIBaselineShift const *const a ) +{ + if( a->type == SP_BASELINE_SHIFT_LITERAL ) { + if( a->literal == SP_CSS_BASELINE_SHIFT_BASELINE ) { + return false; + } + } else { + if( a->value == 0.0 ) { + return false; + } + } + return true; +} + +/** + * Write SPIBaselineShift object into string. + */ +static gint +sp_style_write_ibaselineshift(gchar *p, gint const len, gchar const *key, + SPIBaselineShift const *const val, SPIBaselineShift const *const base, + guint const flags) +{ + if ((flags & SP_STYLE_FLAG_ALWAYS) + || ((flags & SP_STYLE_FLAG_IFSET) && val->set) + || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set + && (!base->set || sp_baseline_shift_notzero(val) ))) + { + if (val->inherit) { + return g_snprintf(p, len, "%s:inherit;", key); + } else if (val->type == SP_BASELINE_SHIFT_LITERAL) { + for (unsigned i = 0; enum_baseline_shift[i].key; i++) { + if (enum_baseline_shift[i].value == static_cast< gint > (val->literal) ) { + return g_snprintf(p, len, "%s:%s;", key, enum_baseline_shift[i].key); + } + } + } else if (val->type == SP_BASELINE_SHIFT_LENGTH) { + if( val->unit == SP_CSS_UNIT_EM || val->unit == SP_CSS_UNIT_EX ) { + Inkscape::CSSOStringStream os; + os << key << ":" << val->value << (val->unit == SP_CSS_UNIT_EM ? "em;" : "ex;"); + return g_strlcpy(p, os.str().c_str(), len); + } else { + Inkscape::CSSOStringStream os; + os << key << ":" << val->computed << "px;"; // must specify px, see inkscape bug 1221626, mozilla bug 234789 + return g_strlcpy(p, os.str().c_str(), len); + } + } else if (val->type == SP_BASELINE_SHIFT_PERCENTAGE) { + Inkscape::CSSOStringStream os; + os << key << ":" << (val->value * 100.0) << "%;"; + return g_strlcpy(p, os.str().c_str(), len); + } + } + return 0; +} + + + /** * Write SPIFilter object into string. */ @@ -4052,7 +4225,7 @@ sp_css_attr_unset_text(SPCSSAttr *css) 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 - sp_repr_css_set_property(css, "baseline-shift", NULL); // not implemented yet + sp_repr_css_set_property(css, "baseline-shift", NULL); return css; } diff --git a/src/style.h b/src/style.h index f1b5ec534..8102ce0ea 100644 --- a/src/style.h +++ b/src/style.h @@ -43,6 +43,7 @@ class SPIString; class SPILength; class SPIPaint; class SPIFontSize; +class SPIBaselineShift; /// Float type internal to SPStyle. struct SPIFloat { @@ -143,8 +144,6 @@ struct SPILength { #define SP_STYLE_FILL_SERVER(s) (((SPStyle *) (s))->getFillPaintServer()) #define SP_STYLE_STROKE_SERVER(s) (((SPStyle *) (s))->getStrokePaintServer()) -#define SP_OBJECT_STYLE_FILL_SERVER(o) (SP_OBJECT (o)->style->getFillPaintServer()) -#define SP_OBJECT_STYLE_STROKE_SERVER(o) (SP_OBJECT (o)->style->getStrokePaintServer()) class SVGICCColor; @@ -190,6 +189,12 @@ enum { SP_FONT_SIZE_PERCENTAGE }; +enum { + SP_BASELINE_SHIFT_LITERAL, + SP_BASELINE_SHIFT_LENGTH, + SP_BASELINE_SHIFT_PERCENTAGE +}; + #define SP_FONT_SIZE ((1 << 24) - 1) #define SP_F8_16_TO_FLOAT(v) ((gdouble) (v) / (1 << 16)) @@ -208,6 +213,17 @@ struct SPIFontSize { float computed; }; +/// Baseline shift type internal to SPStyle. +struct SPIBaselineShift { + unsigned set : 1; + unsigned inherit : 1; + unsigned type : 2; + unsigned unit : 4; + unsigned literal: 2; + float value; // Can be negative + float computed; +}; + /// Text decoration type internal to SPStyle. struct SPITextDecoration { unsigned set : 1; @@ -287,6 +303,8 @@ struct SPStyle { SPIEnum block_progression; /** Writing mode (css3 text 3.2 and svg1.1 10.7.2) */ SPIEnum writing_mode; + /** Baseline shift (svg1.1 10.9.2) */ + SPIBaselineShift baseline_shift; /* SVG */ /** Anchor of the text (svg1.1 10.9.1) */ @@ -500,6 +518,12 @@ enum SPTextAnchor { SP_CSS_TEXT_ANCHOR_END }; +enum SPCSSBaselineShift { + SP_CSS_BASELINE_SHIFT_BASELINE, + SP_CSS_BASELINE_SHIFT_SUB, + SP_CSS_BASELINE_SHIFT_SUPER +}; + enum SPVisibility { SP_CSS_VISIBILITY_HIDDEN, SP_CSS_VISIBILITY_COLLAPSE, diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index c27b2291c..265210a45 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -14,7 +14,6 @@ ink_common_sources += \ svg/svg-affine.cpp \ svg/svg-color.cpp \ svg/svg-color.h \ - svg/svg-device-color.h \ svg/svg-icc-color.h \ svg/svg-length.cpp \ svg/svg-length.h \ diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 7d43b68ee..04f387798 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -35,7 +35,6 @@ #include "preferences.h" #include "svg-color.h" #include "svg-icc-color.h" -#include "svg-device-color.h" #if ENABLE_LCMS #include <lcms.h> @@ -606,110 +605,6 @@ bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ) return sp_svg_read_icc_color(str, NULL, dest); } -bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest) -{ - bool good = true; - unsigned int max_colors; - - if ( end_ptr ) { - *end_ptr = str; - } - if ( dest ) { - dest->colors.clear(); - } - - if ( !str ) { - // invalid input - good = false; - } else { - while ( g_ascii_isspace(*str) ) { - str++; - } - - dest->type = DEVICE_COLOR_INVALID; - if (strneq( str, "device-gray(", 12 )){ - dest->type = DEVICE_GRAY; - max_colors=1; - str += 12; - } - - if (strneq( str, "device-rgb(", 11 )){ - dest->type = DEVICE_RGB; - max_colors=3; - str += 11; - } - - if (strneq( str, "device-cmyk(", 12 )){ - dest->type = DEVICE_CMYK; - max_colors=4; - str += 12; - } - - if (strneq( str, "device-nchannel(", 16 )){ - dest->type = DEVICE_NCHANNEL; - max_colors=0; - str += 16; - } - - if ( dest->type != DEVICE_COLOR_INVALID ) { - while ( g_ascii_isspace(*str) ) { - str++; - } - - while ( *str && *str != ')' ) { - if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') { - gchar* endPtr = 0; - gdouble dbl = g_ascii_strtod( str, &endPtr ); - if ( !errno ) { - if ( dest ) { - dest->colors.push_back( dbl ); - } - str = endPtr; - } else { - good = false; - break; - } - - while ( g_ascii_isspace(*str) || *str == ',' ) { - str++; - } - } else { - break; - } - } - } - - // We need to have ended on a closing parenthesis - if ( good ) { - while ( g_ascii_isspace(*str) ) { - str++; - } - good &= (*str == ')'); - } - } - - if ( dest->colors.size() == 0) good=false; - if ( dest->type != DEVICE_NCHANNEL && (dest->colors.size() != max_colors)) good=false; - - if ( good ) { - if ( end_ptr ) { - *end_ptr = str; - } - } else { - if ( dest ) { - dest->type = DEVICE_COLOR_INVALID; - dest->colors.clear(); - } - } - - return good; -} - - -bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest) -{ - return sp_svg_read_device_color(str, NULL, dest); -} /* Local Variables: diff --git a/src/svg/svg-color.h b/src/svg/svg-color.h index f4e534652..a3868c149 100644 --- a/src/svg/svg-color.h +++ b/src/svg/svg-color.h @@ -4,7 +4,6 @@ #include <glib/gtypes.h> class SVGICCColor; -class SVGDeviceColor; guint32 sp_svg_read_color(gchar const *str, unsigned int dfl); guint32 sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def); @@ -12,8 +11,6 @@ void sp_svg_write_color(char *buf, unsigned int buflen, unsigned int rgba32); bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest ); bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ); -bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest ); -bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest ); void icc_color_to_sRGB(SVGICCColor* dest, guchar* r, guchar* g, guchar* b); #endif /* !SVG_SVG_COLOR_H_SEEN */ diff --git a/src/svg/svg-device-color.h b/src/svg/svg-device-color.h deleted file mode 100644 index 305133ed3..000000000 --- a/src/svg/svg-device-color.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SVG_DEVICE_COLOR_H_SEEN -#define SVG_DEVICE_COLOR_H_SEEN - -#include <string> -#include <vector> - -typedef enum {DEVICE_COLOR_INVALID, DEVICE_GRAY, DEVICE_RGB, DEVICE_CMYK, DEVICE_NCHANNEL} SVGDeviceColorType; - -struct SVGDeviceColor { - SVGDeviceColorType type; - std::vector<double> colors; -}; - - -#endif /* !SVG_DEVICE_COLOR_H_SEEN */ - -/* - 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:encoding=utf-8:textwidth=99 : diff --git a/src/text-editing.cpp b/src/text-editing.cpp index e93ebdffa..372f5026d 100644 --- a/src/text-editing.cpp +++ b/src/text-editing.cpp @@ -928,7 +928,7 @@ sp_te_set_repr_text_multiline(SPItem *text, gchar const *str) /** Returns the attributes block and the character index within that block which represents the iterator \a position. */ -static TextTagAttributes* +TextTagAttributes* text_tag_attributes_at_position(SPItem *item, Inkscape::Text::Layout::iterator const &position, unsigned *char_index) { if (item == NULL || char_index == NULL || !SP_IS_TEXT(item)) @@ -973,6 +973,36 @@ sp_te_adjust_kerning_screen (SPItem *item, Inkscape::Text::Layout::iterator cons } void +sp_te_adjust_dx (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, double delta) +{ + unsigned char_index; + TextTagAttributes *attributes = text_tag_attributes_at_position(item, std::min(start, end), &char_index); + if (attributes) attributes->addToDx(char_index, delta); + if (start != end) { + attributes = text_tag_attributes_at_position(item, std::max(start, end), &char_index); + if (attributes) attributes->addToDx(char_index, -delta); + } + + item->updateRepr(); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void +sp_te_adjust_dy (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, double delta) +{ + unsigned char_index; + TextTagAttributes *attributes = text_tag_attributes_at_position(item, std::min(start, end), &char_index); + if (attributes) attributes->addToDy(char_index, delta); + if (start != end) { + attributes = text_tag_attributes_at_position(item, std::max(start, end), &char_index); + if (attributes) attributes->addToDy(char_index, -delta); + } + + item->updateRepr(); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void sp_te_adjust_rotation_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble pixels) { // divide increment by zoom @@ -1012,6 +1042,25 @@ sp_te_adjust_rotation(SPItem *text, Inkscape::Text::Layout::iterator const &star } void +sp_te_set_rotation(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop */*desktop*/, gdouble degrees) +{ + unsigned char_index; + TextTagAttributes *attributes = text_tag_attributes_at_position(text, std::min(start, end), &char_index); + if (attributes == NULL) return; + + if (start != end) { + for (Inkscape::Text::Layout::iterator it = std::min(start, end) ; it != std::max(start, end) ; it.nextCharacter()) { + attributes = text_tag_attributes_at_position(text, it, &char_index); + if (attributes) attributes->setRotate(char_index, degrees); + } + } else + attributes->setRotate(char_index, degrees); + + text->updateRepr(); + text->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); +} + +void sp_te_adjust_tspan_letterspacing_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble by) { g_return_if_fail (text != NULL); diff --git a/src/text-editing.h b/src/text-editing.h index 7e845dbc9..038458ff7 100644 --- a/src/text-editing.h +++ b/src/text-editing.h @@ -17,6 +17,7 @@ #include <utility> // std::pair #include "libnrtype/Layout-TNG.h" #include <libnr/nr-forward.h> +#include "text-tag-attributes.h" class SPCSSAttr; struct SPItem; @@ -50,10 +51,16 @@ gchar *sp_te_get_string_multiline(SPItem const *text); Glib::ustring sp_te_get_string_multiline(SPItem const *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end); void sp_te_set_repr_text_multiline(SPItem *text, gchar const *str); +TextTagAttributes* +text_tag_attributes_at_position(SPItem *item, Inkscape::Text::Layout::iterator const &position, unsigned *char_index); + void sp_te_adjust_kerning_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, Geom::Point by); +void sp_te_adjust_dx (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, double delta); +void sp_te_adjust_dy (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, double delta); void sp_te_adjust_rotation_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble pixels); void sp_te_adjust_rotation(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble degrees); +void sp_te_set_rotation(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble degrees); void sp_te_adjust_tspan_letterspacing_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble by); void sp_te_adjust_linespacing_screen(SPItem *text, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, SPDesktop *desktop, gdouble by); diff --git a/src/text-tag-attributes.h b/src/text-tag-attributes.h index 7a7ba5763..197bfb73f 100644 --- a/src/text-tag-attributes.h +++ b/src/text-tag-attributes.h @@ -90,15 +90,36 @@ public: position. */ void transform(Geom::Matrix const &matrix, double scale_x, double scale_y, bool extend_zero_length = false); + /** Gets current value of dx vector at \a index. */ + double getDx(unsigned index); + + /** Gets current value of dy vector at \a index. */ + double getDy(unsigned index); + + /** Adds the given value to the dx vector at the given + \a index. The vector is extended if necessary. */ + void addToDx(unsigned index, double delta); + + /** Adds the given value to the dy vector at the given + \a index. The vector is extended if necessary. */ + void addToDy(unsigned index, double delta); + /** Adds the given values to the dx and dy vectors at the given \a index. The vectors are extended if necessary. */ void addToDxDy(unsigned index, Geom::Point const &adjust); + /** Gets current value of rotate vector at \a index. */ + double getRotate(unsigned index); + /** Adds the given value to the rotate vector at the given \a index. The vector is extended if necessary. Delta is measured in degrees, clockwise positive. */ void addToRotate(unsigned index, double delta); + /** Sets rotate vector at the given \a index. The vector is extended if + necessary. Angle is measured in degrees, clockwise positive. */ + void setRotate(unsigned index, double angle); + /** Returns the first coordinates in the x and y vectors. If either is zero length, 0.0 is used for that coordinate. */ Geom::Point firstXY() const; diff --git a/src/tweak-context.cpp b/src/tweak-context.cpp index 13299b5a4..904d0cb23 100644 --- a/src/tweak-context.cpp +++ b/src/tweak-context.cpp @@ -837,20 +837,24 @@ tweak_colors_in_gradient (SPItem *item, bool fill_or_stroke, // Normalize pos to 0..1, taking into accound gradient spread: double pos_e = pos; - if (gradient->spread == SP_GRADIENT_SPREAD_PAD) { - if (pos > 1) + if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) { + if (pos > 1) { pos_e = 1; - if (pos < 0) + } + if (pos < 0) { pos_e = 0; - } else if (gradient->spread == SP_GRADIENT_SPREAD_REPEAT) { - if (pos > 1 || pos < 0) + } + } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) { + if (pos > 1 || pos < 0) { pos_e = pos - floor(pos); - } else if (gradient->spread == SP_GRADIENT_SPREAD_REFLECT) { + } + } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REFLECT) { if (pos > 1 || pos < 0) { bool odd = ((int)(floor(pos)) % 2 == 1); pos_e = pos - floor(pos); - if (odd) + if (odd) { pos_e = 1 - pos_e; + } } } diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index dd1f981b5..0a64e7fa7 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -653,23 +653,23 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) { // copy fill and stroke styles (patterns and gradients) - SPStyle *style = SP_OBJECT_STYLE(item); + SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server) ) { _copyGradient(SP_GRADIENT(server)); } - if (SP_IS_PATTERN(server)) { + if ( SP_IS_PATTERN(server) ) { _copyPattern(SP_PATTERN(server)); } } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server) ) { _copyGradient(SP_GRADIENT(server)); } - if (SP_IS_PATTERN(server)) { + if ( SP_IS_PATTERN(server) ) { _copyPattern(SP_PATTERN(server)); } } @@ -1057,6 +1057,8 @@ void ClipboardManagerImpl::_applyPathEffect(SPItem *item, gchar const *effectsta { SPLPEItem *lpeitem = SP_LPE_ITEM(item); // for each effect in the stack, check if we need to fork it before adding it to the item + sp_lpe_item_fork_path_effects_if_necessary(lpeitem, 1); + std::istringstream iss(effectstack); std::string href; while (std::getline(iss, href, ';')) @@ -1065,8 +1067,7 @@ void ClipboardManagerImpl::_applyPathEffect(SPItem *item, gchar const *effectsta if (!obj) { return; } - // if the effectstack is not used by anyone, we might as well take it - LivePathEffectObject *lpeobj = LIVEPATHEFFECT(obj)->fork_private_if_necessary(1); + LivePathEffectObject *lpeobj = LIVEPATHEFFECT(obj); sp_lpe_item_add_path_effect(lpeitem, lpeobj); } } diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp index bbd02fa5d..8d467d53f 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -216,7 +216,7 @@ void AboutBox::initStrings() { * To update it, execute this snippet of sed magic in the toplevel * source directory: * - * sed -e 's/^\(.*\) \([^ ]*\)*$/\2_ \1/' ../../../AUTHORS + * sed -e 's/^\(.*\) \([^ ]*\)*$/\2_ \1/' AUTHORS | sort | sed -e 's/^\([^_]*\)_ \(.*\)$/\2 \1/;s/^.*$/"\0\\n"/;$s/\\n//' | zenity --text-info @@ -224,140 +224,152 @@ void AboutBox::initStrings() { * and paste the result from the combo box here. */ authors_text = - "Maximilian Albert\n" - "Josh Andler\n" - "Jean-François Barraud\n" - "Bill Baxter\n" - "John Bintz\n" - "Arpad Biro\n" - "Nicholas Bishop\n" - "Joshua L. Blocher\n" - "Henrik Bohre\n" - "Boldewyn\n" - "Daniel Borgmann\n" - "Bastien Bouclet\n" - "Hans Breuer\n" - "Gustav Broberg\n" - "Christopher Brown\n" - "Marcus Brubaker\n" - "Luca Bruno\n" - "Nicu Buculei\n" - "Bulia Byak\n" - "Ian Caldwell\n" - "Gail Carmichael\n" - "Ed Catmur\n" - "Chema Celorio\n" - "Johan Ceuppens\n" - "Zbigniew Chyla\n" - "Alexander Clausen\n" - "John Cliff\n" - "Kees Cook\n" - "Ben Cromwell\n" - "Robert Crosbie\n" - "Jon Cruz\n" - "Aurélie De-Cooman\n" - "Milosz Derezynski\n" - "Daniel Díaz\n" - "Bruno Dilly\n" - "Larry Doolittle\n" - "Tim Dwyer\n" - "Maxim V. Dziumanenko\n" - "Johan Engelen\n" - "Miklos Erdelyi\n" - "Ulf Erikson\n" - "Noé Falzon\n" - "Frank Felfe\n" - "Andrew Fitzsimon\n" - "Edward Flick\n" - "Marcin Floryan\n" - "Ben Fowler\n" - "Fred\n" - "Cedric Gemy\n" - "Steren Giannini\n" - "Olivier Gondouin\n" - "Ted Gould\n" - "Toine de Greef\n" - "Michael Grosberg\n" - "Bryce Harrington\n" - "Dale Harvey\n" - "Aurélio Heckert\n" - "Carl Hetherington\n" - "Jos Hirth\n" - "Thomas Holder\n" - "Joel Holdsworth\n" - "Alan Horkan\n" - "Karl Ove Hufthammer\n" - "Richard Hughes\n" - "Nathan Hurst\n" - "inductiveload\n" - "Thomas Ingham\n" - "Jean-Olivier Irisson\n" - "Bob Jamison\n" - "jEsuSdA\n" - "Lauris Kaplinski\n" - "Lynn Kerby\n" - "Niko Kiirala\n" - "James Kilfiger\n" - "Jason Kivlighn\n" - "Adrian Knoth\n" - "Krzysztof Kosiński\n" - "Petr Kovar\n" - "Raph Levien\n" - "Diederik van Lierop\n" - "Nicklas Lindgren\n" - "Vitaly Lipatov\n" - "Ivan Louette\n" - "Colin Marquardt\n" - "Dmitry G. Mastrukov\n" - "Matiphas\n" - "Michael Meeks\n" - "Federico Mena\n" - "MenTaLguY\n" - "Aubanel Monnier\n" - "Tim Mooney\n" - "Derek P. Moore\n" - "Peter Moulder\n" - "Jörg Müller\n" - "Yukihiro Nakai\n" - "Victor Navez\n" - "Christian Neumair\n" - "Andreas Nilsson\n" - "Mitsuru Oka\n" - "Marten Owens\n" - "Alvin Penner\n" - "Jon Phillips\n" - "Zdenko Podobny\n" - "Alexandre Prokoudine\n" - "Jean-René Reinhard\n" - "Alexey Remizov\n" - "Frederic Rodrigo\n" - "Hugo Rodrigues\n" - "Juarez Rudsatz\n" - "Xavier Conde Rueda\n" - "Felipe Corrêa da Silva Sanches\n" - "Christian Schaller\n" - "Marco Scholten\n" - "Tom von Schwerdtner\n" - "Danilo Šegan\n" - "Shivaken\n" - "Michael Sloan\n" - "Boštjan Špetič\n" - "Aaron Spike\n" - "Kaushik Sridharan\n" - "Ralf Stephan\n" - "Dariusz Stojek\n" - "Pat Suwalski\n" - "Adib Taraben\n" - "Hugh Tebby\n" - "Jonas Termeau\n" - "David Turner\n" - "Andre Twupack\n" - "Aleksandar Urošević\n" - "Lucas Vieites\n" - "Michael Wybrow\n" - "Daniel Yacob\n" - "Masatake Yamato\n" - "David Yip"; + "Maximilian Albert\n" + "Josh Andler\n" + "Tavmjong Bah\n" + "Pierre Barbry-Blot\n" + "Jean-François Barraud\n" + "Bill Baxter\n" + "John Beard\n" + "John Bintz\n" + "Arpad Biro\n" + "Nicholas Bishop\n" + "Joshua L. Blocher\n" + "Henrik Bohre\n" + "Boldewyn\n" + "Daniel Borgmann\n" + "Bastien Bouclet\n" + "Hans Breuer\n" + "Gustav Broberg\n" + "Christopher Brown\n" + "Marcus Brubaker\n" + "Luca Bruno\n" + "Nicu Buculei\n" + "Bulia Byak\n" + "Pierre Caclin\n" + "Ian Caldwell\n" + "Gail Carmichael\n" + "Ed Catmur\n" + "Chema Celorio\n" + "Johan Ceuppens\n" + "Zbigniew Chyla\n" + "Alexander Clausen\n" + "John Cliff\n" + "Kees Cook\n" + "Ben Cromwell\n" + "Robert Crosbie\n" + "Jon Cruz\n" + "Aurélie De-Cooman\n" + "Milosz Derezynski\n" + "Daniel Díaz\n" + "Bruno Dilly\n" + "Larry Doolittle\n" + "Tim Dwyer\n" + "Maxim V. Dziumanenko\n" + "Johan Engelen\n" + "Miklos Erdelyi\n" + "Ulf Erikson\n" + "Noé Falzon\n" + "Frank Felfe\n" + "Andrew Fitzsimon\n" + "Edward Flick\n" + "Marcin Floryan\n" + "Ben Fowler\n" + "Fred\n" + "Cedric Gemy\n" + "Steren Giannini\n" + "Olivier Gondouin\n" + "Ted Gould\n" + "Toine de Greef\n" + "Michael Grosberg\n" + "Bryce Harrington\n" + "Dale Harvey\n" + "Aurélio A. Heckert\n" + "Carl Hetherington\n" + "Jos Hirth\n" + "Hannes Hochreiner\n" + "Thomas Holder\n" + "Joel Holdsworth\n" + "Alan Horkan\n" + "Karl Ove Hufthammer\n" + "Richard Hughes\n" + "Nathan Hurst\n" + "inductiveload\n" + "Thomas Ingham\n" + "Jean-Olivier Irisson\n" + "Bob Jamison\n" + "jEsuSdA\n" + "Lauris Kaplinski\n" + "Lynn Kerby\n" + "Niko Kiirala\n" + "James Kilfiger\n" + "Jason Kivlighn\n" + "Adrian Knoth\n" + "Krzysztof Kosiński\n" + "Petr Kovar\n" + "Benoît Lavorata\n" + "Alex Leone\n" + "Julien Leray\n" + "Raph Levien\n" + "Diederik van Lierop\n" + "Nicklas Lindgren\n" + "Vitaly Lipatov\n" + "Ivan Louette\n" + "Pierre-Antoine Marc\n" + "Aurel-Aimé Marmion\n" + "Colin Marquardt\n" + "Dmitry G. Mastrukov\n" + "Matiphas\n" + "Michael Meeks\n" + "Federico Mena\n" + "MenTaLguY\n" + "Aubanel Monnier\n" + "Vincent Montagne\n" + "Tim Mooney\n" + "Derek P. Moore\n" + "Peter Moulder\n" + "Jörg Müller\n" + "Yukihiro Nakai\n" + "Victor Navez\n" + "Christian Neumair\n" + "Andreas Nilsson\n" + "Mitsuru Oka\n" + "Marten Owens\n" + "Alvin Penner\n" + "Jon Phillips\n" + "Zdenko Podobny\n" + "Alexandre Prokoudine\n" + "Jean-René Reinhard\n" + "Alexey Remizov\n" + "Frederic Rodrigo\n" + "Hugo Rodrigues\n" + "Juarez Rudsatz\n" + "Xavier Conde Rueda\n" + "Felipe Corrêa da Silva Sanches\n" + "Christian Schaller\n" + "Marco Scholten\n" + "Tom von Schwerdtner\n" + "Danilo Šegan\n" + "Shivaken\n" + "Michael Sloan\n" + "Boštjan Špetič\n" + "Aaron Spike\n" + "Kaushik Sridharan\n" + "Ralf Stephan\n" + "Dariusz Stojek\n" + "Martin Sucha\n" + "Pat Suwalski\n" + "Adib Taraben\n" + "Hugh Tebby\n" + "Jonas Termeau\n" + "David Turner\n" + "Andre Twupack\n" + "Aleksandar Urošević\n" + "Lucas Vieites\n" + "Michael Wybrow\n" + "Daniel Yacob\n" + "Masatake Yamato\n" + "David Yip"; //############################## //# T R A N S L A T O R S @@ -385,19 +397,27 @@ void AboutBox::initStrings() { translators_text.append("\n\n\n"); } - /** - * This list is copied from the TRANSLATORS file. It should - * be periodically updated. - */ + /* This text is copied from the TRANSLATORS file. + * To update it, execute this snippet of sed magic in the toplevel + * source directory: + * + * sed -e 's/^\(.*\) \([^ ]*\)*$/\2_ \1/' TRANSLATORS + | sed -e 's/^\([^_]*\)_ \(.*\)$/\2 \1/;s/^.*$/"\0\\n"/;$s/\\n//' + | zenity --text-info + * + * and paste the result from the combo box here. + */ gchar const *allTranslators = "3ARRANO.com <3arrano@3arrano.com>, 2005.\n" - "Adib Taraben <theadib@yahoo.com>, 2004.\n" + "Adib Taraben <theadib@googlemail.com>, 2004.\n" + "Alan Monfort <alan.monfort@free.fr>, 2009-2010.\n" "Alastair McKinstry <mckinstry@computer.org>, 2000.\n" "Aleksandar Urošević <urke@users.sourceforge.net>, 2004, 2005, 2006.\n" "Alessio Frusciante <algol@firenze.linux.it>, 2002, 2003.\n" "Alexander Shopov <ash@contact.bg>, 2006.\n" "Alexandre Prokoudine <alexandre.prokoudine@gmail.com>, 2005.\n" "Alexey Remizov <alexey@remizov.pp.ru>, 2004.\n" + "Ali Ghanavatian <ghanvatian.ali@gmail.com>, 2010.\n" "Álvaro Lopes <alvieboy@alvie.com>, 2001, 2002.\n" "Andreas Hyden <a.hyden@cyberpoint.se>, 2000.\n" "Andrius Ramanauskas <knutux@gmail.com>, 2006.\n" @@ -407,6 +427,7 @@ void AboutBox::initStrings() { "Arman Aksoy <armish@linux-sevenler.de>, 2003.\n" "Arpad Biro <biro_arpad@yahoo.com>, 2004, 2005.\n" "Benedikt Roth <Benedikt.Roth@gmx.net>, 2000.\n" + "Benno Schulenberg <benno@vertaalt.nl>, 2008.\n" "Boštjan Špetič <igzebedze@cyberpipe.org>, 2004, 2005.\n" "Brisa Francesco <fbrisa@yahoo.it>, 2000.\n" "bulia byak <buliabyak@users.sf.net>, 2004.\n" @@ -414,25 +435,33 @@ void AboutBox::initStrings() { "Christian Meyer <chrisime@gnome.org>, 2000-2002.\n" "Christian Neumair <chris@gnome-de.org>, 2002, 2003.\n" "Christian Rose <menthos@menthos.com>, 2000, 2001, 2002, 2003.\n" + "Cristian Secară <cristi@secarica.ro>, 2010.\n" "Christophe Merlet (RedFox) <redfox@redfoxcenter.org>, 2000-2002.\n" + "Clytie Siddall <clytie@riverland.net.au>, 2004-2008.\n" "Colin Marquardt <colin@marquardt-home.de>, 2004-2006.\n" "Cédric Gemy <radar.map35@free.fr>, 2006.\n" "Daniel Díaz <yosoy@danieldiaz.org>, 2004.\n" "Didier Conchaudron <conchaudron@free.fr>, 2003.\n" "Dorji Tashi <dorjee_doss@hotmail.com>, 2006.\n" "Duarte Loreto <happyguy_pt@hotmail.com> 2002, 2003 (Maintainer).\n" + "Equipe de Tradução Inkscape Brasil <www.inkscapebrasil.org>, 2007.\n" "Fatih Demir <kabalak@gtranslator.org>, 2000.\n" + "Foppe Benedictus <foppe.benedictus@gmail.com>, 2007-2009.\n" "Francesc Dorca <f.dorca@filnet.es>, 2003. Traducció sodipodi.\n" "Francisco Javier F. Serrador <serrador@arrakis.es>, 2003.\n" "Francisco Xosé Vázquez Grandal <fxvazquez@arrakis.es>, 2001.\n" "Frederic Rodrigo <f.rodrigo free.fr>, 2004-2005.\n" "Ge'ez Frontier Foundation <locales@geez.org>, 2002.\n" + "Hleb Valoshka <375gnu@gmail.com>, 2008-2009.\n" "Hizkuntza Politikarako Sailburuordetza <hizkpol@ej-gv.es>, 2005.\n" "Ilia Penev <lichopicho@gmail.com>, 2006.\n" + "Ivan Masár <helix84@centrum.sk>, 2006, 2007, 2008, 2009, 2010. \n" "Iñaki Larrañaga <dooteo@euskalgnu.org>, 2006.\n" - "Jin-Hwan Jeong <yongdoria@gmail.com>, 2009.\n" + "Jeffrey Steve Borbón Sanabria <jeff_kerokid@yahoo.com>, 2005.\n" + "Joaquim Perez i Noguer <noguer@gmail.com>, 2008-2009.\n" "Jörg Müller <jfm@ram-brand.de>, 2005.\n" - "Jeroen van der Vegt <ajvdvegt (at) 123mail.org>, 2003, 2005.\n" + "Jeroen van der Vegt <jvdvegt@gmail.com>, 2003, 2005, 2008.\n" + "Jin-Hwan Jeong <yongdoria@gmail.com>, 2009.\n" "Jonathan Ernst <jernst@users.sourceforge.net>, 2006.\n" "Jose Antonio Salgueiro Aquino <developer@telefonica.net>, 2003.\n" "Josef Vybiral <josef.vybiral@gmail.com>, 2005-2006.\n" @@ -441,29 +470,40 @@ void AboutBox::initStrings() { "Jurmey Rabgay <jur_gay@yahoo.com>, 2006.\n" "Kai Lahmann <kailahmann@01019freenet.de>, 2000.\n" "Karl Ove Hufthammer <karl@huftis.org>, 2004, 2005.\n" - "Keld Simonsen <keld@dkuug.dk>, 2000-2001.\n" + "KATSURAGAWA Naoki <naopon@private.email.ne.jp>, 2006.\n" + "Keld Simonsen <keld@dkuug.dk>, 2000, 2001.\n" + "Kenji Inoue <kenz@oct.zaq.ne.jp>, 2006-2007.\n" + "Khandakar Mujahidul Islam <suzan@bengalinux.org>, 2006.\n" "Kitae <bluetux@gmail.com>, 2006.\n" "Kjartan Maraas <kmaraas@gnome.org>, 2000-2002.\n" + "Kris De Gussem <kris.DeGussem@gmail.com>, 2008-2010.\n" "Lauris Kaplinski <lauris@ariman.ee>, 2000.\n" + "Leandro Regueiro <leandro.regueiro@gmail.com>, 2006-2008,2010.\n" + "Liu Xiaoqin <liuxqsmile@gmail.com>, 2008.\n" "Luca Bruno <luca.br@uno.it>, 2005.\n" - "Lucas Vieites Fariña<lucas@asixinformatica.com>, 2003-2005.\n" + "Lucas Vieites Fariña<lucas@codexion.com>, 2003-2010.\n" "Mahesh subedi <submanesh@hotmail.com>, 2006.\n" - "Marcin Floryan <marcin.floryan@gmail.com>, 2008.\n" - "Martin Srebotnjak, <miles@filmsi.net>, 2005.\n" + "Martin Srebotnjak, <miles@filmsi.net>, 2005, 2010.\n" "Masatake YAMATO <jet@gyve.org>, 2002.\n" + "Masato Hashimoto <cabezon.hashimoto@gmail.com>, 2009-2010.\n" "Matiphas <matiphas _a_ free _point_ fr>, 2004-2006.\n" "Mattias Hultgren <mattias_hultgren@tele2.se>, 2005.\n" "Maxim Dziumanenko <mvd@mylinux.com.ua>, 2004.\n" + "Mətin Əmirov <metin@karegen.com>, 2003.\n" "Mitsuru Oka <oka326@parkcity.ne.jp>, 2002.\n" "Morphix User <pchitrakar@gmail.com>, 2006.\n" "Mufit Eribol <meribol@ere.com.tr>, 2000.\n" - "Nicolas Dufour <nicoduf@yahoo.fr>, 2008-2009.\n" + "Muhammad Bashir Al-Noimi <mhdbnoimi@gmail.com>, 2008.\n" + "Myckel Habets <myckel@sdf.lonestar.org>, 2008.\n" + "Nguyen Dinh Trung <nguyendinhtrung141@gmail.com>, 2007, 2008.\n" + "Nicolas Dufour <nicoduf@yahoo.fr>, 2008-2010.\n" "Pawan Chitrakar <pchitrakar@gmail.com>, 2006.\n" "Przemysław Loesch <p_loesch@poczta.onet.pl>, 2005.\n" "Quico Llach <quico@softcatala.org>, 2000. Traducció sodipodi.\n" - "Raymond Ostertag <raymond@linuxgraphic.org>, 2002-2003.\n" + "Raymond Ostertag <raymond@linuxgraphic.org>, 2002, 2003.\n" "Riku Leino <tsoots@gmail.com>, 2006.\n" "Rune Rønde Laursen <runerl@skjoldhoej.dk>, 2006.\n" + "Ruud Steltenpool <svg@steltenpower.com>, 2006.\n" "Serdar Soytetir <sendirom@gmail.com>, 2005.\n" "shivaken <shivaken@owls-nest.net>, 2004.\n" "Shyam Krishna Bal <shyamkrishna_bal@yahoo.com>, 2006.\n" @@ -471,10 +511,13 @@ void AboutBox::initStrings() { "Spyros Blanas <cid_e@users.sourceforge.net>, 2006.\n" "Stefan Graubner <pflaumenmus92@gmx.net>, 2005.\n" "Supranee Thirawatthanasuk <supranee@opentle.org>, 2006.\n" - "Takeshi Aihana <aihana@muc.biglobe.ne.jp>, 2000-2001.\n" + "Takeshi Aihana <aihana@muc.biglobe.ne.jp>, 2000, 2001.\n" + "Tim Sheridan <tim.sheridan@gmail.com>, 2007-2010.\n" "Theppitak Karoonboonyanan <thep@linux.thai.net>, 2006.\n" + "Thiago Pimentel <thiago.merces@gmail.com>, 2006.\n" + "Toshifumi Sato <sato@centrosystem.com>, 2005.\n" "Jon South <striker@lunar-linux.org>, 2006. \n" - "Uwe Schöler <uschoeler@yahoo.de>, 2006.\n" + "Uwe Schöler <oss@oss-marketplace.com>, 2006.\n" "Valek Filippov <frob@df.ru>, 2000, 2003.\n" "Victor Dachev <vdachev@gmail.com>, 2006.\n" "Vincent van Adrighem <V.vanAdrighem@dirck.mine.nu>, 2003.\n" @@ -484,12 +527,14 @@ void AboutBox::initStrings() { "Wang Li <charlesw1234@163.com>, 2002.\n" "Wei-Lun Chao <william.chao@ossii.com.tw>, 2006.\n" "Wolfram Strempfer <wolfram@strempfer.de>, 2006.\n" - "Xavier Conde Rueda <xavi.conde@gmail.com>, 2004, 2005.\n" + "Xavier Conde Rueda <xavi.conde@gmail.com>, 2004-2008.\n" + "Yaron Shahrabani <sh.yaron@gmail.com>, 2009.\n" "Yukihiro Nakai <nakai@gnome.gr.jp>, 2000, 2003.\n" "Yuri Beznos <zhiz0id@gmail.com>, 2006.\n" + "Yuri Chornoivan <yurchor@ukr.net>, 2007-2010.\n" "Yuri Syrota <rasta@renome.rovno.ua>, 2000.\n" "Yves Guillou <yvesguillou@users.sourceforge.net>, 2004.\n" - "Zdenko Podobný <zdpo@mailbox.sk>, 2003, 2004.\n" + "Zdenko Podobný <zdpo@mailbox.sk>, 2003, 2004." ; translators_text.append(allTranslators); diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 6d3bc817e..aab9d89d7 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -41,7 +41,7 @@ #include "ui/dialog/icon-preview.h" #include "ui/dialog/floating-behavior.h" #include "ui/dialog/dock-behavior.h" -#include "ui/dialog/print-colors-preview-dialog.h" +//#include "ui/dialog/print-colors-preview-dialog.h" #include "preferences.h" #ifdef ENABLE_SVG_FONTS @@ -104,7 +104,7 @@ DialogManager::DialogManager() { registerFactory("LivePathEffect", &create<LivePathEffectEditor, FloatingBehavior>); registerFactory("Memory", &create<Memory, FloatingBehavior>); registerFactory("Messages", &create<Messages, FloatingBehavior>); - registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, FloatingBehavior>); +// registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, FloatingBehavior>); registerFactory("Script", &create<ScriptDialog, FloatingBehavior>); #ifdef ENABLE_SVG_FONTS registerFactory("SvgFontsDialog", &create<SvgFontsDialog, FloatingBehavior>); @@ -132,7 +132,7 @@ DialogManager::DialogManager() { registerFactory("LivePathEffect", &create<LivePathEffectEditor, DockBehavior>); registerFactory("Memory", &create<Memory, DockBehavior>); registerFactory("Messages", &create<Messages, DockBehavior>); - registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, DockBehavior>); +// registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, DockBehavior>); registerFactory("Script", &create<ScriptDialog, DockBehavior>); #ifdef ENABLE_SVG_FONTS registerFactory("SvgFontsDialog", &create<SvgFontsDialog, DockBehavior>); diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 86baa85cd..33fdf8327 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -825,6 +825,7 @@ DocumentProperties::update() double const doc_w_px = sp_document_width(sp_desktop_document(dt)); double const doc_h_px = sp_document_height(sp_desktop_document(dt)); _page_sizer.setDim (doc_w_px, doc_h_px); + _page_sizer.updateFitMarginsUI(SP_OBJECT_REPR(nv)); //-----------------------------------------------------------guide page diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 3c48a7972..8f5194eda 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -540,8 +540,9 @@ void InkscapePreferences::initPageTools() this->AddNewObjectsStyle(_page_eraser, "/tools/eraser"); //LPETool - this->AddPage(_page_lpetool, _("LPE Tool"), iter_tools, PREFS_PAGE_TOOLS_LPETOOL); - this->AddNewObjectsStyle(_page_lpetool, "/tools/lpetool"); + // commented out, because the LPETool is not finished yet. + //this->AddPage(_page_lpetool, _("LPE Tool"), iter_tools, PREFS_PAGE_TOOLS_LPETOOL); + //this->AddNewObjectsStyle(_page_lpetool, "/tools/lpetool"); //Text this->AddPage(_page_text, _("Text"), iter_tools, PREFS_PAGE_TOOLS_TEXT); @@ -1116,11 +1117,11 @@ void InkscapePreferences::initPageUI() _("Mongolian (mn)"), _("Nepali (ne)"), _("Norwegian Bokmål (nb)"), _("Norwegian Nynorsk (nn)"), _("Panjabi (pa)"), _("Polish (pl)"), _("Portuguese (pt)"), _("Portuguese/Brazil (pt_BR)"), _("Romanian (ro)"), _("Russian (ru)"), _("Serbian (sr)"), _("Serbian in Latin script (sr@latin)"), _("Slovak (sk)"), _("Slovenian (sl)"), _("Spanish (es)"), _("Spanish/Mexico (es_MX)"), - _("Swedish (sv)"), _("Thai (th)"), _("Turkish (tr)"), _("Ukrainian (uk)"), _("Vietnamese (vi)")}; + _("Swedish (sv)"),_("Telugu (te_IN)"), _("Thai (th)"), _("Turkish (tr)"), _("Ukrainian (uk)"), _("Vietnamese (vi)")}; Glib::ustring langValues[] = {"", "sq", "am", "ar", "hy", "az", "eu", "be", "bg", "bn", "br", "ca", "ca@valencia", "zh_CN", "zh_TW", "hr", "cs", "da", "nl", "dz", "de", "el", "en", "en_AU", "en_CA", "en_GB", "en_US@piglatin", "eo", "et", "fa", "fi", "fr", "ga", "gl", "he", "hu", "id", "it", "ja", "km", "rw", "ko", "lt", "mk", "mn", "ne", "nb", "nn", "pa", - "pl", "pt", "pt_BR", "ro", "ru", "sr", "sr@latin", "sk", "sl", "es", "es_MX", "sv", "th", "tr", "uk", "vi" }; + "pl", "pt", "pt_BR", "ro", "ru", "sr", "sr@latin", "sk", "sl", "es", "es_MX", "sv", "te_IN", "th", "tr", "uk", "vi" }; _ui_languages.init( "/ui/language", languages, langValues, G_N_ELEMENTS(languages), languages[0]); _page_ui.add_line( false, _("Language (requires restart):"), _ui_languages, "", diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp index dec437be9..62ed4e639 100644 --- a/src/ui/dialog/livepatheffect-editor.cpp +++ b/src/ui/dialog/livepatheffect-editor.cpp @@ -51,9 +51,10 @@ namespace Dialog { /*#################### * Callback functions */ -static void lpeeditor_selection_changed (Inkscape::Selection * selection, gpointer data) +void lpeeditor_selection_changed (Inkscape::Selection * selection, gpointer data) { LivePathEffectEditor *lpeeditor = static_cast<LivePathEffectEditor *>(data); + lpeeditor->lpe_list_locked = false; lpeeditor->onSelectionChanged(selection); } @@ -327,6 +328,7 @@ LivePathEffectEditor::setDesktop(SPDesktop *desktop) selection_modified_connection.disconnect(); } + lpe_list_locked = false; current_desktop = desktop; if (desktop) { Inkscape::Selection *selection = sp_desktop_selection(desktop); @@ -373,6 +375,7 @@ LivePathEffectEditor::onApply() sp_document_done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Create and apply path effect")); + lpe_list_locked = false; onSelectionChanged(sel); } } diff --git a/src/ui/dialog/livepatheffect-editor.h b/src/ui/dialog/livepatheffect-editor.h index 1152e8bb8..50e948644 100644 --- a/src/ui/dialog/livepatheffect-editor.h +++ b/src/ui/dialog/livepatheffect-editor.h @@ -123,6 +123,8 @@ private: SPLPEItem * current_lpeitem; + friend void lpeeditor_selection_changed (Inkscape::Selection * selection, gpointer data); + LivePathEffectEditor(LivePathEffectEditor const &d); LivePathEffectEditor& operator=(LivePathEffectEditor const &d); }; diff --git a/src/ui/dialog/print-colors-preview-dialog.cpp b/src/ui/dialog/print-colors-preview-dialog.cpp index f4d83c271..1f999f692 100644 --- a/src/ui/dialog/print-colors-preview-dialog.cpp +++ b/src/ui/dialog/print-colors-preview-dialog.cpp @@ -7,7 +7,7 @@ * Copyright (C) 2009 Authors * Released under GNU GPLv2 (or later). Read the file 'COPYING' for more information. */ - +/* #include "desktop.h" #include "print-colors-preview-dialog.h" #include "preferences.h" @@ -98,3 +98,4 @@ PrintColorsPreviewDialog::~PrintColorsPreviewDialog(){} } // namespace Dialog } // namespace UI } // namespace Inkscape +*/ diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 6f013f4f3..29e480e24 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -14,6 +14,7 @@ #include <errno.h> #include <map> +#include <algorithm> #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types #include <gtk/gtkdnd.h> @@ -44,11 +45,12 @@ #include "swatches.h" #include "style.h" #include "ui/previewholder.h" +#include "widgets/desktop-widget.h" #include "widgets/gradient-vector.h" #include "widgets/eek-preview.h" #include "display/nr-plain-stuff.h" #include "sp-gradient-reference.h" - +#include "dialog-manager.h" namespace Inkscape { namespace UI { @@ -68,6 +70,12 @@ static std::vector<DocTrack*> docTrackings; static std::map<SwatchesPanel*, SPDocument*> docPerPanel; +class SwatchesPanelHook : public SwatchesPanel +{ +public: + static void convertGradient( GtkMenuItem *menuitem, gpointer userData ); + static void deleteGradient( GtkMenuItem *menuitem, gpointer userData ); +}; static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) { ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); @@ -84,6 +92,9 @@ static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer } static GtkWidget* popupMenu = 0; +static GtkWidget *popupSubHolder = 0; +static GtkWidget *popupSub = 0; +static std::vector<Glib::ustring> popupItems; static std::vector<GtkWidget*> popupExtras; static ColorItem* bounceTarget = 0; static SwatchesPanel* bouncePanel = 0; @@ -102,11 +113,37 @@ static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ ) } } -static void editGradientImpl( SPGradient* gr ) +static void editGradientImpl( SPDesktop* desktop, SPGradient* gr ) { if ( gr ) { - GtkWidget *dialog = sp_gradient_vector_editor_new( gr ); - gtk_widget_show( dialog ); + bool shown = false; + if ( desktop && desktop->doc() ) { + Inkscape::Selection *selection = sp_desktop_selection( desktop ); + GSList const *items = selection->itemList(); + if (items) { + SPStyle *query = sp_style_new( desktop->doc() ); + int result = objects_query_fillstroke(const_cast<GSList *>(items), query, true); + if ( (result == QUERY_STYLE_MULTIPLE_SAME) || (result == QUERY_STYLE_SINGLE) ) { + // could be pertinent + if (query->fill.isPaintserver()) { + SPPaintServer* server = query->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { + SPGradient* grad = SP_GRADIENT(server); + if ( grad->isSwatch() && grad->getId() == gr->getId()) { + desktop->_dlg_mgr->showDialog("FillAndStroke"); + shown = true; + } + } + } + } + sp_style_unref(query); + } + } + + if (!shown) { + GtkWidget *dialog = sp_gradient_vector_editor_new( gr ); + gtk_widget_show( dialog ); + } } } @@ -122,7 +159,7 @@ static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); if ( targetName == grad->getId() ) { - editGradientImpl( grad ); + editGradientImpl( desktop, grad ); break; } } @@ -130,31 +167,48 @@ static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) } } -static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) +void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData ) { if ( bounceTarget ) { SwatchesPanel* swp = bouncePanel; SPDesktop* desktop = swp ? swp->getDesktop() : 0; SPDocument *doc = desktop ? desktop->doc() : 0; - if (doc) { - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); - - Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient"); - repr->setAttribute("osb:paint", "solid"); - Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop"); - stop->setAttribute("offset", "0"); - stop->setAttribute("style", "stop-color:#000;stop-opacity:1;"); - repr->appendChild(stop); - Inkscape::GC::release(stop); - - SP_OBJECT_REPR( SP_DOCUMENT_DEFS(doc) )->addChild(repr, NULL); - - SPGradient * gr = static_cast<SPGradient *>(doc->getObjectByRepr(repr)); - - Inkscape::GC::release(repr); + gint index = GPOINTER_TO_INT(userData); + if ( doc && (index >= 0) && (static_cast<guint>(index) < popupItems.size()) ) { + Glib::ustring targetName = popupItems[index]; + const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( targetName == grad->getId() ) { + grad->setSwatch(); + sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT, + _("Add gradient stop")); + break; + } + } + } + } +} - editGradientImpl( gr ); +void SwatchesPanelHook::deleteGradient( GtkMenuItem */*menuitem*/, gpointer /*userData*/ ) +{ + if ( bounceTarget ) { + SwatchesPanel* swp = bouncePanel; + SPDesktop* desktop = swp ? swp->getDesktop() : 0; + SPDocument *doc = desktop ? desktop->doc() : 0; + if (doc) { + std::string targetName(bounceTarget->def.descr); + const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( targetName == grad->getId() ) { + grad->setSwatch(false); + sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT, + _("Delete")); + break; + } + } } } } @@ -177,7 +231,12 @@ static SwatchesPanel* findContainingPanel( GtkWidget *widget ) return swp; } -gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data) +static void removeit( GtkWidget *widget, gpointer data ) +{ + gtk_container_remove( GTK_CONTAINER(data), widget ); +} + +gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data ) { gboolean handled = FALSE; @@ -209,17 +268,13 @@ gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, g gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); popupExtras.push_back(child); - child = gtk_menu_item_new_with_label(_("Add")); + child = gtk_menu_item_new_with_label(_("Delete")); g_signal_connect( G_OBJECT(child), "activate", - G_CALLBACK(addNewGradient), + G_CALLBACK(SwatchesPanelHook::deleteGradient), user_data ); gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); popupExtras.push_back(child); - - child = gtk_menu_item_new_with_label(_("Delete")); - gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); - //popupExtras.push_back(child); gtk_widget_set_sensitive( child, FALSE ); child = gtk_menu_item_new_with_label(_("Edit...")); @@ -237,7 +292,12 @@ gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, g child = gtk_menu_item_new_with_label(_("Convert")); gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); //popupExtras.push_back(child); - gtk_widget_set_sensitive( child, FALSE ); + //gtk_widget_set_sensitive( child, FALSE ); + { + popupSubHolder = child; + popupSub = gtk_menu_new(); + gtk_menu_item_set_submenu( GTK_MENU_ITEM(child), popupSub ); + } gtk_widget_show_all(popupMenu); } @@ -251,7 +311,39 @@ gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, g bounceTarget = item; bouncePanel = swp; + popupItems.clear(); if ( popupMenu ) { + gtk_container_foreach(GTK_CONTAINER(popupSub), removeit, popupSub); + bool processed = false; + GtkWidget *wdgt = gtk_widget_get_ancestor(widget, SP_TYPE_DESKTOP_WIDGET); + if ( wdgt ) { + SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(wdgt); + if ( dtw && dtw->desktop ) { + // Pick up all gradients with vectors + const GSList *gradients = sp_document_get_resource_list(dtw->desktop->doc(), "gradient"); + gint index = 0; + for (const GSList *curr = gradients; curr; curr = curr->next) { + SPGradient* grad = SP_GRADIENT(curr->data); + if ( grad->hasStops() && !grad->isSwatch() ) { + //gl = g_slist_prepend(gl, curr->data); + processed = true; + GtkWidget *child = gtk_menu_item_new_with_label(grad->getId()); + gtk_menu_shell_append(GTK_MENU_SHELL(popupSub), child); + + popupItems.push_back(grad->getId()); + g_signal_connect( G_OBJECT(child), + "activate", + G_CALLBACK(SwatchesPanelHook::convertGradient), + GINT_TO_POINTER(index) ); + index++; + } + } + + gtk_widget_show_all(popupSub); + } + } + gtk_widget_set_sensitive( popupSubHolder, processed ); + gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time); handled = TRUE; } @@ -728,10 +820,11 @@ static void recalcSwatchContents(SPDocument* doc, } if ( !newList.empty() ) { + std::reverse(newList.begin(), newList.end()); for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it ) { SPGradient* grad = *it; - sp_gradient_ensure_vector( grad ); + grad->ensureVector(); SPGradientStop first = grad->vector.stops[0]; SPColor color = first.color; guint32 together = color.toRGBA32(first.opacity); @@ -809,21 +902,25 @@ void SwatchesPanel::handleDefsModified(SPDocument *document) std::map<ColorItem*, SPGradient*> tmpGrads; recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads); - int cap = std::min(docPalette->_colors.size(), tmpColors.size()); - for (int i = 0; i < cap; i++) { - ColorItem* newColor = tmpColors[i]; - ColorItem* oldColor = docPalette->_colors[i]; - if ( (newColor->def.getType() != oldColor->def.getType()) || - (newColor->def.getR() != oldColor->def.getR()) || - (newColor->def.getG() != oldColor->def.getG()) || - (newColor->def.getB() != oldColor->def.getB()) ) { - oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB()); - } - if (tmpGrads.find(newColor) != tmpGrads.end()) { - oldColor->setGradient(tmpGrads[newColor]); - } - if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) { - oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK); + if ( tmpColors.size() != docPalette->_colors.size() ) { + handleGradientsChange(document); + } else { + int cap = std::min(docPalette->_colors.size(), tmpColors.size()); + for (int i = 0; i < cap; i++) { + ColorItem* newColor = tmpColors[i]; + ColorItem* oldColor = docPalette->_colors[i]; + if ( (newColor->def.getType() != oldColor->def.getType()) || + (newColor->def.getR() != oldColor->def.getR()) || + (newColor->def.getG() != oldColor->def.getG()) || + (newColor->def.getB() != oldColor->def.getB()) ) { + oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB()); + } + if (tmpGrads.find(newColor) != tmpGrads.end()) { + oldColor->setGradient(tmpGrads[newColor]); + } + if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) { + oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK); + } } } } @@ -860,11 +957,12 @@ void SwatchesPanel::_updateFromSelection() if ( SP_IS_GRADIENT(server) ) { SPGradient* target = 0; SPGradient* grad = SP_GRADIENT(server); - if (grad->repr->attribute("osb:paint")) { + + if ( grad->isSwatch() ) { target = grad; } else if ( grad->ref ) { SPGradient *tmp = grad->ref->getObject(); - if ( tmp && tmp->repr->attribute("osb:paint") ) { + if ( tmp && tmp->isSwatch() ) { target = tmp; } } @@ -891,11 +989,11 @@ void SwatchesPanel::_updateFromSelection() if ( SP_IS_GRADIENT(server) ) { SPGradient* target = 0; SPGradient* grad = SP_GRADIENT(server); - if (grad->repr->attribute("osb:paint")) { + if ( grad->isSwatch() ) { target = grad; } else if ( grad->ref ) { SPGradient *tmp = grad->ref->getObject(); - if ( tmp && tmp->repr->attribute("osb:paint") ) { + if ( tmp && tmp->isSwatch() ) { target = tmp; } } diff --git a/src/ui/dialog/swatches.h b/src/ui/dialog/swatches.h index b18fd6cad..93bbe81d8 100644 --- a/src/ui/dialog/swatches.h +++ b/src/ui/dialog/swatches.h @@ -43,6 +43,8 @@ public: virtual int getSelectedIndex() {return _currentIndex;} // temporary protected: + static void handleGradientsChange(SPDocument *document); + virtual void _updateFromSelection(); virtual void _handleAction( int setId, int itemId ); virtual void _setDocument( SPDocument *document ); @@ -55,7 +57,6 @@ private: SwatchesPanel &operator=(SwatchesPanel const &); // no assign static void _trackDocument( SwatchesPanel *panel, SPDocument *document ); - static void handleGradientsChange(SPDocument *document); static void handleDefsModified(SPDocument *document); PreviewHolder* _holder; diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp index e761daf20..0e5805dda 100644 --- a/src/ui/tool/curve-drag-point.cpp +++ b/src/ui/tool/curve-drag-point.cpp @@ -164,19 +164,19 @@ Glib::ustring CurveDragPoint::_getTip(unsigned state) bool linear = first->front()->isDegenerate() && first.next()->back()->isDegenerate(); if (state_held_shift(state)) { return C_("Path segment tip", - "<b>Shift:</b> click to toggle segment selection"); + "<b>Shift</b>: click to toggle segment selection"); } if (state_held_control(state) && state_held_alt(state)) { return C_("Path segment tip", - "<b>Ctrl+Alt:</b> click to insert a node"); + "<b>Ctrl+Alt</b>: click to insert a node"); } if (linear) { return C_("Path segment tip", - "<b>Linear segment:</b> drag to convert to a Bezier segment, " + "<b>Linear segment</b>: drag to convert to a Bezier segment, " "doubleclick to insert node, click to select (more: Shift, Ctrl+Alt)"); } else { return C_("Path segment tip", - "<b>Bezier segment:</b> drag to shape the segment, doubleclick to insert node, " + "<b>Bezier segment</b>: drag to shape the segment, doubleclick to insert node, " "click to select (more: Shift, Ctrl+Alt)"); } } diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp index b5f420597..450ca96f0 100644 --- a/src/ui/tool/node-tool.cpp +++ b/src/ui/tool/node-tool.cpp @@ -534,11 +534,11 @@ void ink_node_tool_update_tip(InkNodeTool *nt, GdkEvent *event) if (state_held_shift(new_state)) { if (nt->_last_over) { nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, - C_("Node tool tip", "<b>Shift:</b> drag to add nodes to the selection, " + C_("Node tool tip", "<b>Shift</b>: drag to add nodes to the selection, " "click to toggle object selection")); } else { nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, - C_("Node tool tip", "<b>Shift:</b> drag to add nodes to the selection")); + C_("Node tool tip", "<b>Shift</b>: drag to add nodes to the selection")); } return; } diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index e9fa79fb3..886ddd1be 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -266,7 +266,7 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) Node *node_away = (this == &_parent->_front ? _parent->_prev() : _parent->_next()); if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { - Inkscape::Snapper::ConstraintLine cl(_parent->position(), + Inkscape::Snapper::SnapConstraint cl(_parent->position(), _parent->position() - node_away->position()); Inkscape::SnappedPoint p; p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), cl); @@ -357,21 +357,21 @@ Glib::ustring Handle::_getTip(unsigned state) } else { if (state_held_shift(state) && can_shift_rotate) { return C_("Path handle tip", - "<b>Shift+Alt:</b> preserve handle length and rotate both handles"); + "<b>Shift+Alt</b>: preserve handle length and rotate both handles"); } else { return C_("Path handle tip", - "<b>Alt:</b> preserve handle length while dragging"); + "<b>Alt</b>: preserve handle length while dragging"); } } } else { if (state_held_control(state)) { if (state_held_shift(state) && can_shift_rotate) { return format_tip(C_("Path handle tip", - "<b>Shift+Ctrl:</b> snap rotation angle to %g° increments and rotate both handles"), + "<b>Shift+Ctrl</b>: snap rotation angle to %g° increments and rotate both handles"), snap_increment_degrees()); } else { return format_tip(C_("Path handle tip", - "<b>Ctrl:</b> snap rotation angle to %g° increments, click to retract"), + "<b>Ctrl</b>: snap rotation angle to %g° increments, click to retract"), snap_increment_degrees()); } } else if (state_held_shift(state) && can_shift_rotate) { @@ -383,10 +383,10 @@ Glib::ustring Handle::_getTip(unsigned state) switch (_parent->type()) { case NODE_AUTO: return format_tip(C_("Path handle tip", - "<b>Auto node handle:</b> drag to convert to smooth node (%s)"), more); + "<b>Auto node handle</b>: drag to convert to smooth node (%s)"), more); default: return format_tip(C_("Path handle tip", - "<b>%s:</b> drag to shape the segment (%s)"), + "<b>%s</b>: drag to shape the segment (%s)"), handle_type_to_localized_string(_parent->type()), more); } } @@ -974,7 +974,7 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) // with Ctrl+Alt, constrain to handle lines // project the new position onto a handle line that is closer boost::optional<Geom::Point> front_point, back_point; - boost::optional<Inkscape::Snapper::ConstraintLine> line_front, line_back; + boost::optional<Inkscape::Snapper::SnapConstraint> line_front, line_back; if (_front.isDegenerate()) { if (_is_line_segment(this, _next())) front_point = _next()->position() - origin; @@ -988,9 +988,9 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) back_point = _back.relativePos(); } if (front_point) - line_front = Inkscape::Snapper::ConstraintLine(origin, *front_point); + line_front = Inkscape::Snapper::SnapConstraint(origin, *front_point); if (back_point) - line_back = Inkscape::Snapper::ConstraintLine(origin, *back_point); + line_back = Inkscape::Snapper::SnapConstraint(origin, *back_point); // TODO: combine the snap and non-snap branches by modifying snap.h / snap.cpp if (snap) { @@ -1029,8 +1029,8 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) // with Ctrl, constrain to axes // TODO combine the two branches if (snap) { - Inkscape::Snapper::ConstraintLine line_x(origin, Geom::Point(1, 0)); - Inkscape::Snapper::ConstraintLine line_y(origin, Geom::Point(0, 1)); + Inkscape::Snapper::SnapConstraint line_x(origin, Geom::Point(1, 0)); + Inkscape::Snapper::SnapConstraint line_y(origin, Geom::Point(0, 1)); fp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_x); bp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_y); } @@ -1039,9 +1039,7 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) fp = bp; } fp.getPoint(new_pos); - if (fp.getTarget() != SNAPTARGET_CONSTRAINT) { - _desktop->snapindicator->set_new_snaptarget(fp); - } + _desktop->snapindicator->set_new_snaptarget(fp); } else { Geom::Point origin = _last_drag_origin(); Geom::Point delta = new_pos - origin; @@ -1143,21 +1141,21 @@ Glib::ustring Node::_getTip(unsigned state) "to %f° increments"), snap_increment_degrees()); }*/ return C_("Path node tip", - "<b>Shift:</b> drag out a handle, click to toggle selection"); + "<b>Shift</b>: drag out a handle, click to toggle selection"); } - return C_("Path node tip", "<b>Shift:</b> click to toggle selection"); + return C_("Path node tip", "<b>Shift</b>: click to toggle selection"); } if (state_held_control(state)) { if (state_held_alt(state)) { - return C_("Path node tip", "<b>Ctrl+Alt:</b> move along handle lines, click to delete node"); + return C_("Path node tip", "<b>Ctrl+Alt</b>: move along handle lines, click to delete node"); } return C_("Path node tip", - "<b>Ctrl:</b> move along axes, click to change node type"); + "<b>Ctrl</b>: move along axes, click to change node type"); } if (state_held_alt(state)) { - return C_("Path node tip", "<b>Alt:</b> sculpt nodes"); + return C_("Path node tip", "<b>Alt</b>: sculpt nodes"); } // No modifiers: assemble tip from node type @@ -1165,13 +1163,13 @@ Glib::ustring Node::_getTip(unsigned state) if (_selection.transformHandlesEnabled() && selected()) { if (_selection.size() == 1) { return format_tip(C_("Path node tip", - "<b>%s:</b> drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); + "<b>%s</b>: drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); } return format_tip(C_("Path node tip", - "<b>%s:</b> drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); + "<b>%s</b>: drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); } return format_tip(C_("Path node tip", - "<b>%s:</b> drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); + "<b>%s</b>: drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); } Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index 1af848b96..6b8fb4c11 100644 --- a/src/ui/tool/transform-handle-set.cpp +++ b/src/ui/tool/transform-handle-set.cpp @@ -134,21 +134,21 @@ protected: if (state_held_control(state)) { if (state_held_shift(state)) { return C_("Transform handle tip", - "<b>Shift+Ctrl:</b> scale uniformly about the rotation center"); + "<b>Shift+Ctrl</b>: scale uniformly about the rotation center"); } return C_("Transform handle tip", "<b>Ctrl:</b> scale uniformly"); } if (state_held_shift(state)) { if (state_held_alt(state)) { return C_("Transform handle tip", - "<b>Shift+Alt:</b> scale using an integer ratio about the rotation center"); + "<b>Shift+Alt</b>: scale using an integer ratio about the rotation center"); } - return C_("Transform handle tip", "<b>Shift:</b> scale from the rotation center"); + return C_("Transform handle tip", "<b>Shift</b>: scale from the rotation center"); } if (state_held_alt(state)) { - return C_("Transform handle tip", "<b>Alt:</b> scale using an integer ratio"); + return C_("Transform handle tip", "<b>Alt</b>: scale using an integer ratio"); } - return C_("Transform handle tip", "<b>Scale handle:</b> drag to scale the selection"); + return C_("Transform handle tip", "<b>Scale handle</b>: drag to scale the selection"); } virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { @@ -308,16 +308,16 @@ protected: if (state_held_shift(state)) { if (state_held_control(state)) { return format_tip(C_("Transform handle tip", - "<b>Shift+Ctrl:</b> rotate around the opposite corner and snap " + "<b>Shift+Ctrl</b>: rotate around the opposite corner and snap " "angle to %f° increments"), snap_increment_degrees()); } - return C_("Transform handle tip", "<b>Shift:</b> rotate around the opposite corner"); + return C_("Transform handle tip", "<b>Shift</b>: rotate around the opposite corner"); } if (state_held_control(state)) { return format_tip(C_("Transform handle tip", - "<b>Ctrl:</b> snap angle to %f° increments"), snap_increment_degrees()); + "<b>Ctrl</b>: snap angle to %f° increments"), snap_increment_degrees()); } - return C_("Transform handle tip", "<b>Rotation handle:</b> drag to rotate " + return C_("Transform handle tip", "<b>Rotation handle</b>: drag to rotate " "the selection around the rotation center"); } @@ -416,17 +416,17 @@ protected: if (state_held_shift(state)) { if (state_held_control(state)) { return format_tip(C_("Transform handle tip", - "<b>Shift+Ctrl:</b> skew about the rotation center with snapping " + "<b>Shift+Ctrl</b>: skew about the rotation center with snapping " "to %f° increments"), snap_increment_degrees()); } - return C_("Transform handle tip", "<b>Shift:</b> skew about the rotation center"); + return C_("Transform handle tip", "<b>Shift</b>: skew about the rotation center"); } if (state_held_control(state)) { return format_tip(C_("Transform handle tip", - "<b>Ctrl:</b> snap skew angle to %f° increments"), snap_increment_degrees()); + "<b>Ctrl</b>: snap skew angle to %f° increments"), snap_increment_degrees()); } return C_("Transform handle tip", - "<b>Skew handle:</b> drag to skew (shear) selection about " + "<b>Skew handle</b>: drag to skew (shear) selection about " "the opposite handle"); } @@ -476,7 +476,7 @@ protected: virtual Glib::ustring _getTip(unsigned /*state*/) { return C_("Transform handle tip", - "<b>Rotation center:</b> drag to change the origin of transforms"); + "<b>Rotation center</b>: drag to change the origin of transforms"); } private: diff --git a/src/ui/uxmanager.cpp b/src/ui/uxmanager.cpp index 3c6f85b91..a3ccb3471 100644 --- a/src/ui/uxmanager.cpp +++ b/src/ui/uxmanager.cpp @@ -16,10 +16,12 @@ #include <algorithm> #include "uxmanager.h" +#include "desktop.h" #include "util/ege-tags.h" #include "widgets/toolbox.h" #include "widgets/desktop-widget.h" #include "preferences.h" +#include "gdkmm/screen.h" #ifdef GDK_WINDOWING_X11 #include <gdk/gdkx.h> @@ -38,23 +40,72 @@ static vector<SPDesktopWidget*> dtws; static map<SPDesktop*, vector<GtkWidget*> > trackedBoxes; +namespace { +// TODO unify this later: +static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view ) +{ + Glib::ustring prefPath; + + if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) { + prefPath = "/focus/"; + } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) { + prefPath = "/fullscreen/"; + } else { + prefPath = "/window/"; + } + + return prefPath; +} + +} namespace Inkscape { namespace UI { UXManager* instance = 0; +class UXManagerImpl : public UXManager +{ +public: + UXManagerImpl(); + virtual ~UXManagerImpl(); + + virtual void addTrack( SPDesktopWidget* dtw ); + virtual void delTrack( SPDesktopWidget* dtw ); + + virtual void connectToDesktop( vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ); + + virtual gint getDefaultTask( SPDesktop *desktop ); + virtual void setTask(SPDesktop* dt, gint val); + + virtual bool isFloatWindowProblem() const; + virtual bool isWidescreen() const; + +private: + bool _floatwindowIssues; + bool _widescreen; +}; + UXManager* UXManager::getInstance() { if (!instance) { - instance = new UXManager(); + instance = new UXManagerImpl(); } return instance; } -UXManager::UXManager() : - floatwindowIssues(false) +UXManager::UXManager() +{ +} + +UXManager::~UXManager() +{ +} + +UXManagerImpl::UXManagerImpl() : + _floatwindowIssues(false), + _widescreen(false) { ege::TagSet tags; tags.setLang("en"); @@ -68,24 +119,50 @@ UXManager::UXManager() : //if (g_ascii_strcasecmp( wmName, UNKOWN_WINDOW_MANAGER_NAME ) == 0) { if (g_ascii_strcasecmp( wmName, KDE_WINDOW_MANAGER_NAME ) == 0) { - floatwindowIssues = true; + _floatwindowIssues = true; } #elif defined(GDK_WINDOWING_WIN32) - floatwindowIssues = true; + _floatwindowIssues = true; #endif // GDK_WINDOWING_WIN32 + + + Glib::RefPtr<Gdk::Screen> defaultScreen = Gdk::Screen::get_default(); + if (defaultScreen) { + int width = defaultScreen->get_width(); + int height = defaultScreen->get_height(); + gdouble aspect = static_cast<gdouble>(width) / static_cast<gdouble>(height); + if (aspect > 1.65) { + _widescreen = true; + } + } } -UXManager::~UXManager() +UXManagerImpl::~UXManagerImpl() { } +bool UXManagerImpl::isFloatWindowProblem() const +{ + return _floatwindowIssues; +} + +bool UXManagerImpl::isWidescreen() const +{ + return _widescreen; +} -bool UXManager::isFloatWindowProblem() const +gint UXManagerImpl::getDefaultTask( SPDesktop *desktop ) { - return floatwindowIssues; + gint taskNum = isWidescreen() ? 2 : 0; + + Glib::ustring prefPath = getLayoutPrefPath( desktop ); + taskNum = Inkscape::Preferences::get()->getInt( prefPath + "task/taskset", taskNum ); + taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum; + + return taskNum; } -void UXManager::setTask(SPDesktop* dt, gint val) +void UXManagerImpl::setTask(SPDesktop* dt, gint val) { for (vector<SPDesktopWidget*>::iterator it = dtws.begin(); it != dtws.end(); ++it) { SPDesktopWidget* dtw = *it; @@ -93,6 +170,7 @@ void UXManager::setTask(SPDesktop* dt, gint val) gboolean notDone = Inkscape::Preferences::get()->getBool("/options/workarounds/dynamicnotdone", false); if (dtw->desktop == dt) { + int taskNum = val; switch (val) { default: case 0: @@ -101,15 +179,16 @@ void UXManager::setTask(SPDesktop* dt, gint val) if (notDone) { dtw->setToolboxPosition("AuxToolbar", GTK_POS_TOP); } - dtw->setToolboxPosition("SnapToolbar", GTK_POS_TOP); + dtw->setToolboxPosition("SnapToolbar", GTK_POS_RIGHT); + taskNum = val; // in case it was out of range break; case 1: - dtw->setToolboxPosition("ToolToolbar", GTK_POS_TOP); - dtw->setToolboxPosition("CommandsToolbar", GTK_POS_LEFT); + dtw->setToolboxPosition("ToolToolbar", GTK_POS_LEFT); + dtw->setToolboxPosition("CommandsToolbar", GTK_POS_TOP); if (notDone) { dtw->setToolboxPosition("AuxToolbar", GTK_POS_TOP); } - dtw->setToolboxPosition("SnapToolbar", GTK_POS_RIGHT); + dtw->setToolboxPosition("SnapToolbar", GTK_POS_TOP); break; case 2: dtw->setToolboxPosition("ToolToolbar", GTK_POS_LEFT); @@ -119,19 +198,21 @@ void UXManager::setTask(SPDesktop* dt, gint val) dtw->setToolboxPosition("AuxToolbar", GTK_POS_RIGHT); } } + Glib::ustring prefPath = getLayoutPrefPath( dtw->desktop ); + Inkscape::Preferences::get()->setInt( prefPath + "task/taskset", taskNum ); } } } -void UXManager::addTrack( SPDesktopWidget* dtw ) +void UXManagerImpl::addTrack( SPDesktopWidget* dtw ) { if (std::find(dtws.begin(), dtws.end(), dtw) == dtws.end()) { dtws.push_back(dtw); } } -void UXManager::delTrack( SPDesktopWidget* dtw ) +void UXManagerImpl::delTrack( SPDesktopWidget* dtw ) { vector<SPDesktopWidget*>::iterator iter = std::find(dtws.begin(), dtws.end(), dtw); if (iter != dtws.end()) { @@ -139,9 +220,9 @@ void UXManager::delTrack( SPDesktopWidget* dtw ) } } -void UXManager::connectToDesktop( vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ) +void UXManagerImpl::connectToDesktop( vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ) { -//static map<SPDesktop*, vector<GtkWidget*> > trackedBoxes; + //static map<SPDesktop*, vector<GtkWidget*> > trackedBoxes; for (vector<GtkWidget*>::const_iterator it = toolboxes.begin(); it != toolboxes.end(); ++it ) { GtkWidget* toolbox = *it; @@ -156,6 +237,11 @@ void UXManager::connectToDesktop( vector<GtkWidget *> const & toolboxes, SPDeskt if (std::find(desktops.begin(), desktops.end(), desktop) == desktops.end()) { desktops.push_back(desktop); } + + gint taskNum = getDefaultTask( desktop ); + + // note: this will change once more options are in the task set support: + Inkscape::UI::UXManager::getInstance()->setTask( desktop, taskNum ); } diff --git a/src/ui/uxmanager.h b/src/ui/uxmanager.h index aecda2b5e..5fef08f11 100644 --- a/src/ui/uxmanager.h +++ b/src/ui/uxmanager.h @@ -34,19 +34,23 @@ public: static UXManager* getInstance(); virtual ~UXManager(); - void addTrack( SPDesktopWidget* dtw ); - void delTrack( SPDesktopWidget* dtw ); + virtual void addTrack( SPDesktopWidget* dtw ) = 0; + virtual void delTrack( SPDesktopWidget* dtw ) = 0; - void connectToDesktop( std::vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ); + virtual void connectToDesktop( std::vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ) = 0; - void setTask(SPDesktop* dt, gint val); + virtual gint getDefaultTask( SPDesktop *desktop ) = 0; + virtual void setTask( SPDesktop* dt, gint val ) = 0; - bool isFloatWindowProblem() const; + virtual bool isFloatWindowProblem() const = 0; + virtual bool isWidescreen() const = 0; -private: +protected: UXManager(); - bool floatwindowIssues; +private: + UXManager( UXManager const & ); + UXManager & operator=( UXManager const & ); }; } // namespace UI diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp index e604a24ec..26763cc77 100644 --- a/src/ui/widget/page-sizer.cpp +++ b/src/ui/widget/page-sizer.cpp @@ -18,25 +18,26 @@ # include <config.h> #endif -#include <string.h> -#include <vector> -#include <string> - #include <cmath> #include <gtkmm.h> -#include "ui/widget/button.h" - -#include "ui/widget/scalar-unit.h" +#include <string> +#include <string.h> +#include <vector> -#include "helper/units.h" -#include "inkscape.h" -#include "verbs.h" #include "desktop-handles.h" #include "document.h" #include "desktop.h" -#include "page-sizer.h" #include "helper/action.h" +#include "helper/units.h" +#include "inkscape.h" +#include "page-sizer.h" +#include "sp-namedview.h" #include "sp-root.h" +#include "ui/widget/button.h" +#include "ui/widget/scalar-unit.h" +#include "verbs.h" +#include "xml/node.h" +#include "xml/repr.h" using std::pair; @@ -310,9 +311,17 @@ PageSizer::PageSizer(Registry & _wr) _customDimTable.attach(_dimensionHeight, 0,1, 1,2); _customDimTable.attach(_fitPageMarginExpander, 0,2, 2,3); + _dimTabOrderGList = NULL; + _dimTabOrderGList = g_list_append(_dimTabOrderGList, _dimensionWidth.gobj()); + _dimTabOrderGList = g_list_append(_dimTabOrderGList, _dimensionHeight.gobj()); + _dimTabOrderGList = g_list_append(_dimTabOrderGList, _dimensionUnits.gobj()); + _dimTabOrderGList = g_list_append(_dimTabOrderGList, _fitPageMarginExpander.gobj()); + Glib::ListHandle<Widget *> dimFocusChain(_dimTabOrderGList, Glib::OWNERSHIP_NONE); + _customDimTable.set_focus_chain(dimFocusChain); + //## Set up fit page expander - _fitPageMarginExpander.set_label(_("Resi_ze page to content...")); _fitPageMarginExpander.set_use_underline(); + _fitPageMarginExpander.set_label(_("Resi_ze page to content...")); _fitPageMarginExpander.add(_marginTable); //## Set up margin settings @@ -320,11 +329,11 @@ PageSizer::PageSizer(Registry & _wr) _marginTable.set_border_width(4); _marginTable.set_row_spacings(4); _marginTable.set_col_spacings(4); - _marginTable.attach(_fitPageButtonAlign, 0,2, 0,1); - _marginTable.attach(_marginTopAlign, 0,2, 1,2); - _marginTable.attach(_marginLeftAlign, 0,1, 2,3); - _marginTable.attach(_marginRightAlign, 1,2, 2,3); - _marginTable.attach(_marginBottomAlign, 0,2, 3,4); + _marginTable.attach(_marginTopAlign, 0,2, 0,1); + _marginTable.attach(_marginLeftAlign, 0,1, 1,2); + _marginTable.attach(_marginRightAlign, 1,2, 1,2); + _marginTable.attach(_marginBottomAlign, 0,2, 2,3); + _marginTable.attach(_fitPageButtonAlign, 0,2, 3,4); _marginTopAlign.set(0.5, 0.5, 0.0, 1.0); _marginTopAlign.add(_marginTop); @@ -349,6 +358,7 @@ PageSizer::PageSizer(Registry & _wr) */ PageSizer::~PageSizer() { + g_list_free(_dimTabOrderGList); } @@ -437,6 +447,28 @@ PageSizer::setDim (double w, double h, bool changeList) _called = false; } +/** + * Updates the scalar widgets for the fit margins. (Just changes the value + * of the ui widgets to match the xml). + */ +void +PageSizer::updateFitMarginsUI(Inkscape::XML::Node *nv_repr) +{ + double value = 0.0; + if (sp_repr_get_double(nv_repr, "fit-margin-top", &value)) { + _marginTop.setValue(value); + } + if (sp_repr_get_double(nv_repr, "fit-margin-left", &value)) { + _marginLeft.setValue(value); + } + if (sp_repr_get_double(nv_repr, "fit-margin-right", &value)) { + _marginRight.setValue(value); + } + if (sp_repr_get_double(nv_repr, "fit-margin-bottom", &value)) { + _marginBottom.setValue(value); + } +} + /** * Returns an iterator pointing to a row in paperSizeListStore which @@ -492,11 +524,23 @@ PageSizer::fire_fit_canvas_to_selection_or_drawing() if (!dt) { return; } + SPDocument *doc; + SPNamedView *nv; + Inkscape::XML::Node *nv_repr; + if ((doc = sp_desktop_document(SP_ACTIVE_DESKTOP)) + && (nv = sp_document_namedview(doc, 0)) + && (nv_repr = SP_OBJECT_REPR(nv))) { + sp_repr_set_svg_double(nv_repr, "fit-margin-top", _marginTop.getValue()); + sp_repr_set_svg_double(nv_repr, "fit-margin-left", _marginLeft.getValue()); + sp_repr_set_svg_double(nv_repr, "fit-margin-right", _marginRight.getValue()); + sp_repr_set_svg_double(nv_repr, "fit-margin-bottom", _marginBottom.getValue()); + } Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING ); if (verb) { SPAction *action = verb->get_action(dt); - if (action) + if (action) { sp_action_perform(action, NULL); + } } } diff --git a/src/ui/widget/page-sizer.h b/src/ui/widget/page-sizer.h index 718eb95b5..ba6c8dd13 100644 --- a/src/ui/widget/page-sizer.h +++ b/src/ui/widget/page-sizer.h @@ -14,10 +14,11 @@ #include <gtkmm.h> #include <sigc++/sigc++.h> + +#include "helper/units.h" #include "ui/widget/registry.h" #include "ui/widget/registered-widget.h" -#include "helper/units.h" - +#include "xml/node.h" namespace Inkscape { namespace UI { @@ -141,6 +142,12 @@ public: * true, then reset the paper size list to the closest match */ void setDim (double w, double h, bool changeList=true); + + /** + * Updates the scalar widgets for the fit margins. (Just changes the value + * of the ui widgets to match the xml). + */ + void updateFitMarginsUI(Inkscape::XML::Node *nv_repr); protected: @@ -196,6 +203,7 @@ protected: RegisteredUnitMenu _dimensionUnits; RegisteredScalarUnit _dimensionWidth; RegisteredScalarUnit _dimensionHeight; + GList * _dimTabOrderGList; //### Fit Page options Gtk::Expander _fitPageMarginExpander; diff --git a/src/verbs.cpp b/src/verbs.cpp index 24c17aad8..c2af399c5 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1682,9 +1682,9 @@ ZoomVerb::perform(SPAction *action, void *data, void */*pdata*/) case SP_VERB_VIEW_MODE_OUTLINE: dt->setDisplayModeOutline(); break; - case SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW: - dt->setDisplayModePrintColorsPreview(); - break; +// case SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW: +// dt->setDisplayModePrintColorsPreview(); +// break; case SP_VERB_VIEW_MODE_TOGGLE: dt->displayModeToggle(); break; @@ -2569,8 +2569,8 @@ Verb *Verb::_base_verbs[] = { N_("Switch to normal display without filters"), NULL), new ZoomVerb(SP_VERB_VIEW_MODE_OUTLINE, "ViewModeOutline", N_("_Outline"), N_("Switch to outline (wireframe) display mode"), NULL), - new ZoomVerb(SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, "ViewModePrintColorsPreview", N_("_Print Colors Preview"), - N_("Switch to print colors preview mode"), NULL), +// new ZoomVerb(SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, "ViewModePrintColorsPreview", N_("_Print Colors Preview"), +// N_("Switch to print colors preview mode"), NULL), new ZoomVerb(SP_VERB_VIEW_MODE_TOGGLE, "ViewModeToggle", N_("_Toggle"), N_("Toggle between normal and outline display modes"), NULL), diff --git a/src/verbs.h b/src/verbs.h index 36bb9f9d9..7c04ba3f6 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -214,7 +214,7 @@ enum { SP_VERB_VIEW_MODE_NORMAL, SP_VERB_VIEW_MODE_NO_FILTERS, SP_VERB_VIEW_MODE_OUTLINE, - SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, +// SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, SP_VERB_VIEW_MODE_TOGGLE, SP_VERB_VIEW_CMS_TOGGLE, SP_VERB_VIEW_ICON_PREVIEW, diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index ab440595f..36047e81b 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -1393,8 +1393,6 @@ SPViewWidget *sp_desktop_widget_new( SPNamedView *namedview ) { SPDesktopWidget* dtw = SPDesktopWidget::createInstance(namedview); - UXManager::getInstance()->addTrack(dtw); - return SP_VIEW_WIDGET(dtw); } @@ -1440,10 +1438,12 @@ SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview) toolboxes.push_back(dtw->aux_toolbox); toolboxes.push_back(dtw->commands_toolbox); toolboxes.push_back(dtw->snap_toolbox); - UXManager::getInstance()->connectToDesktop( toolboxes, dtw->desktop ); dtw->panels->setDesktop( dtw->desktop ); + UXManager::getInstance()->addTrack(dtw); + UXManager::getInstance()->connectToDesktop( toolboxes, dtw->desktop ); + return dtw; } diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp index 15d8b6cc2..a0e343b58 100644 --- a/src/widgets/fill-style.cpp +++ b/src/widgets/fill-style.cpp @@ -268,15 +268,15 @@ void FillNStroke::performUpdate() psel->setGradientLinear( vector ); SPLinearGradient *lg = SP_LINEARGRADIENT(server); - psel->setGradientProperties( SP_GRADIENT_UNITS(lg), - SP_GRADIENT_SPREAD(lg) ); + psel->setGradientProperties( lg->getUnits(), + lg->getSpread() ); } else if (SP_IS_RADIALGRADIENT(server)) { SPGradient *vector = SP_GRADIENT(server)->getVector(); psel->setGradientRadial( vector ); SPRadialGradient *rg = SP_RADIALGRADIENT(server); - psel->setGradientProperties( SP_GRADIENT_UNITS(rg), - SP_GRADIENT_SPREAD(rg) ); + psel->setGradientProperties( rg->getUnits(), + rg->getSpread() ); } else if (SP_IS_PATTERN(server)) { SPPattern *pat = pattern_getroot(SP_PATTERN(server)); psel->updatePatternList( pat ); @@ -523,6 +523,7 @@ void FillNStroke::updateFromPaint() SPGradientType const gradient_type = ( psel->mode != SPPaintSelector::MODE_GRADIENT_RADIAL ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL ); + bool createSwatch = (psel->mode == SPPaintSelector::MODE_SWATCH); SPCSSAttr *css = 0; if (kind == FILL) { @@ -537,27 +538,34 @@ void FillNStroke::updateFromPaint() SPStyle *query = sp_style_new(desktop->doc()); int result = objects_query_fillstroke(const_cast<GSList *>(items), query, kind == FILL); - SPIPaint &targPaint = (kind == FILL) ? query->fill : query->stroke; - guint32 common_rgb = 0; if (result == QUERY_STYLE_MULTIPLE_SAME) { + SPIPaint &targPaint = (kind == FILL) ? query->fill : query->stroke; + SPColor common; if (!targPaint.isColor()) { - common_rgb = sp_desktop_get_color(desktop, kind == FILL); + common = sp_desktop_get_color(desktop, kind == FILL); } else { - common_rgb = targPaint.value.color.toRGBA32( 0xff ); + common = targPaint.value.color; + } + vector = sp_document_default_gradient_vector( document, common, createSwatch ); + if ( vector && createSwatch ) { + vector->setSwatch(); } - vector = sp_document_default_gradient_vector(document, common_rgb); } sp_style_unref(query); for (GSList const *i = items; i != NULL; i = i->next) { //FIXME: see above if (kind == FILL) { - sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style"); + sp_repr_css_change_recursive(reinterpret_cast<SPObject*>(i->data)->repr, css, "style"); } if (!vector) { + SPGradient *gr = sp_gradient_vector_for_object( document, desktop, reinterpret_cast<SPObject*>(i->data), kind == FILL, createSwatch ); + if ( gr && createSwatch ) { + gr->setSwatch(); + } sp_item_set_gradient(SP_ITEM(i->data), - sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), kind == FILL), + gr, gradient_type, kind == FILL); } else { sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, kind == FILL); @@ -570,7 +578,7 @@ void FillNStroke::updateFromPaint() for (GSList const *i = items; i != NULL; i = i->next) { //FIXME: see above if (kind == FILL) { - sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style"); + sp_repr_css_change_recursive(reinterpret_cast<SPObject*>(i->data)->repr, css, "style"); } SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, kind == FILL); @@ -600,7 +608,7 @@ void FillNStroke::updateFromPaint() */ } else { - Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern); + Inkscape::XML::Node *patrepr = pattern->repr; SPCSSAttr *css = sp_repr_css_attr_new(); gchar *urltext = g_strdup_printf("url(#%s)", patrepr->attribute("id")); sp_repr_css_set_property(css, (kind == FILL) ? "fill" : "stroke", urltext); @@ -614,17 +622,17 @@ void FillNStroke::updateFromPaint() // objects who already have the same root pattern but through a different href // chain. FIXME: move this to a sp_item_set_pattern for (GSList const *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *selrepr = SP_OBJECT_REPR(i->data); + Inkscape::XML::Node *selrepr = reinterpret_cast<SPObject*>(i->data)->repr; if ( (kind == STROKE) && !selrepr) { continue; } - SPObject *selobj = SP_OBJECT(i->data); + SPObject *selobj = reinterpret_cast<SPObject*>(i->data); - SPStyle *style = SP_OBJECT_STYLE(selobj); + SPStyle *style = selobj->style; if (style && ((kind == FILL) ? style->fill : style->stroke).isPaintserver()) { - SPObject *server = (kind == FILL) ? - SP_OBJECT_STYLE_FILL_SERVER(selobj) : - SP_OBJECT_STYLE_STROKE_SERVER(selobj); + SPPaintServer *server = (kind == FILL) ? + selobj->style->getFillPaintServer() : + selobj->style->getStrokePaintServer(); if (SP_IS_PATTERN(server) && pattern_getroot(SP_PATTERN(server)) == pattern) // only if this object's pattern is not rooted in our selected pattern, apply continue; diff --git a/src/widgets/gradient-selector.cpp b/src/widgets/gradient-selector.cpp index bc2413730..5b663c493 100644 --- a/src/widgets/gradient-selector.cpp +++ b/src/widgets/gradient-selector.cpp @@ -117,10 +117,10 @@ sp_gradient_selector_class_init (SPGradientSelectorClass *klass) object_class->destroy = sp_gradient_selector_destroy; } -static void -sp_gradient_selector_init (SPGradientSelector *sel) +static void sp_gradient_selector_init(SPGradientSelector *sel) { - GtkWidget *hb, *m, *mi; + sel->safelyInit = true; + new (&sel->nonsolid) std::vector<GtkWidget*>(); sel->mode = SPGradientSelector::MODE_LINEAR; @@ -134,39 +134,44 @@ sp_gradient_selector_init (SPGradientSelector *sel) g_signal_connect (G_OBJECT (sel->vectors), "vector_set", G_CALLBACK (sp_gradient_selector_vector_set), sel); /* Create box for buttons */ - hb = gtk_hbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (sel), hb, FALSE, FALSE, 0); + GtkWidget *hb = gtk_hbox_new( FALSE, 0 ); + sel->nonsolid.push_back(hb); + gtk_box_pack_start( GTK_BOX(sel), hb, FALSE, FALSE, 0 ); GtkTooltips *ttips = gtk_tooltips_new (); sel->add = gtk_button_new_with_label (_("Duplicate")); + sel->nonsolid.push_back(sel->add); gtk_box_pack_start (GTK_BOX (hb), sel->add, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (sel->add), "clicked", G_CALLBACK (sp_gradient_selector_add_vector_clicked), sel); gtk_widget_set_sensitive (sel->add, FALSE); sel->edit = gtk_button_new_with_label (_("Edit...")); + sel->nonsolid.push_back(sel->edit); gtk_box_pack_start (GTK_BOX (hb), sel->edit, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (sel->edit), "clicked", G_CALLBACK (sp_gradient_selector_edit_vector_clicked), sel); gtk_widget_set_sensitive (sel->edit, FALSE); - gtk_widget_show_all (hb); + gtk_widget_show_all(hb); /* Spread selector */ - hb = gtk_hbox_new (FALSE, 0); - gtk_widget_show (hb); - gtk_box_pack_start (GTK_BOX (sel), hb, FALSE, FALSE, 0); - - sel->spread = gtk_option_menu_new (); - gtk_widget_show (sel->spread); - gtk_box_pack_end (GTK_BOX (hb), sel->spread, FALSE, FALSE, 0); - gtk_tooltips_set_tip (ttips, sel->spread, + hb = gtk_hbox_new( FALSE, 0 ); + sel->nonsolid.push_back(hb); + gtk_widget_show(hb); + gtk_box_pack_start( GTK_BOX(sel), hb, FALSE, FALSE, 0 ); + + sel->spread = gtk_option_menu_new(); + sel->nonsolid.push_back(sel->spread); + gtk_widget_show(sel->spread); + gtk_box_pack_end( GTK_BOX(hb), sel->spread, FALSE, FALSE, 0 ); + gtk_tooltips_set_tip( ttips, sel->spread, // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/pservers.html#LinearGradientSpreadMethodAttribute _("Whether to fill with flat color beyond the ends of the gradient vector " "(spreadMethod=\"pad\"), or repeat the gradient in the same direction " "(spreadMethod=\"repeat\"), or repeat the gradient in alternating opposite " "directions (spreadMethod=\"reflect\")"), NULL); - m = gtk_menu_new (); - mi = gtk_menu_item_new_with_label (_("none")); + GtkWidget *m = gtk_menu_new(); + GtkWidget *mi = gtk_menu_item_new_with_label(_("none")); gtk_menu_append (GTK_MENU (m), mi); g_object_set_data (G_OBJECT (mi), "gradientSpread", GUINT_TO_POINTER (SP_GRADIENT_SPREAD_PAD)); g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (sp_gradient_selector_spread_activate), sel); @@ -180,22 +185,27 @@ sp_gradient_selector_init (SPGradientSelector *sel) gtk_menu_append (GTK_MENU (m), mi); gtk_widget_show_all (m); - gtk_option_menu_set_menu (GTK_OPTION_MENU (sel->spread), m); + gtk_option_menu_set_menu( GTK_OPTION_MENU(sel->spread), m ); - sel->spreadLbl = gtk_label_new (_("Repeat:")); - gtk_widget_show(sel->spreadLbl); - gtk_box_pack_end(GTK_BOX(hb), sel->spreadLbl, FALSE, FALSE, 4); + sel->spreadLbl = gtk_label_new( _("Repeat:") ); + sel->nonsolid.push_back(sel->spreadLbl); + gtk_widget_show( sel->spreadLbl ); + gtk_box_pack_end( GTK_BOX(hb), sel->spreadLbl, FALSE, FALSE, 4 ); } -static void -sp_gradient_selector_destroy (GtkObject *object) +static void sp_gradient_selector_destroy(GtkObject *object) { - SPGradientSelector *sel; + SPGradientSelector *sel = SP_GRADIENT_SELECTOR( object ); - sel = SP_GRADIENT_SELECTOR (object); + if ( sel->safelyInit ) { + sel->safelyInit = false; + using std::vector; + sel->nonsolid.~vector<GtkWidget*>(); + } - if (((GtkObjectClass *) (parent_class))->destroy) + if (((GtkObjectClass *) (parent_class))->destroy) { (* ((GtkObjectClass *) (parent_class))->destroy) (object); + } } GtkWidget * @@ -213,19 +223,9 @@ void SPGradientSelector::setMode(SelectorMode mode) if (mode != this->mode) { this->mode = mode; if (mode == MODE_SWATCH) { - if (spread) { - GtkWidget *parent = gtk_widget_get_parent(spread); - if (parent) { - gtk_container_remove(GTK_CONTAINER(parent), spread); - spread = 0; - } - } - if (spreadLbl) { - GtkWidget *parent = gtk_widget_get_parent(spreadLbl); - if (parent) { - gtk_container_remove(GTK_CONTAINER(parent), spreadLbl); - spreadLbl = 0; - } + for (std::vector<GtkWidget*>::iterator it = nonsolid.begin(); it != nonsolid.end(); ++it) + { + gtk_widget_hide(*it); } SPGradientVectorSelector* vs = SP_GRADIENT_VECTOR_SELECTOR(vectors); @@ -261,18 +261,40 @@ void SPGradientSelector::setVector(SPDocument *doc, SPGradient *vector) g_return_if_fail(!vector || SP_IS_GRADIENT(vector)); g_return_if_fail(!vector || (SP_OBJECT_DOCUMENT(vector) == doc)); - if (vector && !SP_GRADIENT_HAS_STOPS(vector)) { + if (vector && !vector->hasStops()) { return; } sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(vectors), doc, vector); if (vector) { - gtk_widget_set_sensitive(edit, TRUE); - gtk_widget_set_sensitive(add, TRUE); + if ( (mode == MODE_SWATCH) && vector->isSwatch() ) { + if ( vector->isSolid() ) { + for (std::vector<GtkWidget*>::iterator it = nonsolid.begin(); it != nonsolid.end(); ++it) + { + gtk_widget_hide(*it); + } + } else { + for (std::vector<GtkWidget*>::iterator it = nonsolid.begin(); it != nonsolid.end(); ++it) + { + gtk_widget_show_all(*it); + } + } + } + + if (edit) { + gtk_widget_set_sensitive(edit, TRUE); + } + if (add) { + gtk_widget_set_sensitive(add, TRUE); + } } else { - gtk_widget_set_sensitive(edit, FALSE); - gtk_widget_set_sensitive(add, (doc != NULL)); + if (edit) { + gtk_widget_set_sensitive(edit, FALSE); + } + if (add) { + gtk_widget_set_sensitive(add, (doc != NULL)); + } } } diff --git a/src/widgets/gradient-selector.h b/src/widgets/gradient-selector.h index 25f561a6e..860804ec6 100644 --- a/src/widgets/gradient-selector.h +++ b/src/widgets/gradient-selector.h @@ -17,6 +17,7 @@ #include <glib.h> #include <gtk/gtkvbox.h> +#include <vector> #include "sp-gradient.h" #include "sp-gradient-spread.h" #include "sp-gradient-units.h" @@ -59,6 +60,9 @@ struct SPGradientSelector { GtkWidget *spread; GtkWidget *spreadLbl; + bool safelyInit; + std::vector<GtkWidget*> nonsolid; + void setMode(SelectorMode mode); void setUnits(SPGradientUnits units); void setSpread(SPGradientSpread spread); diff --git a/src/widgets/gradient-toolbar.cpp b/src/widgets/gradient-toolbar.cpp index 077e038e7..ce5f5fb8f 100644 --- a/src/widgets/gradient-toolbar.cpp +++ b/src/widgets/gradient-toolbar.cpp @@ -73,18 +73,17 @@ static void gr_toggle_fillstroke (GtkWidget *button, gpointer data) { } } -void -gr_apply_gradient_to_item (SPItem *item, SPGradient *gr, SPGradientType new_type, guint new_fill, bool do_fill, bool do_stroke) +void gr_apply_gradient_to_item( SPItem *item, SPGradient *gr, SPGradientType new_type, guint new_fill, bool do_fill, bool do_stroke ) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (do_fill) { if (style && (style->fill.isPaintserver()) && - SP_IS_GRADIENT (SP_OBJECT_STYLE_FILL_SERVER (item))) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SP_IS_GRADIENT( item->style->getFillPaintServer() )) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_LINEARGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, true); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, true); } } else if (new_fill) { @@ -94,11 +93,11 @@ gr_apply_gradient_to_item (SPItem *item, SPGradient *gr, SPGradientType new_type if (do_stroke) { if (style && (style->stroke.isPaintserver()) && - SP_IS_GRADIENT (SP_OBJECT_STYLE_STROKE_SERVER (item))) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SP_IS_GRADIENT( item->style->getStrokePaintServer() )) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_LINEARGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, false); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, false); } } else if (!new_fill) { @@ -175,7 +174,7 @@ GtkWidget *gr_vector_list(SPDesktop *desktop, bool selection_empty, SPGradient * const GSList *gradients = sp_document_get_resource_list (document, "gradient"); for (const GSList *i = gradients; i != NULL; i = i->next) { SPGradient *grad = SP_GRADIENT(i->data); - if (SP_GRADIENT_HAS_STOPS(grad) && !grad->isSolid()) { + if ( grad->hasStops() && !grad->isSolid() ) { gl = g_slist_prepend (gl, i->data); } } @@ -238,7 +237,7 @@ GtkWidget *gr_vector_list(SPDesktop *desktop, bool selection_empty, SPGradient * GtkWidget *hb = gtk_hbox_new (FALSE, 4); GtkWidget *l = gtk_label_new (""); - gchar *label = gr_prepare_label (SP_OBJECT(gradient)); + gchar *label = gr_prepare_label(gradient); gtk_label_set_markup (GTK_LABEL(l), label); g_free (label); gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); @@ -310,13 +309,13 @@ void gr_read_selection( Inkscape::Selection *selection, // If no selected dragger, read desktop selection for (GSList const* i = selection->itemList(); i; i = i->next) { SPItem *item = SP_ITEM(i->data); - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_GRADIENT(server)) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { SPGradient *gradient = SP_GRADIENT(server)->getVector(); - SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server)); + SPGradientSpread spread = SP_GRADIENT(server)->fetchSpread(); if (gradient && gradient->isSolid()) { gradient = 0; @@ -339,14 +338,13 @@ void gr_read_selection( Inkscape::Selection *selection, } } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_GRADIENT(server)) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { SPGradient *gradient = SP_GRADIENT(server)->getVector(); - SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server)); + SPGradientSpread spread = SP_GRADIENT(server)->fetchSpread(); if (gradient && gradient->isSolid()) { gradient = 0; - } if (gradient && (gradient != gr_selected)) { diff --git a/src/widgets/gradient-vector.cpp b/src/widgets/gradient-vector.cpp index 7bfe27310..c49d9e06f 100644 --- a/src/widgets/gradient-vector.cpp +++ b/src/widgets/gradient-vector.cpp @@ -174,6 +174,10 @@ GtkWidget *sp_gradient_vector_selector_new(SPDocument *doc, SPGradient *gr) void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr) { +// g_message("sp_gradient_vector_selector_set_gradient(%p, %p, %p) [%s] %d %d", gvs, doc, gr, +// (gr ? gr->getId():"N/A"), +// (gr ? gr->isSwatch() : -1), +// (gr ? gr->isSolid() : -1)); static gboolean suppress = FALSE; g_return_if_fail(gvs != NULL); @@ -181,7 +185,7 @@ void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPD g_return_if_fail(!gr || (doc != NULL)); g_return_if_fail(!gr || SP_IS_GRADIENT(gr)); g_return_if_fail(!gr || (SP_OBJECT_DOCUMENT(gr) == doc)); - g_return_if_fail(!gr || SP_GRADIENT_HAS_STOPS(gr)); + g_return_if_fail(!gr || gr->hasStops()); if (doc != gvs->doc) { /* Disconnect signals */ @@ -252,7 +256,7 @@ static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs) const GSList *gradients = sp_document_get_resource_list(SP_OBJECT_DOCUMENT(gvs->gr), "gradient"); for (const GSList *curr = gradients; curr; curr = curr->next) { SPGradient* grad = SP_GRADIENT(curr->data); - if (SP_GRADIENT_HAS_STOPS(grad) && (grad->isSwatch() == gvs->swatched)) { + if ( grad->hasStops() && (grad->isSwatch() == gvs->swatched) ) { gl = g_slist_prepend(gl, curr->data); } } @@ -337,6 +341,7 @@ static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector * /* Hmmm - probably we can just re-set it as menuitem data (Lauris) */ //g_print("SPGradientVectorSelector: gradient %s activated\n", SP_OBJECT_ID(gr)); + //g_message("Setting to gradient %p swatch:%d solid:%d", gr, gr->isSwatch(), gr->isSolid()); norm = sp_gradient_ensure_vector_normalized(gr); if (norm != gr) { @@ -535,7 +540,7 @@ static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_ GtkWidget *m = gtk_menu_new(); gtk_widget_show(m); GSList *sl = NULL; - if (gradient->has_stops) { + if ( gradient->hasStops() ) { for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { if (SP_IS_STOP(ochild)) { sl = g_slist_append(sl, ochild); @@ -1017,7 +1022,7 @@ static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradien if (gradient) { gtk_widget_set_sensitive(widget, TRUE); - sp_gradient_ensure_vector(gradient); + gradient->ensureVector(); GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(widget), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); @@ -1140,7 +1145,7 @@ static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *o sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } - sp_gradient_ensure_vector(ngr); + ngr->ensureVector(); GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(object), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); @@ -1175,7 +1180,7 @@ static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *o sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } - sp_gradient_ensure_vector(ngr); + ngr->ensureVector(); /* Set start parameters */ /* We rely on normalized vector, i.e. stops HAVE to exist */ diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp index d3092669a..cf6a04a3c 100644 --- a/src/widgets/paint-selector.cpp +++ b/src/widgets/paint-selector.cpp @@ -538,8 +538,8 @@ void SPPaintSelector::pushAttrsToGradient( SPGradient *gr ) const SPGradientUnits units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX; SPGradientSpread spread = SP_GRADIENT_SPREAD_PAD; getGradientProperties( units, spread ); - sp_gradient_set_units(gr, units); - sp_gradient_set_spread(gr, spread); + gr->setUnits(units); + gr->setSpread(spread); SP_OBJECT(gr)->updateRepr(); } @@ -1082,11 +1082,10 @@ static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSele SwatchSelector *swatchsel = 0; if (psel->mode == SPPaintSelector::MODE_SWATCH){ - /* Already have pattern menu */ swatchsel = static_cast<SwatchSelector*>(g_object_get_data(G_OBJECT(psel->selector), "swatch-selector")); } else { sp_paint_selector_clear_frame(psel); - /* Create new gradient selector */ + // Create new gradient selector SwatchSelector *swatchsel = new SwatchSelector(); swatchsel->show(); @@ -1102,6 +1101,7 @@ static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSele gtk_frame_set_label(GTK_FRAME(psel->frame), _("Swatch fill")); } + #ifdef SP_PS_VERBOSE g_print("Swatch req\n"); #endif diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index a249786ff..6c5113084 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -519,6 +519,7 @@ void ColorNotebook::_rgbaEntryChanged(GtkEntry* entry) } } +// TODO pass in param so as to avoid the need for SP_ACTIVE_DOCUMENT void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); @@ -539,7 +540,7 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) gtk_widget_set_sensitive (_box_toomuchink, false); if (color.icc){ Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(color.icc->colorProfile.c_str()); - if (prof->getColorSpace() == icSigCmykData || prof->getColorSpace() == icSigCmyData){ + if ( prof && ( (prof->getColorSpace() == icSigCmykData) || (prof->getColorSpace() == icSigCmyData) ) ) { gtk_widget_show(GTK_WIDGET(_box_toomuchink)); double ink_sum = 0; for (unsigned int i=0; i<color.icc->colors.size(); i++){ diff --git a/src/widgets/sp-color-scales.cpp b/src/widgets/sp-color-scales.cpp index e41b81e5c..d20cf65ef 100644 --- a/src/widgets/sp-color-scales.cpp +++ b/src/widgets/sp-color-scales.cpp @@ -11,7 +11,6 @@ #include "../dialogs/dialog-events.h" #include "sp-color-scales.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #define CSC_CHANNEL_R (1 << 0) #define CSC_CHANNEL_G (1 << 1) @@ -232,12 +231,6 @@ void ColorScales::_recalcColor( gboolean changing ) case SP_COLOR_SCALES_MODE_CMYK: { _getCmykaFloatv( c ); - color.device = new SVGDeviceColor(); - color.device->type=DEVICE_CMYK; - color.device->colors.clear(); - for (int i=0;i<4;i++){ - color.device->colors.push_back(c[i]); - } float rgb[3]; sp_color_cmyk_to_rgb_floatv( rgb, c[0], c[1], c[2], c[3] ); @@ -483,19 +476,12 @@ void ColorScales::setMode(SPColorScalesMode mode) gtk_widget_show (_b[4]); _updating = TRUE; - if (_color.device && _color.device->type == DEVICE_CMYK){ - setScaled( _a[0], _color.device->colors[0] ); - setScaled( _a[1], _color.device->colors[1] ); - setScaled( _a[2], _color.device->colors[2] ); - setScaled( _a[3], _color.device->colors[3] ); - } else { - //If we still dont have a device-color, convert from rbga - sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); - setScaled( _a[0], c[0] ); - setScaled( _a[1], c[1] ); - setScaled( _a[2], c[2] ); - setScaled( _a[3], c[3] ); - } + sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); + setScaled( _a[0], c[0] ); + setScaled( _a[1], c[1] ); + setScaled( _a[2], c[2] ); + setScaled( _a[3], c[3] ); + setScaled( _a[4], rgba[3] ); _updating = FALSE; _updateSliders( CSC_CHANNELS_ALL ); diff --git a/src/widgets/swatch-selector.cpp b/src/widgets/swatch-selector.cpp index a6f5133b7..88abca358 100644 --- a/src/widgets/swatch-selector.cpp +++ b/src/widgets/swatch-selector.cpp @@ -116,7 +116,7 @@ void SwatchSelector::_changedCb(SPColorSelector */*csel*/, void *data) // TODO replace with proper - sp_gradient_vector_widget_load_gradient(GTK_WIDGET(swsel->_gsel), ngr); } - sp_gradient_ensure_vector(ngr); + ngr->ensureVector(); SPStop* stop = ngr->getFirstStop(); @@ -169,10 +169,9 @@ void SwatchSelector::connectchangedHandler( GCallback handler, void *data ) void SwatchSelector::setVector(SPDocument */*doc*/, SPGradient *vector) { //GtkVBox * box = gobj(); - _gsel->setVector((vector) ? SP_OBJECT_DOCUMENT(vector) : 0, vector); - if (vector) { + if ( vector && vector->isSolid() ) { SPStop* stop = vector->getFirstStop(); guint32 const colorVal = sp_stop_get_rgba32(stop); @@ -180,6 +179,9 @@ void SwatchSelector::setVector(SPDocument */*doc*/, SPGradient *vector) SPColor color( SP_RGBA32_R_F(colorVal), SP_RGBA32_G_F(colorVal), SP_RGBA32_B_F(colorVal) ); // set its color, from the stored array _csel->base->setColor( color ); + gtk_widget_show_all( GTK_WIDGET(_csel) ); + } else { + gtk_widget_hide( GTK_WIDGET(_csel) ); } /* diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index de97e9ea4..bdf1e6ff2 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -85,6 +85,8 @@ #include "../sp-text.h" #include "../style.h" #include "../svg/css-ostringstream.h" +#include "../text-context.h" +#include "../text-editing.h" #include "../tools-switch.h" #include "../tweak-context.h" #include "../spray-context.h" @@ -106,7 +108,6 @@ #include "toolbox.h" -#define ENABLE_TASK_SUPPORT 1 //#define DEBUG_TEXT using Inkscape::UnitTracker; @@ -145,14 +146,6 @@ static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mai static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); -#if ENABLE_TASK_SUPPORT -static void fireTaskChange( EgeSelectOneAction *act, SPDesktop *dt ) -{ - gint selected = ege_select_one_action_get_active( act ); - UXManager::getInstance()->setTask(dt, selected); -} -#endif // ENABLE_TASK_SUPPORT - using Inkscape::UI::ToolboxFactory; @@ -487,9 +480,15 @@ static gchar const * ui_descr = " <separator />" " <toolitem action='TextAlignAction' />" " <separator />" + " <toolitem action='TextSuperscriptAction' />" + " <toolitem action='TextSubscriptAction' />" + " <separator />" " <toolitem action='TextLineHeightAction' />" " <toolitem action='TextLetterSpacingAction' />" " <toolitem action='TextWordSpacingAction' />" + " <toolitem action='TextDxAction' />" + " <toolitem action='TextDyAction' />" + " <toolitem action='TextRotationAction' />" " <separator />" " <toolitem action='TextOrientationAction' />" " </toolbar>" @@ -970,44 +969,6 @@ static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* deskto } } -#if ENABLE_TASK_SUPPORT - if ( !mainActions->get_action("TaskSetAction") ) { - GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ); - - GtkTreeIter iter; - gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, - 0, _("Default"), - 1, _("Default interface setup"), - -1 ); - - gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, - 0, _("Custom"), - 1, _("Set the custom task"), - -1 ); - - gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, - 0, _("Wide"), - 1, _("Setup for widescreen work"), - -1 ); - - EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) ); - g_object_set( act, "short_label", _("Task:"), NULL ); - mainActions->add(Glib::wrap(GTK_ACTION(act))); - //g_object_set_data( holder, "mode_action", act ); - - ege_select_one_action_set_appearance( act, "minimal" ); - ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); - //ege_select_one_action_set_icon_size( act, secondarySize ); - ege_select_one_action_set_tooltip_column( act, 1 ); - - //ege_select_one_action_set_active( act, mode ); - g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop ); - } -#endif // ENABLE_TASK_SUPPORT - return mainActions; } @@ -1031,7 +992,8 @@ static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType han gtk_widget_set_sensitive(tb, FALSE); GtkWidget *hb = 0; - if ( UXManager::getInstance()->isFloatWindowProblem() ) { + gboolean forceFloatAllowed = Inkscape::Preferences::get()->getBool("/options/workarounds/floatallowed", false); + if ( UXManager::getInstance()->isFloatWindowProblem() && !forceFloatAllowed ) { hb = gtk_event_box_new(); // A simple, neutral container. } else { hb = gtk_handle_box_new(); @@ -1853,26 +1815,40 @@ void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop) gchar const * descr = "<ui>" " <toolbar name='ToolToolbar'>" + + " <!-- Basics -->" " <toolitem action='ToolSelector' />" " <toolitem action='ToolNode' />" " <toolitem action='ToolTweak' />" - " <toolitem action='ToolSpray' />" " <toolitem action='ToolZoom' />" + + " <!-- Shapes -->" " <toolitem action='ToolRect' />" " <toolitem action='Tool3DBox' />" " <toolitem action='ToolArc' />" " <toolitem action='ToolStar' />" " <toolitem action='ToolSpiral' />" + + " <!-- Paths -->" " <toolitem action='ToolPencil' />" " <toolitem action='ToolPen' />" " <toolitem action='ToolCalligraphic' />" + + " <!-- Text -->" + " <toolitem action='ToolText' />" + + " <!-- Paint large areas -->" + " <toolitem action='ToolSpray' />" " <toolitem action='ToolEraser' />" -// " <toolitem action='ToolLPETool' />" + + " <!-- Fill -->" " <toolitem action='ToolPaintBucket' />" - " <toolitem action='ToolText' />" - " <toolitem action='ToolConnector' />" " <toolitem action='ToolGradient' />" " <toolitem action='ToolDropper' />" + + " <toolitem action='ToolConnector' />" + +// " <toolitem action='ToolLPETool' />" " </toolbar>" "</ui>"; @@ -2039,10 +2015,6 @@ void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " <separator />" " <toolitem action='DialogPreferences' />" " <toolitem action='DialogDocumentProperties' />" -#if ENABLE_TASK_SUPPORT - " <separator />" - " <toolitem action='TaskSetAction' />" -#endif // ENABLE_TASK_SUPPORT " </toolbar>" "</ui>"; @@ -6605,6 +6577,82 @@ static void sp_text_style_changed( InkToggleAction* act, GObject *tbl ) g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } +// Handles both Superscripts and Subscripts +static void sp_text_script_changed( InkToggleAction* act, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + // Called by Superscript or Subscript button? + const gchar* name = gtk_action_get_name( GTK_ACTION( act ) ); + gint prop = (strcmp(name, "TextSuperscriptAction") == 0) ? 0 : 1; + +#ifdef DEBUG_TEXT + std::cout << "sp_text_script_changed: " << prop << std::endl; +#endif + + // Query baseline + SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); + int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES); + + bool setSuper = false; + bool setSub = false; + + if(result_baseline == QUERY_STYLE_NOTHING || result_baseline == QUERY_STYLE_MULTIPLE_DIFFERENT ) { + // If not set or mixed, turn on superscript or subscript + if( prop == 0 ) { + setSuper = true; + } else { + setSub = true; + } + } else { + // Superscript + gboolean superscriptSet = (query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER ); + + // Subscript + gboolean subscriptSet = (query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB ); + + setSuper = !superscriptSet && prop == 0; + setSub = !subscriptSet && prop == 1; + } + + // Set css properties + SPCSSAttr *css = sp_repr_css_attr_new (); + if( setSuper || setSub ) { + // Openoffice 2.3 and Adobe use 58%, Microsoft Word 2002 uses 65%, LaTex about 70%. + // 58% looks too small to me, especially if a superscript is placed on a superscript. + // If you make a change here, consider making a change to baseline-shift amount + // in style.cpp. + sp_repr_css_set_property (css, "font-size", "65%"); + } else { + sp_repr_css_set_property (css, "font-size", ""); + } + if( setSuper ) { + sp_repr_css_set_property (css, "baseline-shift", "super"); + } else if( setSub ) { + sp_repr_css_set_property (css, "baseline-shift", "sub"); + } else { + sp_repr_css_set_property (css, "baseline-shift", "baseline"); + } + + // Apply css to selected objects. + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + sp_desktop_set_style (desktop, css, true, false); + + // Save for undo + sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:script", SP_VERB_NONE, + _("Text: Change superscript or subscript")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + static void sp_text_align_mode_changed( EgeSelectOneAction *act, GObject *tbl ) { // quit if run by the _changed callbacks @@ -6769,7 +6817,17 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl ) // Apply line-height to selected objects. SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, true); + sp_desktop_set_style (desktop, css, true, false); + + + // Until deprecated sodipodi:linespacing purged: + Inkscape::Selection *selection = sp_desktop_selection(desktop); + GSList const *items = selection->itemList(); + for (; items != NULL; items = items->next) { + if (SP_IS_TEXT (items->data)) { + SP_OBJECT_REPR(items->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL)); + } + } // Save for undo sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:line-height", SP_VERB_NONE, @@ -6803,12 +6861,12 @@ static void sp_text_wordspacing_value_changed( GtkAdjustment *adj, GObject *tbl // Set css word-spacing SPCSSAttr *css = sp_repr_css_attr_new (); Inkscape::CSSOStringStream osfs; - osfs << adj->value; + osfs << adj->value << "px"; // For now always use px sp_repr_css_set_property (css, "word-spacing", osfs.str().c_str()); // Apply word-spacing to selected objects. SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, true); + sp_desktop_set_style (desktop, css, true, false); // Save for undo sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:word-spacing", SP_VERB_NONE, @@ -6842,12 +6900,12 @@ static void sp_text_letterspacing_value_changed( GtkAdjustment *adj, GObject *tb // Set css letter-spacing SPCSSAttr *css = sp_repr_css_attr_new (); Inkscape::CSSOStringStream osfs; - osfs << adj->value; + osfs << adj->value << "px"; // For now always use px sp_repr_css_set_property (css, "letter-spacing", osfs.str().c_str()); // Apply letter-spacing to selected objects. SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, true); + sp_desktop_set_style (desktop, css, true, false); // Save for undo sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:letter-spacing", SP_VERB_NONE, @@ -6869,6 +6927,100 @@ static void sp_text_letterspacing_value_changed( GtkAdjustment *adj, GObject *tb g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } + +static void sp_text_dx_value_changed( GtkAdjustment *adj, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + gdouble new_dx = adj->value; + + if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) { + SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context); + if( tc ) { + unsigned char_index = -1; + TextTagAttributes *attributes = + text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index ); + if( attributes ) { + double old_dx = attributes->getDx( char_index ); + double delta_dx = new_dx - old_dx; + sp_te_adjust_dx( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_dx ); + } + } + } + + // Save for undo + sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:dx", SP_VERB_NONE, + _("Text: Change dx (kern)")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + +static void sp_text_dy_value_changed( GtkAdjustment *adj, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + gdouble new_dy = adj->value; + + if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) { + SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context); + if( tc ) { + unsigned char_index = -1; + TextTagAttributes *attributes = + text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index ); + if( attributes ) { + double old_dy = attributes->getDy( char_index ); + double delta_dy = new_dy - old_dy; + sp_te_adjust_dy( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_dy ); + } + } + } + + // Save for undo + sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:dy", SP_VERB_NONE, + _("Text: Change dy")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + +static void sp_text_rotation_value_changed( GtkAdjustment *adj, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + gdouble new_degrees = adj->value; + + if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) { + SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context); + if( tc ) { + unsigned char_index = -1; + TextTagAttributes *attributes = + text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index ); + if( attributes ) { + double old_degrees = attributes->getRotate( char_index ); + double delta_deg = new_degrees - old_degrees; + sp_te_adjust_rotation( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_deg ); + } + } + } + + // Save for undo + sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:rotate", SP_VERB_NONE, + _("Text: Change rotate")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *tbl ) { // quit if run by the _changed callbacks @@ -6938,6 +7090,9 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ const gchar* id = SP_OBJECT_ID((SPItem *) items->data); std::cout << " " << id << std::endl; } + Glib::ustring selected_text = sp_text_get_selected_text((SP_ACTIVE_DESKTOP)->event_context); + std::cout << " Selected text:" << std::endl; + std::cout << selected_text << std::endl; #endif // quit if run by the _changed callbacks @@ -6951,6 +7106,21 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + // Only flowed text can be justified, only normal text can be kerned... + // Find out if we have flowed text now so we can use it several places + gboolean isFlow = false; + for (GSList const *items = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList(); + items != NULL; + items = items->next) { + // const gchar* id = SP_OBJECT_ID((SPItem *) items->data); + // std::cout << " " << id << std::endl; + if( SP_IS_FLOWTEXT(( SPItem *) items->data )) { + isFlow = true; + // std::cout << " Found flowed text" << std::endl; + break; + } + } + /* * Query from current selection: * Font family (font-family) @@ -6963,6 +7133,8 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES); + // Used later: sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); @@ -7007,6 +7179,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ ink_comboboxentry_action_set_active_text( fontFamilyAction, fontFamily ); } + // Size (average of text selected) double size = query->font_size.computed; gchar size_text[G_ASCII_DTOSTR_BUF_SIZE]; @@ -7016,6 +7189,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontSizeAction" ) ); ink_comboboxentry_action_set_active_text( fontSizeAction, size_text ); + // Weight (Bold) // Note: in the enumeration, normal and lighter come at the end so we must explicitly test for them. gboolean boldSet = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) && @@ -7033,31 +7207,47 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textItalicAction), italicSet ); + // Superscript + gboolean superscriptSet = + ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && + query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER ); + + InkToggleAction* textSuperscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSuperscriptAction" ) ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSuperscriptAction), superscriptSet ); + + + // Subscript + gboolean subscriptSet = + ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && + query->baseline_shift.set && + query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL && + query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB ); + + InkToggleAction* textSubscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSubscriptAction" ) ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSubscriptAction), subscriptSet ); + + // Alignment + EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) ); + // Note: SVG 1.1 doesn't include text-align, SVG 1.2 Tiny doesn't include text-align="justify" // text-align="justify" was a draft SVG 1.2 item (along with flowed text). // Only flowed text can be left and right justified at the same time. - // Check if we have flowed text and disable botton. - // NEED: ege_select_one_action_set_sensitve( ) - /* - gboolean isFlow = false; - for (GSList const *items = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList(); - items != NULL; - items = items->next) { - const gchar* id = SP_OBJECT_ID((SPItem *) items->data); - std::cout << " " << id << std::endl; - if( SP_IS_FLOWTEXT(( SPItem *) items->data )) { - isFlow = true; - std::cout << " Found flowed text" << std::endl; - break; - } - } - if( isFlow ) { - // enable justify button - } else { - // disable justify button - } - */ + // Disable button if we don't have flowed text. + + // The GtkTreeModel class doesn't have a set function so we can't + // simply add an ege_select_one_action_set_sensitive method! + // We must set values directly with the GtkListStore and then + // ask that the GtkAction update the sensitive parameters. + GtkListStore * model = GTK_LIST_STORE( ege_select_one_action_get_model( textAlignAction ) ); + GtkTreePath * path = gtk_tree_path_new_from_string("3"); // Justify entry + GtkTreeIter iter; + gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path ); + gtk_list_store_set( model, &iter, /* column */ 3, isFlow, -1 ); + ege_select_one_action_update_sensitive( textAlignAction ); + // ege_select_one_action_set_sensitive( textAlignAction, 3, isFlow ); int activeButton = 0; if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY) @@ -7068,7 +7258,6 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) activeButton = 1; if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END) activeButton = 2; } - EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) ); ege_select_one_action_set_active( textAlignAction, activeButton ); @@ -7099,7 +7288,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ GtkAdjustment *wordSpacingAdjustment = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( wordSpacingAction )); gtk_adjustment_set_value( wordSpacingAdjustment, wordSpacing ); - + // Letter spacing double letterSpacing; @@ -7141,12 +7330,68 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ << " letter_spacing.value: " << query->letter_spacing.value << " letter_spacing.unit: " << query->letter_spacing.unit << std::endl; std::cout << " GUI: writing_mode.computed: " << query->writing_mode.computed << std::endl; - std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; - std::cout << std::endl; #endif sp_style_unref(query); + // Kerning (xshift), yshift, rotation. NB: These are not CSS attributes. + if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) { + SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context); + if( tc ) { + unsigned char_index = -1; + TextTagAttributes *attributes = + text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index ); + if( attributes ) { + + // Dx + double dx = attributes->getDx( char_index ); + GtkAction* dxAction = GTK_ACTION( g_object_get_data( tbl, "TextDxAction" )); + GtkAdjustment *dxAdjustment = + ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( dxAction )); + gtk_adjustment_set_value( dxAdjustment, dx ); + + // Dy + double dy = attributes->getDy( char_index ); + GtkAction* dyAction = GTK_ACTION( g_object_get_data( tbl, "TextDyAction" )); + GtkAdjustment *dyAdjustment = + ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( dyAction )); + gtk_adjustment_set_value( dyAdjustment, dy ); + + // Rotation + double rotation = attributes->getRotate( char_index ); + /* SVG value is between 0 and 360 but we're using -180 to 180 in widget */ + if( rotation > 180.0 ) rotation -= 360.0; + GtkAction* rotationAction = GTK_ACTION( g_object_get_data( tbl, "TextRotationAction" )); + GtkAdjustment *rotationAdjustment = + ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( rotationAction )); + gtk_adjustment_set_value( rotationAdjustment, rotation ); + +#ifdef DEBUG_TEXT + std::cout << " GUI: Dx: " << dx << std::endl; + std::cout << " GUI: Dy: " << dy << std::endl; + std::cout << " GUI: Rotation: " << rotation << std::endl; +#endif + } + } + } + + { + // Set these here as we don't always have kerning/rotating attributes + GtkAction* dxAction = GTK_ACTION( g_object_get_data( tbl, "TextDxAction" )); + gtk_action_set_sensitive( GTK_ACTION(dxAction), !isFlow ); + + GtkAction* dyAction = GTK_ACTION( g_object_get_data( tbl, "TextDyAction" )); + gtk_action_set_sensitive( GTK_ACTION(dyAction), !isFlow ); + + GtkAction* rotationAction = GTK_ACTION( g_object_get_data( tbl, "TextRotationAction" )); + gtk_action_set_sensitive( GTK_ACTION(rotationAction), !isFlow ); + } + +#ifdef DEBUG_TEXT + std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; + std::cout << std::endl; +#endif + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } @@ -7221,7 +7466,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontSizeAction", _("Font Size"), - _("Select Font Size"), + _("Font size (px)"), NULL, GTK_TREE_MODEL(model_size), 4 ); // Width in characters @@ -7234,7 +7479,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions { InkToggleAction* act = ink_toggle_action_new( "TextBoldAction", // Name _("Toggle Bold"), // Label - _("Toggle On/Off Bold Style"), // Tooltip + _("Toggle bold or normal weight"), // Tooltip GTK_STOCK_BOLD, // Icon (inkId) secondarySize ); // Icon size gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); @@ -7247,7 +7492,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions { InkToggleAction* act = ink_toggle_action_new( "TextItalicAction", // Name _("Toggle Italic/Oblique"), // Label - _("Toggle On/Off Italic/Oblique Style"),// Tooltip + _("Toggle italic/oblique style"),// Tooltip GTK_STOCK_ITALIC, // Icon (inkId) secondarySize ); // Icon size gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); @@ -7256,9 +7501,35 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions g_object_set_data( holder, "TextItalicAction", act ); } + /* Style - Superscript */ + { + InkToggleAction* act = ink_toggle_action_new( "TextSuperscriptAction", // Name + _("Toggle Superscript"), // Label + _("Toggle superscript"), // Tooltip + "text_superscript", // Icon (inkId) + secondarySize ); // Icon size + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/super", false) ); + g_object_set_data( holder, "TextSuperscriptAction", act ); + } + + /* Style - Subscript */ + { + InkToggleAction* act = ink_toggle_action_new( "TextSubscriptAction", // Name + _("Toggle Subscript"), // Label + _("Toggle subscript"), // Tooltip + "text_subscript", // Icon (inkId) + secondarySize ); // Icon size + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/sub", false) ); + g_object_set_data( holder, "TextSubscriptAction", act ); + } + /* Alignment */ { - GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); + GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN ); GtkTreeIter iter; @@ -7267,6 +7538,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions 0, _("Align left"), 1, _("Align left"), 2, GTK_STOCK_JUSTIFY_LEFT, + 3, true, -1 ); gtk_list_store_append( model, &iter ); @@ -7274,6 +7546,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions 0, _("Align center"), 1, _("Align center"), 2, GTK_STOCK_JUSTIFY_CENTER, + 3, true, -1 ); gtk_list_store_append( model, &iter ); @@ -7281,21 +7554,23 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions 0, _("Align right"), 1, _("Align right"), 2, GTK_STOCK_JUSTIFY_RIGHT, + 3, true, -1 ); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Justify"), - 1, _("Justify - Only flowed text"), + 1, _("Justify (only flowed text)"), 2, GTK_STOCK_JUSTIFY_FILL, + 3, false, -1 ); EgeSelectOneAction* act = ege_select_one_action_new( "TextAlignAction", // Name _("Alignment"), // Label - _("Text Alignment"), // Tooltip + _("Text alignment"), // Tooltip NULL, // StockID GTK_TREE_MODEL(model) ); // Model - g_object_set( act, "short_label", _("Align"), NULL ); + g_object_set( act, "short_label", "NotUsed", NULL ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); g_object_set_data( holder, "TextAlignAction", act ); @@ -7305,7 +7580,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions ege_select_one_action_set_icon_column( act, 2 ); ege_select_one_action_set_icon_size( act, secondarySize ); ege_select_one_action_set_tooltip_column( act, 1 ); - + ege_select_one_action_set_sensitive_column( act, 3 ); gint mode = prefs->getInt("/tools/text/align_mode", 0); ege_select_one_action_set_active( act, mode ); g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_align_mode_changed), holder ); @@ -7320,24 +7595,24 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Horizontal"), - 1, _("Horizontal Text"), + 1, _("Horizontal text"), 2, INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL, -1 ); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Vertical"), - 1, _("Vertical Text"), + 1, _("Vertical text"), 2, INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL, -1 ); EgeSelectOneAction* act = ege_select_one_action_new( "TextOrientationAction", // Name _("Orientation"), // Label - _("Text Orientation"), // Tooltip + _("Text orientation"), // Tooltip NULL, // StockID GTK_TREE_MODEL(model) ); // Model - g_object_set( act, "short_label", _("O:"), NULL ); + g_object_set( act, "short_label", "NotUsed", NULL ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); g_object_set_data( holder, "TextOrientationAction", act ); @@ -7363,8 +7638,8 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions "TextLineHeightAction", /* name */ _("Line Height"), /* label */ _("Line:"), /* short label */ - _("Spacing between lines."), /* tooltip */ - "/tools/text/lineheight", /* path? */ + _("Spacing between lines (times font size)"), /* tooltip */ + "/tools/text/lineheight", /* preferences path */ 0.0, /* default */ GTK_WIDGET(desktop->canvas), /* focusTarget */ NULL, /* unit selector */ @@ -7381,6 +7656,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "TextLineHeightAction", eact ); + g_object_set( G_OBJECT(eact), "iconId", "text_line_spacing", NULL ); } /* Word spacing */ @@ -7393,8 +7669,8 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions "TextWordSpacingAction", /* name */ _("Word spacing"), /* label */ _("Word:"), /* short label */ - _("Spacing between words."), /* tooltip */ - "/tools/text/wordspacing", /* path? */ + _("Spacing between words (px)"), /* tooltip */ + "/tools/text/wordspacing", /* preferences path */ 0.0, /* default */ GTK_WIDGET(desktop->canvas), /* focusTarget */ NULL, /* unit selector */ @@ -7411,6 +7687,7 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "TextWordSpacingAction", eact ); + g_object_set( G_OBJECT(eact), "iconId", "text_word_spacing", NULL ); } /* Letter spacing */ @@ -7423,8 +7700,8 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions "TextLetterSpacingAction", /* name */ _("Letter spacing"), /* label */ _("Letter:"), /* short label */ - _("Spacing between letters."), /* tooltip */ - "/tools/text/letterspacing", /* path? */ + _("Spacing between letters (px)"), /* tooltip */ + "/tools/text/letterspacing", /* preferences path */ 0.0, /* default */ GTK_WIDGET(desktop->canvas), /* focusTarget */ NULL, /* unit selector */ @@ -7441,6 +7718,100 @@ static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "TextLetterSpacingAction", eact ); + g_object_set( G_OBJECT(eact), "iconId", "text_letter_spacing", NULL ); + } + + /* Character kerning (horizontal shift) */ + { + // Drop down menu + gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + gdouble values[] = { -2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5 }; + + EgeAdjustmentAction *eact = create_adjustment_action( + "TextDxAction", /* name */ + _("Kerning"), /* label */ + _("Kern:"), /* short label */ + _("Horizontal kerning (px)"), /* tooltip */ + "/tools/text/dx", /* preferences path */ + 0.0, /* default */ + GTK_WIDGET(desktop->canvas), /* focusTarget */ + NULL, /* unit selector */ + holder, /* dataKludge */ + FALSE, /* set alt-x keyboard shortcut? */ + NULL, /* altx_mark */ + -100.0, 100.0, 0.01, 0.1, /* lower, upper, step (arrow up/down), page up/down */ + labels, values, G_N_ELEMENTS(labels), /* drop down menu */ + sp_text_dx_value_changed, /* callback */ + 0.1, /* step (used?) */ + 2, /* digits to show */ + 1.0 /* factor (multiplies default) */ + ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + g_object_set_data( holder, "TextDxAction", eact ); + g_object_set( G_OBJECT(eact), "iconId", "text_horz_kern", NULL ); + } + + /* Character vertical shift */ + { + // Drop down menu + gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + gdouble values[] = { -2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5 }; + + EgeAdjustmentAction *eact = create_adjustment_action( + "TextDyAction", /* name */ + _("Vertical Shift"), /* label */ + _("Vert:"), /* short label */ + _("Vertical shift (px)"), /* tooltip */ + "/tools/text/dy", /* preferences path */ + 0.0, /* default */ + GTK_WIDGET(desktop->canvas), /* focusTarget */ + NULL, /* unit selector */ + holder, /* dataKludge */ + FALSE, /* set alt-x keyboard shortcut? */ + NULL, /* altx_mark */ + -100.0, 100.0, 0.01, 0.1, /* lower, upper, step (arrow up/down), page up/down */ + labels, values, G_N_ELEMENTS(labels), /* drop down menu */ + sp_text_dy_value_changed, /* callback */ + 0.1, /* step (used?) */ + 2, /* digits to show */ + 1.0 /* factor (multiplies default) */ + ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + g_object_set_data( holder, "TextDyAction", eact ); + g_object_set( G_OBJECT(eact), "iconId", "text_vert_kern", NULL ); + } + + /* Character rotation */ + { + // Drop down menu + gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + gdouble values[] = { -90, -45, -30, -15, 0, 15, 30, 45, 90, 180 }; + + EgeAdjustmentAction *eact = create_adjustment_action( + "TextRotationAction", /* name */ + _("Letter rotation"), /* label */ + _("Rot:"), /* short label */ + _("Character rotation (degrees)"),/* tooltip */ + "/tools/text/rotation", /* preferences path */ + 0.0, /* default */ + GTK_WIDGET(desktop->canvas), /* focusTarget */ + NULL, /* unit selector */ + holder, /* dataKludge */ + FALSE, /* set alt-x keyboard shortcut? */ + NULL, /* altx_mark */ + -180.0, 180.0, 0.1, 1.0, /* lower, upper, step (arrow up/down), page up/down */ + labels, values, G_N_ELEMENTS(labels), /* drop down menu */ + sp_text_rotation_value_changed, /* callback */ + 0.1, /* step (used?) */ + 2, /* digits to show */ + 1.0 /* factor (multiplies default) */ + ); + gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); + gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); + g_object_set_data( holder, "TextRotationAction", eact ); + g_object_set( G_OBJECT(eact), "iconId", "text_rotation", NULL ); } // Is this necessary to call? Shouldn't hurt. |
