diff options
| author | Denis Declara <declara91@gmail.com> | 2012-03-26 15:59:00 +0000 |
|---|---|---|
| committer | Denis Declara <declara91@gmail.com> | 2012-03-26 15:59:00 +0000 |
| commit | 8874f80dd60361ed721554497862ce3a6fb5ea8e (patch) | |
| tree | fe114b3559c430d1a90f073a3458f4bd1c6d2285 /src | |
| parent | Added anchor-selection widget, it doesn't do much at the moment, next step wi... (diff) | |
| parent | powerstroke: spiro join: more robust detection of tangent direction. fixes in... (diff) | |
| download | inkscape-8874f80dd60361ed721554497862ce3a6fb5ea8e.tar.gz inkscape-8874f80dd60361ed721554497862ce3a6fb5ea8e.zip | |
Trunk merge
(bzr r11073.1.4)
Diffstat (limited to 'src')
37 files changed, 3506 insertions, 2597 deletions
diff --git a/src/2geom/svg-path.h b/src/2geom/svg-path.h index 89192fb72..591eb3aa2 100644 --- a/src/2geom/svg-path.h +++ b/src/2geom/svg-path.h @@ -120,6 +120,14 @@ public: large_arc, sweep, p); } + void append(Path const &other, Path::Stitching stitching = Path::NO_STITCHING) + { + if (!_in_path) { + moveTo(other.initialPoint()); + } + _path.append(other, stitching); + } + void closePath() { _path.close(); finish(); diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index 7af6f2ede..1cd230a08 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -1,12 +1,10 @@ set(dialogs_SRC - clonetiler.cpp dialog-events.cpp find.cpp # ------- # Headers - clonetiler.h dialog-events.h find.h ) diff --git a/src/dialogs/Makefile_insert b/src/dialogs/Makefile_insert index ac0792a73..62641ecb9 100644 --- a/src/dialogs/Makefile_insert +++ b/src/dialogs/Makefile_insert @@ -1,8 +1,6 @@ ## Makefile.am fragment sourced by src/Makefile.am. ink_common_sources += \ - dialogs/clonetiler.cpp \ - dialogs/clonetiler.h \ dialogs/dialog-events.cpp \ dialogs/dialog-events.h \ dialogs/find.cpp \ diff --git a/src/dialogs/clonetiler.h b/src/dialogs/clonetiler.h deleted file mode 100644 index 899181346..000000000 --- a/src/dialogs/clonetiler.h +++ /dev/null @@ -1,30 +0,0 @@ -/** @file - * @brief Clone tiling dialog - */ -/* Authors: - * bulia byak <buliabyak@users.sf.net> - * - * Copyright (C) 2004 Authors - * Released under the GNU GPL, read the file 'COPYING' for more information - */ -#ifndef __SP_CLONE_TILER_H__ -#define __SP_CLONE_TILER_H__ - -#include <glib.h> - -#include <gtk/gtk.h> - -void clonetiler_dialog ( void ); - -#endif - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index 3b19f925e..5347a85ac 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -1672,17 +1672,23 @@ void PdfParser::opShFill(Object args[], int /*numArgs*/) if (matrix != NULL) { xTemp = matrix[0]*xMin + matrix[2]*yMin + matrix[4]; yTemp = matrix[1]*xMin + matrix[3]*yMin + matrix[5]; - xMin = xTemp; - yMin = yTemp; + state->moveTo(xTemp, yTemp); + xTemp = matrix[0]*xMax + matrix[2]*yMin + matrix[4]; + yTemp = matrix[1]*xMax + matrix[3]*yMin + matrix[5]; + state->lineTo(xTemp, yTemp); xTemp = matrix[0]*xMax + matrix[2]*yMax + matrix[4]; yTemp = matrix[1]*xMax + matrix[3]*yMax + matrix[5]; - xMax = xTemp; - yMax = yTemp; + state->lineTo(xTemp, yTemp); + xTemp = matrix[0]*xMin + matrix[2]*yMax + matrix[4]; + yTemp = matrix[1]*xMin + matrix[3]*yMax + matrix[5]; + state->lineTo(xTemp, yTemp); + } + else { + state->moveTo(xMin, yMin); + state->lineTo(xMax, yMin); + state->lineTo(xMax, yMax); + state->lineTo(xMin, yMax); } - state->moveTo(xMin, yMin); - state->lineTo(xMax, yMin); - state->lineTo(xMax, yMax); - state->lineTo(xMin, yMax); state->closePath(); state->clip(); if (savedState) diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index e066e7f43..a5f50a69d 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -1,6 +1,5 @@ set(live_effects_SRC - bezctx.cpp effect.cpp lpe-angle_bisector.cpp lpe-bendpath.cpp @@ -41,6 +40,7 @@ set(live_effects_SRC lpeobject-reference.cpp lpeobject.cpp spiro.cpp + spiro-converters.cpp parameter/array.cpp parameter/bool.cpp @@ -58,8 +58,6 @@ set(live_effects_SRC # ------- # Headers - bezctx.h - bezctx_intf.h effect-enum.h effect.h lpe-angle_bisector.h @@ -101,6 +99,8 @@ set(live_effects_SRC lpegroupbbox.h lpeobject-reference.h lpeobject.h + spiro.h + spiro-converters.h parameter/array.h parameter/bool.h @@ -115,7 +115,7 @@ set(live_effects_SRC parameter/text.h parameter/unit.h parameter/vector.h - spiro.h + ) # add_inkscape_lib(live_effects_LIB "${live_effects_SRC}") diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 74356f563..9c3c171f2 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -50,9 +50,8 @@ ink_common_sources += \ live_effects/lpe-perp_bisector.h \ live_effects/spiro.h \ live_effects/spiro.cpp \ - live_effects/bezctx.h \ - live_effects/bezctx_intf.h \ - live_effects/bezctx.cpp \ + live_effects/spiro-converters.h \ + live_effects/spiro-converters.cpp \ live_effects/lpe-circle_with_radius.cpp \ live_effects/lpe-circle_with_radius.h \ live_effects/lpe-perspective_path.cpp \ diff --git a/src/live_effects/bezctx.cpp b/src/live_effects/bezctx.cpp deleted file mode 100644 index 722f5dbaf..000000000 --- a/src/live_effects/bezctx.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* -ppedit - A pattern plate editor for Spiro splines. -Copyright (C) 2007 Raph Levien - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301, USA. - -*/ -#include "bezctx.h" - -void bezctx_moveto(bezctx *bc, double x, double y, int is_open) -{ - bc->moveto(bc, x, y, is_open); -} - -void bezctx_lineto(bezctx *bc, double x, double y) -{ - bc->lineto(bc, x, y); -} - -void bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2) -{ - bc->quadto(bc, x1, y1, x2, y2); -} - -void bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3) -{ - bc->curveto(bc, x1, y1, x2, y2, x3, y3); -} - -void bezctx_mark_knot(bezctx *bc, int knot_idx) -{ - if (bc->mark_knot) - bc->mark_knot(bc, knot_idx); -} diff --git a/src/live_effects/bezctx.h b/src/live_effects/bezctx.h deleted file mode 100644 index 057312e5a..000000000 --- a/src/live_effects/bezctx.h +++ /dev/null @@ -1,10 +0,0 @@ -#include "bezctx_intf.h" - -struct _bezctx { - void (*moveto)(bezctx *bc, double x, double y, int is_open); - void (*lineto)(bezctx *bc, double x, double y); - void (*quadto)(bezctx *bc, double x1, double y1, double x2, double y2); - void (*curveto)(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3); - void (*mark_knot)(bezctx *bc, int knot_idx); -}; diff --git a/src/live_effects/bezctx_intf.h b/src/live_effects/bezctx_intf.h deleted file mode 100644 index a47b8ef5a..000000000 --- a/src/live_effects/bezctx_intf.h +++ /dev/null @@ -1,20 +0,0 @@ -typedef struct _bezctx bezctx; - -bezctx * -new_bezctx(void); - -void -bezctx_moveto(bezctx *bc, double x, double y, int is_open); - -void -bezctx_lineto(bezctx *bc, double x, double y); - -void -bezctx_quadto(bezctx *bc, double x1, double y1, double x2, double y2); - -void -bezctx_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3); - -void -bezctx_mark_knot(bezctx *bc, int knot_idx); diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h index 7f9cb3ddb..6f5b75af8 100644 --- a/src/live_effects/lpe-powerstroke-interpolators.h +++ b/src/live_effects/lpe-powerstroke-interpolators.h @@ -16,8 +16,6 @@ #include <2geom/bezier-utils.h> #include <2geom/sbasis-to-bezier.h> -#include "live_effects/bezctx.h" -#include "live_effects/bezctx_intf.h" #include "live_effects/spiro.h" @@ -136,7 +134,6 @@ private: }; -#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS class SpiroInterpolator : public Interpolator { public: SpiroInterpolator() {}; @@ -148,8 +145,7 @@ public: Coord scale_y = 100.; guint len = points.size(); - bezctx *bc = new_bezctx_ink(&fit); - spiro_cp *controlpoints = g_new (spiro_cp, len); + Spiro::spiro_cp *controlpoints = g_new (Spiro::spiro_cp, len); for (unsigned int i = 0; i < len; ++i) { controlpoints[i].x = points[i][X]; controlpoints[i].y = points[i][Y] / scale_y; @@ -160,88 +156,13 @@ public: controlpoints[len-2].ty = 'v'; controlpoints[len-1].ty = '}'; - spiro_seg *s = run_spiro(controlpoints, len); - spiro_to_bpath(s, len, bc); - free(s); - free(bc); + Spiro::spiro_run(controlpoints, len, fit); fit *= Scale(1,scale_y); return fit; }; private: - typedef struct { - bezctx base; - Path *path; - int is_open; - } bezctx_ink; - - static void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/) - { - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->path->start(Point(x, y)); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro moveto not finite"); - } - #endif - } - - static void bezctx_ink_lineto(bezctx *bc, double x, double y) - { - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->path->appendNew<LineSegment>( Point(x, y) ); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro lineto not finite"); - } - #endif - } - - static void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3) - { - bezctx_ink *bi = (bezctx_ink *) bc; - - if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) { - bi->path->appendNew<QuadraticBezier>(Point(xm, ym), Point(x3, y3)); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro quadto not finite"); - } - #endif - } - - static void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3) - { - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) { - bi->path->appendNew<CubicBezier>(Point(x1, y1), Point(x2, y2), Point(x3, y3)); - } - #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("spiro curveto not finite"); - } - #endif - } - - bezctx * - new_bezctx_ink(Geom::Path *path) const { - bezctx_ink *result = g_new(bezctx_ink, 1); - result->base.moveto = bezctx_ink_moveto; - result->base.lineto = bezctx_ink_lineto; - result->base.quadto = bezctx_ink_quadto; - result->base.curveto = bezctx_ink_curveto; - result->base.mark_knot = NULL; - result->path = path; - return &result->base; - } - SpiroInterpolator(const SpiroInterpolator&); SpiroInterpolator& operator=(const SpiroInterpolator&); }; diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 257dba8ea..d3843dc92 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -5,7 +5,7 @@ /* Authors: * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl> * - * Copyright (C) 2010-2011 Authors + * Copyright (C) 2010-2012 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -26,9 +26,12 @@ #include <2geom/svg-path.h> #include <2geom/path-intersection.h> #include <2geom/crossing.h> +#include <2geom/ellipse.h> -namespace Geom { +#include "spiro.h" +namespace Geom { +// should all be moved to 2geom at some point Point unitTangentAt( D2<SBasis> const & a, Coord t, unsigned n = 3) { std::vector<Point> derivs = a.valueAndDerivatives(t, n); @@ -55,8 +58,48 @@ boost::optional<Point> intersection_point( Point const & origin_a, Point const & return boost::none; } +Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in) +{ + std::vector<Geom::Point> temp; + sbasis_to_bezier(temp, sbasis_in, 4); + return Geom::CubicBezier( temp ); +} + +/** + * document this! + * very quick: this finds the ellipse with minimum eccentricity + passing through point P and Q, with tangent PO at P and QO at Q + http://mathforum.org/kb/message.jspa?messageID=7471596&tstart=0 + */ +static Ellipse find_ellipse(Point P, Point Q, Point O) +{ + Point p = P - O; + Point q = Q - O; + Coord K = 4 * dot(p,q) / (L2sq(p) + L2sq(q)); + + double cross = p[Y]*q[X] - p[X]*q[Y]; + double a = -q[Y]/cross; + double b = q[X]/cross; + double c = (O[X]*q[Y] - O[Y]*q[X])/cross; + + double d = p[Y]/cross; + double e = -p[X]/cross; + double f = (-O[X]*p[Y] + O[Y]*p[X])/cross; + + // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0 + double A = (a*d*K+d*d+a*a); + double B = (a*e*K+b*d*K+2*d*e+2*a*b); + double C = (b*e*K+e*e+b*b); + double D = (a*f*K+c*d*K+2*d*f-2*d+2*a*c-2*a); + double E = (b*f*K+c*e*K+2*e*f-2*e+2*b*c-2*b); + double F = c*f*K+f*f-2*f+c*c-2*c+1; + + return Ellipse(A, B, C, D, E, F); } + +} // namespace Geom + namespace Inkscape { namespace LivePathEffect { @@ -84,19 +127,21 @@ static const Util::EnumData<unsigned> LineCapTypeData[] = { }; static const Util::EnumDataConverter<unsigned> LineCapTypeConverter(LineCapTypeData, sizeof(LineCapTypeData)/sizeof(*LineCapTypeData)); -enum LineCuspType { - LINECUSP_BEVEL, - LINECUSP_ROUND, - LINECUSP_EXTRP_MITER, - LINECUSP_MITER +enum LineJoinType { + LINEJOIN_BEVEL, + LINEJOIN_ROUND, + LINEJOIN_EXTRP_MITER, + LINEJOIN_MITER, + LINEJOIN_SPIRO }; -static const Util::EnumData<unsigned> LineCuspTypeData[] = { - {LINECUSP_BEVEL, N_("Beveled"), "bevel"}, - {LINECUSP_ROUND, N_("Rounded"), "round"}, - {LINECUSP_EXTRP_MITER, N_("Extrapolated"), "extrapolated"}, - {LINECUSP_MITER, N_("Miter"), "miter"}, +static const Util::EnumData<unsigned> LineJoinTypeData[] = { + {LINEJOIN_BEVEL, N_("Beveled"), "bevel"}, + {LINEJOIN_ROUND, N_("Rounded"), "round"}, + {LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"}, + {LINEJOIN_MITER, N_("Miter"), "miter"}, + {LINEJOIN_SPIRO, N_("Spiro"), "spiro"}, }; -static const Util::EnumDataConverter<unsigned> LineCuspTypeConverter(LineCuspTypeData, sizeof(LineCuspTypeData)/sizeof(*LineCuspTypeData)); +static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData)); LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : Effect(lpeobject), @@ -105,7 +150,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : interpolator_type(_("Interpolator type"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path."), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN), interpolator_beta(_("Smoothness"), _("Sets the smoothness for the CubicBezierJohan interpolator. 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2), start_linecap_type(_("Start cap"), _("Determines the shape of the path's start."), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND), - cusp_linecap_type(_("Join"), _("Specifies the shape of the path's corners."), "cusp_linecap_type", LineCuspTypeConverter, &wr, this, LINECUSP_ROUND), + linejoin_type(_("Join"), _("Specifies the shape of the path's corners."), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND), miter_limit(_("Miter limit"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.), end_linecap_type(_("End cap"), _("Determines the shape of the path's end."), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND) { @@ -121,7 +166,7 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : registerParameter( dynamic_cast<Parameter *>(&interpolator_type) ); registerParameter( dynamic_cast<Parameter *>(&interpolator_beta) ); registerParameter( dynamic_cast<Parameter *>(&start_linecap_type) ); - registerParameter( dynamic_cast<Parameter *>(&cusp_linecap_type) ); + registerParameter( dynamic_cast<Parameter *>(&linejoin_type) ); registerParameter( dynamic_cast<Parameter *>(&miter_limit) ); registerParameter( dynamic_cast<Parameter *>(&end_linecap_type) ); } @@ -163,9 +208,9 @@ static bool compare_offsets (Geom::Point first, Geom::Point second) // find discontinuities in input path struct discontinuity_data { - Geom::Point der0; // unit derivative of 'left' side of cusp - Geom::Point der1; // unit derivative of 'right' side of cusp - double width; // intended stroke width at cusp + Geom::Point der0; // unit derivative of 'left' side of join + Geom::Point der1; // unit derivative of 'right' side of join + double width; // intended stroke width at join }; std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & der, Geom::Piecewise<Geom::SBasis> const & x, @@ -176,8 +221,16 @@ std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<G for(unsigned i = 1; i < der.size(); i++) { if ( ! are_near(der[i-1].at1(), der[i].at0(), eps) ) { discontinuity_data data; + data.der0 = der[i-1].at1(); data.der1 = der[i].at0(); + if ( Geom::are_near(data.der0.length(), 0) ) { + data.der0 = unitTangentAt(der[i-1], 1, 2); + } + if ( Geom::are_near(data.der1.length(), 0) ) { + data.der1 = unitTangentAt(der[i], 0, 2); + } + double t = der.cuts[i]; std::vector< double > rts = roots (x - t); /// @todo this has multiple solutions for general strokewidth paths (generated by spiro interpolator...), ignore for now if (!rts.empty()) { @@ -194,12 +247,12 @@ std::vector<discontinuity_data> find_discontinuities( Geom::Piecewise<Geom::D2<G Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & B, std::vector<discontinuity_data> const & cusps, - LineCuspType cusp_linecap, + LineJoinType jointype, double miter_limit, bool forward_direction, double tol=Geom::EPSILON) { -/* per definition, each discontinuity should be fixed with a cusp-ending, as defined by cusp_linecap_type +/* per definition, each discontinuity should be fixed with a join-ending, as defined by linejoin_type */ Geom::PathBuilder pb; if (B.size() == 0) { @@ -223,14 +276,41 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> { // discontinuity found, so fix it :-) discontinuity_data cusp = cusps[cusp_i]; - switch (cusp_linecap) { - case LINECUSP_ROUND: // properly bugged ^_^ - pb.arcTo( abs(cusp.width), abs(cusp.width), - angle_between(cusp.der0, cusp.der1), false, cusp.width < 0, - B[i].at0() ); + bool on_outside = ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0. ); + + switch (jointype) { + case LINEJOIN_ROUND: { + if (on_outside) { + // we are on the outside: round corner + /* for constant width paths, the rounding is a circular arc (rx == ry), + for non-constant width paths, the rounding can be done with an ellipse but is hard and ambiguous. + The elliptical arc should go through the discontinuity's start and end points (of course!) + and also should match the discontinuity tangents at those start and end points. + To resolve the ambiguity, the elliptical arc with minimal eccentricity should be chosen. + A 2Geom method was created to do exactly this :) + */ + + Geom::Point tang1 = unitTangentAt(B[prev_i],1); + Geom::Point tang2 = unitTangentAt(B[i],0); + boost::optional<Geom::Point> O = intersection_point( B[prev_i].at1(), tang1, + B[i].at0(), tang2 ); + if (!O) { + // no center found, i.e. 180 degrees round + pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles + break; + } + + Geom::Ellipse ellipse = find_ellipse(B[prev_i].at1(), B[i].at0(), *O); + pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rot_angle(), + false, cusp.width < 0, B[i].at0() ); + } else { + // we are on the inside, do a simple bevel to connect the paths + pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles + } break; -/* case LINECUSP_NONE: { - if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) { + } +/* case LINEJOIN_NONE: { + if ( on_outside ) { // we are on the outside Geom::Point der1 = unitTangentAt(B[prev_i],1); Geom::Point point_on_path = B[prev_i].at1() - rot90(der1) * cusp.width; @@ -241,24 +321,18 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles } } */ - case LINECUSP_EXTRP_MITER: { - // first figure out whether we are on the outside or inside of the corner in the path - if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) { + case LINEJOIN_EXTRP_MITER: { + if (on_outside) { // we are on the outside, do something complicated to make it look good ;) Geom::Point der1 = unitTangentAt(B[prev_i],1); Geom::Point der2 = unitTangentAt(B[i],0); Geom::D2<Geom::SBasis> newcurve1 = B[prev_i] * Geom::reflection(rot90(der1), B[prev_i].at1()); - newcurve1 = reverse(newcurve1); - std::vector<Geom::Point> temp; - sbasis_to_bezier(temp, newcurve1, 4); - Geom::CubicBezier bzr1( temp ); + Geom::CubicBezier bzr1 = sbasis_to_cubicbezier( reverse(newcurve1) ); Geom::D2<Geom::SBasis> newcurve2 = B[i] * Geom::reflection(rot90(der2), B[i].at0()); - newcurve2 = reverse(newcurve2); - sbasis_to_bezier(temp, newcurve2, 4); - Geom::CubicBezier bzr2( temp ); + Geom::CubicBezier bzr2 = sbasis_to_cubicbezier( reverse(newcurve2) ); Geom::Crossings cross = crossings(bzr1, bzr2); if (cross.empty()) { @@ -285,9 +359,8 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> } break; } - case LINECUSP_MITER: { - // first figure out whether we are on the outside or inside of the corner in the path - if ( sign*cusp.width*angle_between(cusp.der0, cusp.der1) < 0.) { + case LINEJOIN_MITER: { + if (on_outside) { // we are on the outside, do something complicated to make it look good ;) Geom::Point der1 = unitTangentAt(B[prev_i],1); @@ -310,7 +383,39 @@ Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> } break; } - case LINECUSP_BEVEL: + case LINEJOIN_SPIRO: { + if (on_outside) { + Geom::Point tang1 = unitTangentAt(B[prev_i],1); + Geom::Point tang2 = unitTangentAt(B[i],0); + + Geom::Point direction = B[i].at0() - B[prev_i].at1(); + double tang1_sign = dot(direction,tang1); + double tang2_sign = dot(direction,tang2); + + Spiro::spiro_cp *controlpoints = g_new (Spiro::spiro_cp, 4); + controlpoints[0].x = (B[prev_i].at1() - tang1_sign*tang1)[Geom::X]; + controlpoints[0].y = (B[prev_i].at1() - tang1_sign*tang1)[Geom::Y]; + controlpoints[0].ty = '{'; + controlpoints[1].x = B[prev_i].at1()[Geom::X]; + controlpoints[1].y = B[prev_i].at1()[Geom::Y]; + controlpoints[1].ty = ']'; + controlpoints[2].x = B[i].at0()[Geom::X]; + controlpoints[2].y = B[i].at0()[Geom::Y]; + controlpoints[2].ty = '['; + controlpoints[3].x = (B[i].at0() + tang2_sign*tang2)[Geom::X]; + controlpoints[3].y = (B[i].at0() + tang2_sign*tang2)[Geom::Y]; + controlpoints[3].ty = '}'; + + Geom::Path spiro; + Spiro::spiro_run(controlpoints, 4, spiro); + pb.append(spiro.portion(1,spiro.size_open()-1), Geom::Path::STITCH_DISCONTINUOUS); + } else { + // we are on the inside, do a simple bevel to connect the paths + pb.lineTo(B[i].at0()); // default to bevel for too shallow cusp angles + } + break; + } + case LINEJOIN_BEVEL: default: pb.lineTo(B[i].at0()); break; @@ -388,13 +493,13 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in) } std::vector<discontinuity_data> cusps = find_discontinuities(der, x, y); - LineCuspType cusp_linecap = static_cast<LineCuspType>(cusp_linecap_type.get_value()); + LineJoinType jointype = static_cast<LineJoinType>(linejoin_type.get_value()); Piecewise<D2<SBasis> > pwd2_out = compose(pwd2_in,x) + y*compose(n,x); Piecewise<D2<SBasis> > mirrorpath = reverse(compose(pwd2_in,x) - y*compose(n,x)); - Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, cusps, cusp_linecap, miter_limit, true, LPE_CONVERSION_TOLERANCE); - Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, cusps, cusp_linecap, miter_limit, false, LPE_CONVERSION_TOLERANCE); + Geom::Path fixed_path = path_from_piecewise_fix_cusps( pwd2_out, cusps, jointype, miter_limit, true, LPE_CONVERSION_TOLERANCE); + Geom::Path fixed_mirrorpath = path_from_piecewise_fix_cusps( mirrorpath, cusps, jointype, miter_limit, false, LPE_CONVERSION_TOLERANCE); if (path_in[0].closed()) { fixed_path.close(true); diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h index a5bb8c836..e6c915234 100644 --- a/src/live_effects/lpe-powerstroke.h +++ b/src/live_effects/lpe-powerstroke.h @@ -39,7 +39,7 @@ private: EnumParam<unsigned> interpolator_type; ScalarParam interpolator_beta; EnumParam<unsigned> start_linecap_type; - EnumParam<unsigned> cusp_linecap_type; + EnumParam<unsigned> linejoin_type; ScalarParam miter_limit; EnumParam<unsigned> end_linecap_type; diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp index 22974fe13..8b4274ab2 100644 --- a/src/live_effects/lpe-spiro.cpp +++ b/src/live_effects/lpe-spiro.cpp @@ -15,8 +15,6 @@ #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" -#include "live_effects/bezctx.h" -#include "live_effects/bezctx_intf.h" #include "live_effects/spiro.h" // For handling un-continuous paths: @@ -24,82 +22,6 @@ #include "inkscape.h" #include "desktop.h" -#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS - -typedef struct { - bezctx base; - SPCurve *curve; - int is_open; -} bezctx_ink; - -void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->curve->moveto(x, y); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe moveto not finite"); - } -#endif -} - -void bezctx_ink_lineto(bezctx *bc, double x, double y) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x) && IS_FINITE(y) ) { - bi->curve->lineto(x, y); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe lineto not finite"); - } -#endif -} - -void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - - if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) { - bi->curve->quadto(xm, ym, x3, y3); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe quadto not finite"); - } -#endif -} - -void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2, - double x3, double y3) -{ - bezctx_ink *bi = (bezctx_ink *) bc; - if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) { - bi->curve->curveto(x1, y1, x2, y2, x3, y3); - } -#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS - else { - g_message("lpe curveto not finite"); - } -#endif -} - -bezctx * -new_bezctx_ink(SPCurve *curve) { - bezctx_ink *result = g_new(bezctx_ink, 1); - result->base.moveto = bezctx_ink_moveto; - result->base.lineto = bezctx_ink_lineto; - result->base.quadto = bezctx_ink_quadto; - result->base.curveto = bezctx_ink_curveto; - result->base.mark_knot = NULL; - result->curve = curve; - return &result->base; -} - - - namespace Inkscape { namespace LivePathEffect { @@ -124,8 +46,7 @@ LPESpiro::doEffect(SPCurve * curve) guint len = curve->get_segment_count() + 2; curve->reset(); - bezctx *bc = new_bezctx_ink(curve); - spiro_cp *path = g_new (spiro_cp, len); + Spiro::spiro_cp *path = g_new (Spiro::spiro_cp, len); int ip = 0; for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { @@ -218,9 +139,7 @@ LPESpiro::doEffect(SPCurve * curve) // run subpath through spiro int sp_len = ip; - spiro_seg *s = run_spiro(path, sp_len); - spiro_to_bpath(s, sp_len, bc); - free(s); + Spiro::spiro_run(path, sp_len, *curve); ip = 0; } diff --git a/src/live_effects/spiro-converters.cpp b/src/live_effects/spiro-converters.cpp new file mode 100644 index 000000000..3c7bdf99e --- /dev/null +++ b/src/live_effects/spiro-converters.cpp @@ -0,0 +1,123 @@ +/* Authors:
+ * Johan Engelen
+ *
+ * Copyright (C) 2010-2012 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "spiro-converters.h"
+#include <2geom/path.h>
+#include "display/curve.h"
+#include <glib.h>
+
+#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+#ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
+# define SPIRO_G_MESSAGE(x) g_message(x)
+#else
+# define SPIRO_G_MESSAGE(x)
+#endif
+
+namespace Spiro {
+
+void
+ConverterSPCurve::moveto(double x, double y, bool is_open)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _curve.moveto(x, y);
+ if (!is_open) {
+ _curve.closepath();
+ }
+ } else {
+ SPIRO_G_MESSAGE("Spiro: moveto not finite");
+ }
+}
+
+void
+ConverterSPCurve::lineto(double x, double y)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _curve.lineto(x, y);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: lineto not finite");
+ }
+}
+
+void
+ConverterSPCurve::quadto(double xm, double ym, double x3, double y3)
+{
+ if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
+ _curve.quadto(xm, ym, x3, y3);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: quadto not finite");
+ }
+}
+
+void
+ConverterSPCurve::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
+ _curve.curveto(x1, y1, x2, y2, x3, y3);
+ } else {
+ SPIRO_G_MESSAGE("Spiro: curveto not finite");
+ }
+}
+
+
+
+
+void
+ConverterPath::moveto(double x, double y, bool is_open)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _path.start(Geom::Point(x, y));
+ _path.close(!is_open);
+ } else {
+ SPIRO_G_MESSAGE("spiro moveto not finite");
+ }
+}
+
+void
+ConverterPath::lineto(double x, double y)
+{
+ if ( IS_FINITE(x) && IS_FINITE(y) ) {
+ _path.appendNew<Geom::LineSegment>( Geom::Point(x, y) );
+ } else {
+ SPIRO_G_MESSAGE("spiro lineto not finite");
+ }
+}
+
+void
+ConverterPath::quadto(double xm, double ym, double x3, double y3)
+{
+ if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
+ _path.appendNew<Geom::QuadraticBezier>(Geom::Point(xm, ym), Geom::Point(x3, y3));
+ } else {
+ SPIRO_G_MESSAGE("spiro quadto not finite");
+ }
+}
+
+void
+ConverterPath::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
+ _path.appendNew<Geom::CubicBezier>(Geom::Point(x1, y1), Geom::Point(x2, y2), Geom::Point(x3, y3));
+ } else {
+ SPIRO_G_MESSAGE("spiro curveto not finite");
+ }
+}
+
+} // namespace Spiro
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/spiro-converters.h b/src/live_effects/spiro-converters.h new file mode 100644 index 000000000..83f6ebbc3 --- /dev/null +++ b/src/live_effects/spiro-converters.h @@ -0,0 +1,67 @@ +#ifndef INKSCAPE_SPIRO_CONVERTERS_H +#define INKSCAPE_SPIRO_CONVERTERS_H + +#include <2geom/forward.h> +class SPCurve; + +namespace Spiro { + +class ConverterBase { +public: + ConverterBase() {}; + virtual ~ConverterBase() {}; + + virtual void moveto(double x, double y, bool is_open) = 0; + virtual void lineto(double x, double y) = 0; + virtual void quadto(double x1, double y1, double x2, double y2) = 0; + virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3) = 0; +}; + + +/** + * Converts Spiro to Inkscape's SPCurve + */ +class ConverterSPCurve : public ConverterBase { +public: + ConverterSPCurve(SPCurve &curve) + : _curve(curve) + {} ; + + virtual void moveto(double x, double y, bool is_open); + virtual void lineto(double x, double y); + virtual void quadto(double x1, double y1, double x2, double y2); + virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3); + +private: + SPCurve &_curve; + + ConverterSPCurve(const ConverterSPCurve&); + ConverterSPCurve& operator=(const ConverterSPCurve&); +}; + + +/** + * Converts Spiro to 2Geom's Path + */ +class ConverterPath : public ConverterBase { +public: + ConverterPath(Geom::Path &path) + : _path(path) + {} ; + + virtual void moveto(double x, double y, bool is_open); + virtual void lineto(double x, double y); + virtual void quadto(double x1, double y1, double x2, double y2); + virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3); + +private: + Geom::Path &_path; + + ConverterPath(const ConverterPath&); + ConverterPath& operator=(const ConverterPath&); +}; + + +} // namespace Spiro + +#endif diff --git a/src/live_effects/spiro.cpp b/src/live_effects/spiro.cpp index b98e12213..e4b72793c 100644 --- a/src/live_effects/spiro.cpp +++ b/src/live_effects/spiro.cpp @@ -1,6 +1,8 @@ /* -ppedit - A pattern plate editor for Spiro splines. -Copyright (C) 2007 Raph Levien +Copyright (C) 2007-2012 Authors + +Authors: Raph Levien + Johan Engelen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -20,12 +22,39 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA */ /* C implementation of third-order polynomial spirals. */ +#include "spiro.h" + #include <math.h> #include <stdlib.h> #include <string.h> -#include "bezctx_intf.h" -#include "spiro.h" +#include "display/curve.h" +#include <2geom/math-utils.h> + +#define SPIRO_SHOW_INFINITE_COORDINATE_CALLS + +namespace Spiro { + +void spiro_run(const spiro_cp *src, int src_len, SPCurve &curve) +{ + spiro_seg *s = Spiro::run_spiro(src, src_len); + Spiro::ConverterSPCurve bc(curve); + Spiro::spiro_to_otherpath(s, src_len, bc); + free(s); +} + +void spiro_run(const spiro_cp *src, int src_len, Geom::Path &path) +{ + spiro_seg *s = Spiro::run_spiro(src, src_len); + Spiro::ConverterPath bc(path); + Spiro::spiro_to_otherpath(s, src_len, bc); + free(s); +} + + +/************************************ + * Spiro math + */ struct spiro_seg_s { double x; @@ -814,15 +843,15 @@ solve_spiro(spiro_seg *s, int nseg) } static void -spiro_seg_to_bpath(const double ks[4], +spiro_seg_to_otherpath(const double ks[4], double x0, double y0, double x1, double y1, - bezctx *bc, int depth) + ConverterBase &bc, int depth) { double bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) + fabs((1./48) * ks[3]); if (!bend > 1e-8) { - bezctx_lineto(bc, x1, y1); + bc.lineto(x1, y1); } else { double seg_ch = hypot(x1 - x0, y1 - y0); double seg_th = atan2(y1 - y0, x1 - x0); @@ -845,7 +874,7 @@ spiro_seg_to_bpath(const double ks[4], vl = (scale * (1./3)) * sin(th_even - th_odd); ur = (scale * (1./3)) * cos(th_even + th_odd); vr = (scale * (1./3)) * sin(th_even + th_odd); - bezctx_curveto(bc, x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); + bc.curveto(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1); } else { /* subdivide */ double ksub[4]; @@ -864,11 +893,11 @@ spiro_seg_to_bpath(const double ks[4], integrate_spiro(ksub, xysub); xmid = x0 + cth * xysub[0] - sth * xysub[1]; ymid = y0 + cth * xysub[1] + sth * xysub[0]; - spiro_seg_to_bpath(ksub, x0, y0, xmid, ymid, bc, depth + 1); + spiro_seg_to_otherpath(ksub, x0, y0, xmid, ymid, bc, depth + 1); ksub[0] += .25 * ks[1] + (1./384) * ks[3]; ksub[1] += .125 * ks[2]; ksub[2] += (1./16) * ks[3]; - spiro_seg_to_bpath(ksub, xmid, ymid, x1, y1, bc, depth + 1); + spiro_seg_to_otherpath(ksub, xmid, ymid, x1, y1, bc, depth + 1); } } } @@ -890,7 +919,7 @@ free_spiro(spiro_seg *s) } void -spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc) +spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc) { int i; int nsegs = s[n - 1].ty == '}' ? n - 1 : n; @@ -901,10 +930,10 @@ spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc) double x1 = s[i + 1].x; double y1 = s[i + 1].y; - if (i == 0) - bezctx_moveto(bc, x0, y0, s[0].ty == '{'); - bezctx_mark_knot(bc, i); - spiro_seg_to_bpath(s[i].ks, x0, y0, x1, y1, bc, 0); + if (i == 0) { + bc.moveto(x0, y0, s[0].ty == '{'); + } + spiro_seg_to_otherpath(s[i].ks, x0, y0, x1, y1, bc, 0); } } @@ -922,10 +951,20 @@ get_knot_th(const spiro_seg *s, int i) } } + +} // namespace Spiro + +/************************************ + * Unit_test code + */ + + #ifdef UNIT_TEST #include <stdio.h> #include <sys/time.h> /* for gettimeofday */ +using namespace Spiro; + static double get_time (void) { diff --git a/src/live_effects/spiro.h b/src/live_effects/spiro.h index 54de40465..0d85da74b 100644 --- a/src/live_effects/spiro.h +++ b/src/live_effects/spiro.h @@ -1,18 +1,56 @@ +/* +Copyright (C) 2007-2012 Authors + +Authors: Raph Levien + Johan Engelen + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ + +#ifndef INKSCAPE_SPIRO_H +#define INKSCAPE_SPIRO_H + +#include "live_effects/spiro-converters.h" + +class SPCurve; +namespace Geom { + class Path; +} + +namespace Spiro { + typedef struct { double x; double y; char ty; } spiro_cp; -typedef struct spiro_seg_s spiro_seg; -spiro_seg * -run_spiro(const spiro_cp *src, int n); +void spiro_run(const spiro_cp *src, int src_len, SPCurve &curve); +void spiro_run(const spiro_cp *src, int src_len, Geom::Path &path); -void -free_spiro(spiro_seg *s); +/* the following methods are only for expert use: */ +typedef struct spiro_seg_s spiro_seg; +spiro_seg * run_spiro(const spiro_cp *src, int n); +void free_spiro(spiro_seg *s); +void spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc); +double get_knot_th(const spiro_seg *s, int i); -void -spiro_to_bpath(const spiro_seg *s, int n, bezctx *bc); -double get_knot_th(const spiro_seg *s, int i); +} // namespace Spiro + +#endif // INKSCAPE_SPIRO_H
\ No newline at end of file diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index e21c3795f..bfd8cca95 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -25,6 +25,7 @@ set(ui_SRC dialog/align-and-distribute.cpp dialog/calligraphic-profile-rename.cpp dialog/color-item.cpp + dialog/clonetiler.cpp dialog/debug.cpp dialog/desktop-tracker.cpp dialog/dialog-manager.cpp @@ -135,6 +136,7 @@ set(ui_SRC dialog/behavior.h dialog/calligraphic-profile-rename.h dialog/color-item.h + dialog/clonetiler.h dialog/debug.h dialog/desktop-tracker.h dialog/dialog-manager.h diff --git a/src/ui/context-menu.cpp b/src/ui/context-menu.cpp index bf116f5fb..9e5c65af9 100644 --- a/src/ui/context-menu.cpp +++ b/src/ui/context-menu.cpp @@ -1,679 +1,693 @@ -/*
- * Unser-interface related object extension
- *
- * Authors:
- * Lauris Kaplinski <lauris@kaplinski.com>
- * Jon A. Cruz <jon@joncruz.org>
- * Abhishek Sharma
- *
- * This code is in public domain
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "ui/dialog/dialog-manager.h"
-#include "context-menu.h"
-#include "../xml/repr.h"
-#include "desktop.h"
-#include "document.h"
-#include "document-undo.h"
-#include "helper/action.h" //sp_action_perform
-#include "inkscape.h"
-#include "message-stack.h"
-#include "preferences.h"
-#include "verbs.h"
-
-using Inkscape::DocumentUndo;
-
-static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-
-/* Append object-specific part to context menu */
-
-void sp_object_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
-{
- GObjectClass *klass;
- klass = G_OBJECT_GET_CLASS(object);
- while (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_OBJECT)) {
- GType type;
- type = G_TYPE_FROM_CLASS(klass);
- sp_object_type_menu(type, object, desktop, menu);
- klass = (GObjectClass*)g_type_class_peek_parent(klass);
- }
-}
-
-/* Implementation */
-
-#include <gtk/gtk.h>
-#include <glibmm/i18n.h>
-
-#include "selection.h"
-#include "selection-chemistry.h"
-#include "sp-anchor.h"
-#include "sp-clippath.h"
-#include "sp-image.h"
-#include "sp-mask.h"
-#include "sp-path.h"
-#include "sp-text.h"
-#include "desktop-handles.h"
-#include "ui/dialog/object-attributes.h"
-#include "ui/dialog/object-properties.h"
-#include "ui/dialog/spellcheck.h"
-
-
-static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
-
-static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu)
-{
- static GHashTable *t2m = NULL;
- void (* handler)(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
- if (!t2m) {
- t2m = g_hash_table_new(NULL, NULL);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ITEM), (void*)sp_item_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_GROUP), (void*)sp_group_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ANCHOR), (void*)sp_anchor_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_IMAGE), (void*)sp_image_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_SHAPE), (void*)sp_shape_menu);
- g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_TEXT), (void*)sp_text_menu);
- }
- handler = (void (*)(SPObject*, SPDesktop*, GtkMenu*))g_hash_table_lookup(t2m, GUINT_TO_POINTER(type));
- if (handler) handler(object, desktop, menu);
-}
-
-/* SPItem */
-
-static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item);
-static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item);
-static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item);
-static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item);
-static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item);
-static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item);
-static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item);
-
-/* Generate context menu item section */
-static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Item dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Object Properties..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_properties), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Separator */
- w = gtk_menu_item_new();
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Select item */
- w = gtk_menu_item_new_with_mnemonic(_("_Select This"));
- if (sp_desktop_selection(desktop)->includes(item)) {
- gtk_widget_set_sensitive(w, FALSE);
- } else {
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_select_this), item);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Create link */
- w = gtk_menu_item_new_with_mnemonic(_("_Create Link"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_create_link), item);
- gtk_widget_set_sensitive(w, !SP_IS_ANCHOR(item));
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Set mask */
- w = gtk_menu_item_new_with_mnemonic(_("Set Mask"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_mask), item);
- if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
- gtk_widget_set_sensitive(w, FALSE);
- } else {
- gtk_widget_set_sensitive(w, TRUE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Release mask */
- w = gtk_menu_item_new_with_mnemonic(_("Release Mask"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_mask), item);
- if (item && item->mask_ref && item->mask_ref->getObject()) {
- gtk_widget_set_sensitive(w, TRUE);
- } else {
- gtk_widget_set_sensitive(w, FALSE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Set Clip */
- w = gtk_menu_item_new_with_mnemonic(_("Set _Clip"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_clip), item);
- if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
- gtk_widget_set_sensitive(w, FALSE);
- } else {
- gtk_widget_set_sensitive(w, TRUE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Release Clip */
- w = gtk_menu_item_new_with_mnemonic(_("Release C_lip"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_clip), item);
- if (item && item->clip_ref && item->clip_ref->getObject()) {
- gtk_widget_set_sensitive(w, TRUE);
- } else {
- gtk_widget_set_sensitive(w, FALSE);
- }
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
-}
-
-static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_desktop_selection(desktop)->set(item);
-
- // sp_item_dialog();
- desktop->_dlg_mgr->showDialog("ObjectProperties");
-}
-
-
-static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_set_mask(desktop, false, false);
-}
-
-
-static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_unset_mask(desktop, false);
-}
-
-
-static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_set_mask(desktop, true, false);
-}
-
-
-static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_selection_unset_mask(desktop, true);
-}
-
-
-static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- sp_desktop_selection(desktop)->set(item);
-}
-
-static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item)
-{
- g_assert(SP_IS_ITEM(item));
- g_assert(!SP_IS_ANCHOR(item));
-
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc();
- Inkscape::XML::Node *repr = xml_doc->createElement("svg:a");
- item->parent->getRepr()->addChild(repr, item->getRepr());
- SPObject *object = item->document->getObjectByRepr(repr);
- g_return_if_fail(SP_IS_ANCHOR(object));
-
- const char *id = item->getRepr()->attribute("id");
- Inkscape::XML::Node *child = item->getRepr()->duplicate(xml_doc);
- item->deleteObject(false);
- repr->addChild(child, NULL);
- child->setAttribute("id", id);
-
- Inkscape::GC::release(repr);
- Inkscape::GC::release(child);
-
- DocumentUndo::done(object->document, SP_VERB_NONE,
- _("Create link"));
-
- sp_desktop_selection(desktop)->set(SP_ITEM(object));
- desktop->_dlg_mgr->showDialog("ObjectAttributes");
-}
-
-/* SPGroup */
-
-static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group);
-
-static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
-{
- SPItem *item=SP_ITEM(object);
- GtkWidget *w;
-
- /* "Ungroup" */
- w = gtk_menu_item_new_with_mnemonic(_("_Ungroup"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_group_ungroup_activate), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), w);
-}
-
-static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group)
-{
- SPDesktop *desktop;
- GSList *children;
-
- g_assert(SP_IS_GROUP(group));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- children = NULL;
- sp_item_group_ungroup(group, &children);
-
- sp_desktop_selection(desktop)->setList(children);
- g_slist_free(children);
-}
-
-/* SPAnchor */
-
-static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_anchor_link_follow(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_anchor_link_remove(GtkMenuItem *menuitem, SPAnchor *anchor);
-
-static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Link dialog */
- w = gtk_menu_item_new_with_mnemonic(_("Link _Properties..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_properties), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Select item */
- w = gtk_menu_item_new_with_mnemonic(_("_Follow Link"));
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_follow), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- /* Reset transformations */
- w = gtk_menu_item_new_with_mnemonic(_("_Remove Link"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_remove), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-}
-
-static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/)
-{
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
- desktop->_dlg_mgr->showDialog("ObjectAttributes");
-}
-
-static void sp_anchor_link_follow(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
-{
- g_return_if_fail(anchor != NULL);
- g_return_if_fail(SP_IS_ANCHOR(anchor));
-
- /* shell out to an external browser here */
-}
-
-static void sp_anchor_link_remove(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
-{
- GSList *children;
-
- g_return_if_fail(anchor != NULL);
- g_return_if_fail(SP_IS_ANCHOR(anchor));
-
- children = NULL;
- sp_item_group_ungroup(SP_GROUP(anchor), &children);
-
- g_slist_free(children);
-}
-
-/* Image */
-
-static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_image_image_edit(GtkMenuItem *menuitem, SPAnchor *anchor);
-static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item);
-static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item);
-
-static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item = SP_ITEM(object);
- GtkWidget *w;
- Inkscape::XML::Node *ir = object->getRepr();
- const gchar *href = ir->attribute("xlink:href");
-
- /* Image properties */
- w = gtk_menu_item_new_with_mnemonic(_("Image _Properties..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_properties), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
- /* Edit externally */
- w = gtk_menu_item_new_with_mnemonic(_("Edit Externally..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_edit), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
- gtk_widget_set_sensitive( w, FALSE );
- }
-
- /* Embed image */
- if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) {
- w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Embed Image"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_embed), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
- gtk_widget_set_sensitive( w, FALSE );
- }
- }
-
- /* Extract image */
- if (Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" )) {
- w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Extract Image"));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_extract), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
- if ( (!href) || ((strncmp(href, "data:", 5) != 0)) ) {
- gtk_widget_set_sensitive( w, FALSE );
- }
- }
-}
-
-/* Image Properties entry */
-static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/)
-{
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
- desktop->_dlg_mgr->showDialog("ObjectAttributes");
-}
-
-static gchar* getImageEditorName() {
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- gchar* value = 0;
- Glib::ustring choices = prefs->getString("/options/bitmapeditor/value");
- if (!choices.empty()) {
- value = g_strdup(choices.c_str());
- }
- if (!value) {
- value = g_strdup("gimp");
- }
- return value;
-}
-
-/* Edit Externally entry */
-static void sp_image_image_edit(GtkMenuItem *menuitem, SPAnchor *anchor)
-{
- SPObject* obj = anchor;
- Inkscape::XML::Node *ir = obj->getRepr();
- const gchar *href = ir->attribute("xlink:href");
-
- GError* errThing = 0;
- Glib::ustring cmdline = getImageEditorName();
- Glib::ustring name;
- Glib::ustring fullname;
-
-#ifdef WIN32
- // g_spawn_command_line_sync parsing is done according to Unix shell rules,
- // not Windows command interpreter rules. Thus we need to enclose the
- // executable path with sigle quotes.
- int index = cmdline.find(".exe");
- if ( index < 0 ) index = cmdline.find(".bat");
- if ( index < 0 ) index = cmdline.find(".com");
- if ( index >= 0 ) {
- Glib::ustring editorBin = cmdline.substr(0, index + 4).c_str();
- Glib::ustring args = cmdline.substr(index + 4, cmdline.length()).c_str();
- editorBin.insert(0, "'");
- editorBin.append("'");
- cmdline = editorBin;
- cmdline.append(args);
- } else {
- // Enclose the whole command line if no executable path can be extracted.
- cmdline.insert(0, "'");
- cmdline.append("'");
- }
-#endif
-
- if (strncmp (href,"file:",5) == 0) {
- // URI to filename conversion
- name = g_filename_from_uri(href, NULL, NULL);
- } else {
- name.append(href);
- }
-
- if (Glib::path_is_absolute(name)) {
- fullname = name;
- } else if (SP_ACTIVE_DOCUMENT->getBase()) {
- fullname = Glib::build_filename(SP_ACTIVE_DOCUMENT->getBase(), name);
- } else {
- fullname = Glib::build_filename(Glib::get_current_dir(), name);
- }
-
- cmdline.append(" '");
- cmdline.append(fullname.c_str());
- cmdline.append("'");
-
- //g_warning("##Command line: %s\n", cmdline.c_str());
-
- g_spawn_command_line_async(cmdline.c_str(),
- &errThing);
-
- if ( errThing ) {
- g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message);
- SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, errThing->message);
- g_error_free(errThing);
- errThing = 0;
- }
-}
-
-/* Embed Image entry */
-static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" );
- if (verb) {
- SPAction *action = verb->get_action(desktop);
- if (action) {
- sp_action_perform(action, NULL);
- }
- }
-}
-
-/* Extract Image entry */
-static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" );
- if (verb) {
- SPAction *action = verb->get_action(desktop);
- if (action) {
- sp_action_perform(action, NULL);
- }
- }
-}
-
-/* Fill and Stroke entry */
-static void sp_fill_settings(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- desktop->_dlg_mgr->showDialog("FillAndStroke");
-}
-
-/* SPShape */
-static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Item dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-}
-
-/* Edit Text entry */
-static void sp_text_settings(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- desktop->_dlg_mgr->showDialog("TextFont");
-}
-
-/* Spellcheck entry */
-static void sp_spellcheck_settings(GtkMenuItem *menuitem, SPItem *item)
-{
- SPDesktop *desktop;
-
- g_assert(SP_IS_ITEM(item));
-
- desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop");
- g_return_if_fail(desktop != NULL);
-
- if (sp_desktop_selection(desktop)->isEmpty()) {
- sp_desktop_selection(desktop)->set(item);
- }
-
- desktop->_dlg_mgr->showDialog("SpellCheck");
-}
-
-/* SPText */
-static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
-{
- SPItem *item;
- GtkWidget *w;
-
- item = (SPItem *) object;
-
- /* Fill and Stroke dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
- /* Edit Text dialog */
- w = gtk_menu_item_new_with_mnemonic(_("_Text and Font..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_text_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-
- /* Spellcheck dialog */
- w = gtk_menu_item_new_with_mnemonic(_("Check Spellin_g..."));
- g_object_set_data(G_OBJECT(w), "desktop", desktop);
- g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_spellcheck_settings), item);
- gtk_widget_show(w);
- gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
-}
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+/* + * Unser-interface related object extension + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> + * Abhishek Sharma + * + * This code is in public domain + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "ui/dialog/dialog-manager.h" +#include "context-menu.h" +#include "../xml/repr.h" +#include "desktop.h" +#include "document.h" +#include "document-undo.h" +#include "helper/action.h" //sp_action_perform +#include "inkscape.h" +#include "message-stack.h" +#include "preferences.h" +#include "verbs.h" + +using Inkscape::DocumentUndo; + +static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu); + +/* Append object-specific part to context menu */ + +void sp_object_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu) +{ + GObjectClass *klass; + klass = G_OBJECT_GET_CLASS(object); + while (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_OBJECT)) { + GType type; + type = G_TYPE_FROM_CLASS(klass); + sp_object_type_menu(type, object, desktop, menu); + klass = (GObjectClass*)g_type_class_peek_parent(klass); + } +} + +/* Implementation */ + +#include <gtk/gtk.h> +#include <glibmm/i18n.h> + +#include "selection.h" +#include "selection-chemistry.h" +#include "sp-anchor.h" +#include "sp-clippath.h" +#include "sp-image.h" +#include "sp-mask.h" +#include "sp-path.h" +#include "sp-text.h" +#include "desktop-handles.h" +#include "ui/dialog/object-attributes.h" +#include "ui/dialog/object-properties.h" +#include "ui/dialog/spellcheck.h" + + +static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu); +static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu); +static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu); +static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu); +static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu); +static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu); + +static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu) +{ + static GHashTable *t2m = NULL; + void (* handler)(SPObject *object, SPDesktop *desktop, GtkMenu *menu); + if (!t2m) { + t2m = g_hash_table_new(NULL, NULL); + g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ITEM), (void*)sp_item_menu); + g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_GROUP), (void*)sp_group_menu); + g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ANCHOR), (void*)sp_anchor_menu); + g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_IMAGE), (void*)sp_image_menu); + g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_SHAPE), (void*)sp_shape_menu); + g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_TEXT), (void*)sp_text_menu); + } + handler = (void (*)(SPObject*, SPDesktop*, GtkMenu*))g_hash_table_lookup(t2m, GUINT_TO_POINTER(type)); + if (handler) handler(object, desktop, menu); +} + +/* SPItem */ + +static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item); +static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item); +static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item); +static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item); +static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item); +static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item); +static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item); + +/* Generate context menu item section */ +static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m) +{ + SPItem *item; + GtkWidget *w; + + item = (SPItem *) object; + + /* Item dialog */ + w = gtk_menu_item_new_with_mnemonic(_("_Object Properties...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_properties), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Separator */ + w = gtk_menu_item_new(); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Select item */ + w = gtk_menu_item_new_with_mnemonic(_("_Select This")); + if (sp_desktop_selection(desktop)->includes(item)) { + gtk_widget_set_sensitive(w, FALSE); + } else { + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_select_this), item); + } + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Create link */ + w = gtk_menu_item_new_with_mnemonic(_("_Create Link")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_create_link), item); + gtk_widget_set_sensitive(w, !SP_IS_ANCHOR(item)); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Set mask */ + w = gtk_menu_item_new_with_mnemonic(_("Set Mask")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_mask), item); + if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) { + gtk_widget_set_sensitive(w, FALSE); + } else { + gtk_widget_set_sensitive(w, TRUE); + } + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Release mask */ + w = gtk_menu_item_new_with_mnemonic(_("Release Mask")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_mask), item); + if (item && item->mask_ref && item->mask_ref->getObject()) { + gtk_widget_set_sensitive(w, TRUE); + } else { + gtk_widget_set_sensitive(w, FALSE); + } + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Set Clip */ + w = gtk_menu_item_new_with_mnemonic(_("Set _Clip")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_set_clip), item); + if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) { + gtk_widget_set_sensitive(w, FALSE); + } else { + gtk_widget_set_sensitive(w, TRUE); + } + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Release Clip */ + w = gtk_menu_item_new_with_mnemonic(_("Release C_lip")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_release_clip), item); + if (item && item->clip_ref && item->clip_ref->getObject()) { + gtk_widget_set_sensitive(w, TRUE); + } else { + gtk_widget_set_sensitive(w, FALSE); + } + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + +} + +static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + sp_desktop_selection(desktop)->set(item); + + // sp_item_dialog(); + desktop->_dlg_mgr->showDialog("ObjectProperties"); +} + + +static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + sp_selection_set_mask(desktop, false, false); +} + + +static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + sp_selection_unset_mask(desktop, false); +} + + +static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + sp_selection_set_mask(desktop, true, false); +} + + +static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + sp_selection_unset_mask(desktop, true); +} + + +static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + sp_desktop_selection(desktop)->set(item); +} + +static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item) +{ + g_assert(SP_IS_ITEM(item)); + g_assert(!SP_IS_ANCHOR(item)); + + SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:a"); + item->parent->getRepr()->addChild(repr, item->getRepr()); + SPObject *object = item->document->getObjectByRepr(repr); + g_return_if_fail(SP_IS_ANCHOR(object)); + + const char *id = item->getRepr()->attribute("id"); + Inkscape::XML::Node *child = item->getRepr()->duplicate(xml_doc); + item->deleteObject(false); + repr->addChild(child, NULL); + child->setAttribute("id", id); + + Inkscape::GC::release(repr); + Inkscape::GC::release(child); + + DocumentUndo::done(object->document, SP_VERB_NONE, + _("Create link")); + + sp_desktop_selection(desktop)->set(SP_ITEM(object)); + desktop->_dlg_mgr->showDialog("ObjectAttributes"); +} + +/* SPGroup */ + +static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group); + +static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu) +{ + SPItem *item=SP_ITEM(object); + GtkWidget *w; + + /* "Ungroup" */ + w = gtk_menu_item_new_with_mnemonic(_("_Ungroup")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_item_group_ungroup_activate), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), w); +} + +static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group) +{ + SPDesktop *desktop; + GSList *children; + + g_assert(SP_IS_GROUP(group)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + children = NULL; + sp_item_group_ungroup(group, &children); + + sp_desktop_selection(desktop)->setList(children); + g_slist_free(children); +} + +/* SPAnchor */ + +static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor *anchor); +static void sp_anchor_link_follow(GtkMenuItem *menuitem, SPAnchor *anchor); +static void sp_anchor_link_remove(GtkMenuItem *menuitem, SPAnchor *anchor); + +static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m) +{ + SPItem *item; + GtkWidget *w; + + item = (SPItem *) object; + + /* Link dialog */ + w = gtk_menu_item_new_with_mnemonic(_("Link _Properties...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_properties), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Select item */ + w = gtk_menu_item_new_with_mnemonic(_("_Follow Link")); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_follow), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + /* Reset transformations */ + w = gtk_menu_item_new_with_mnemonic(_("_Remove Link")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_anchor_link_remove), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); +} + +static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/) +{ + SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + desktop->_dlg_mgr->showDialog("ObjectAttributes"); +} + +static void sp_anchor_link_follow(GtkMenuItem */*menuitem*/, SPAnchor *anchor) +{ + g_return_if_fail(anchor != NULL); + g_return_if_fail(SP_IS_ANCHOR(anchor)); + + /* shell out to an external browser here */ +} + +static void sp_anchor_link_remove(GtkMenuItem */*menuitem*/, SPAnchor *anchor) +{ + GSList *children; + + g_return_if_fail(anchor != NULL); + g_return_if_fail(SP_IS_ANCHOR(anchor)); + + children = NULL; + sp_item_group_ungroup(SP_GROUP(anchor), &children); + + g_slist_free(children); +} + +/* Image */ + +static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor *anchor); +static void sp_image_image_edit(GtkMenuItem *menuitem, SPItem *item); +static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item); +static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item); + +static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m) +{ + SPItem *item = SP_ITEM(object); + GtkWidget *w; + Inkscape::XML::Node *ir = object->getRepr(); + const gchar *href = ir->attribute("xlink:href"); + + /* Image properties */ + w = gtk_menu_item_new_with_mnemonic(_("Image _Properties...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_properties), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + + /* Edit externally */ + w = gtk_menu_item_new_with_mnemonic(_("Edit Externally...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_edit), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) { + gtk_widget_set_sensitive( w, FALSE ); + } + + /* Embed image */ + if (Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" )) { + w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Embed Image")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_embed), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) { + gtk_widget_set_sensitive( w, FALSE ); + } + } + + /* Extract image */ + if (Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" )) { + w = gtk_menu_item_new_with_mnemonic(C_("Context menu", "Extract Image...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_image_image_extract), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + if ( (!href) || ((strncmp(href, "data:", 5) != 0)) ) { + gtk_widget_set_sensitive( w, FALSE ); + } + } +} + +/* Image Properties entry */ +static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor */*anchor*/) +{ + SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + desktop->_dlg_mgr->showDialog("ObjectAttributes"); +} + +static gchar* getImageEditorName() { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gchar* value = 0; + Glib::ustring choices = prefs->getString("/options/bitmapeditor/value"); + if (!choices.empty()) { + value = g_strdup(choices.c_str()); + } + if (!value) { + value = g_strdup("gimp"); + } + return value; +} + +/* Edit Externally entry */ +static void sp_image_image_edit(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop = NULL; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + if (sp_desktop_selection(desktop)->isEmpty()) { + sp_desktop_selection(desktop)->set(item); + } + + GSList const *selected = sp_desktop_selection(desktop)->itemList(); + + GError* errThing = 0; + Glib::ustring cmdline = getImageEditorName(); + Glib::ustring name; + Glib::ustring fullname; + +#ifdef WIN32 + // g_spawn_command_line_sync parsing is done according to Unix shell rules, + // not Windows command interpreter rules. Thus we need to enclose the + // executable path with sigle quotes. + int index = cmdline.find(".exe"); + if ( index < 0 ) index = cmdline.find(".bat"); + if ( index < 0 ) index = cmdline.find(".com"); + if ( index >= 0 ) { + Glib::ustring editorBin = cmdline.substr(0, index + 4).c_str(); + Glib::ustring args = cmdline.substr(index + 4, cmdline.length()).c_str(); + editorBin.insert(0, "'"); + editorBin.append("'"); + cmdline = editorBin; + cmdline.append(args); + } else { + // Enclose the whole command line if no executable path can be extracted. + cmdline.insert(0, "'"); + cmdline.append("'"); + } +#endif + + for (GSList const *iter = selected; iter != NULL; iter = iter->next) { + Inkscape::XML::Node *ir = SP_ITEM(iter->data)->getRepr(); + const gchar *href = ir->attribute("xlink:href"); + + if (strncmp (href,"file:",5) == 0) { + // URI to filename conversion + name = g_filename_from_uri(href, NULL, NULL); + } else { + name.append(href); + } + + if (Glib::path_is_absolute(name)) { + fullname = name; + } else if (SP_ACTIVE_DOCUMENT->getBase()) { + fullname = Glib::build_filename(SP_ACTIVE_DOCUMENT->getBase(), name); + } else { + fullname = Glib::build_filename(Glib::get_current_dir(), name); + } + + cmdline.append(" '"); + cmdline.append(fullname.c_str()); + cmdline.append("'"); + } + + //g_warning("##Command line: %s\n", cmdline.c_str()); + + g_spawn_command_line_async(cmdline.c_str(), + &errThing); + + if ( errThing ) { + g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message); + SPDesktop *desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, errThing->message); + g_error_free(errThing); + errThing = 0; + } +} + +/* Embed Image entry */ +static void sp_image_image_embed(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + if (sp_desktop_selection(desktop)->isEmpty()) { + sp_desktop_selection(desktop)->set(item); + } + + Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.embedselectedimages" ); + if (verb) { + SPAction *action = verb->get_action(desktop); + if (action) { + sp_action_perform(action, NULL); + } + } +} + +/* Extract Image entry */ +static void sp_image_image_extract(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + if (sp_desktop_selection(desktop)->isEmpty()) { + sp_desktop_selection(desktop)->set(item); + } + + Inkscape::Verb *verb = Inkscape::Verb::getbyid( "org.ekips.filter.extractimage" ); + if (verb) { + SPAction *action = verb->get_action(desktop); + if (action) { + sp_action_perform(action, NULL); + } + } +} + +/* Fill and Stroke entry */ +static void sp_fill_settings(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + if (sp_desktop_selection(desktop)->isEmpty()) { + sp_desktop_selection(desktop)->set(item); + } + + desktop->_dlg_mgr->showDialog("FillAndStroke"); +} + +/* SPShape */ +static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m) +{ + SPItem *item; + GtkWidget *w; + + item = (SPItem *) object; + + /* Item dialog */ + w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); +} + +/* Edit Text entry */ +static void sp_text_settings(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + if (sp_desktop_selection(desktop)->isEmpty()) { + sp_desktop_selection(desktop)->set(item); + } + + desktop->_dlg_mgr->showDialog("TextFont"); +} + +/* Spellcheck entry */ +static void sp_spellcheck_settings(GtkMenuItem *menuitem, SPItem *item) +{ + SPDesktop *desktop; + + g_assert(SP_IS_ITEM(item)); + + desktop = (SPDesktop*)g_object_get_data(G_OBJECT(menuitem), "desktop"); + g_return_if_fail(desktop != NULL); + + if (sp_desktop_selection(desktop)->isEmpty()) { + sp_desktop_selection(desktop)->set(item); + } + + desktop->_dlg_mgr->showDialog("SpellCheck"); +} + +/* SPText */ +static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m) +{ + SPItem *item; + GtkWidget *w; + + item = (SPItem *) object; + + /* Fill and Stroke dialog */ + w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_fill_settings), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + + /* Edit Text dialog */ + w = gtk_menu_item_new_with_mnemonic(_("_Text and Font...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_text_settings), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); + + /* Spellcheck dialog */ + w = gtk_menu_item_new_with_mnemonic(_("Check Spellin_g...")); + g_object_set_data(G_OBJECT(w), "desktop", desktop); + g_signal_connect(G_OBJECT(w), "activate", G_CALLBACK(sp_spellcheck_settings), item); + gtk_widget_show(w); + gtk_menu_shell_append(GTK_MENU_SHELL(m), w); +} +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index 91b1ccaf6..f8b95247a 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -8,6 +8,8 @@ ink_common_sources += \ ui/dialog/behavior.h \ ui/dialog/calligraphic-profile-rename.h \ ui/dialog/calligraphic-profile-rename.cpp \ + ui/dialog/clonetiler.cpp \ + ui/dialog/clonetiler.h \ ui/dialog/color-item.cpp \ ui/dialog/color-item.h \ ui/dialog/debug.cpp \ diff --git a/src/dialogs/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index c56abc21a..36e79a682 100644 --- a/src/dialogs/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -1,3 +1,4 @@ + /** @file * Clone tiling dialog */ @@ -16,9 +17,9 @@ # include "config.h" #endif -#include <climits> +#include "clonetiler.h" -#include "ui/widget/color-picker.h" +#include <climits> #include <glib.h> #include <glibmm/i18n.h> @@ -27,7 +28,6 @@ #include "desktop.h" #include "desktop-handles.h" -#include "dialog-events.h" #include "display/cairo-utils.h" #include "display/drawing.h" #include "display/drawing-context.h" @@ -58,81 +58,1175 @@ #include "xml/repr.h" #include "sp-root.h" -#include <gtk/gtk.h> - using Inkscape::DocumentUndo; -#define MIN_ONSCREEN_DISTANCE 50 +namespace Inkscape { +namespace UI { +namespace Dialog { -static GtkWidget *dlg = NULL; -static win_data wd; +#define SB_MARGIN 1 +#define VB_MARGIN 4 -// impossible original values to make sure they are read from prefs -static gint x = -1000, y = -1000, w = 0, h = 0; static Glib::ustring const prefs_path = "/dialogs/clonetiler/"; -#define SB_MARGIN 1 -#define VB_MARGIN 4 +static Inkscape::Drawing *trace_drawing = NULL; +static unsigned trace_visionkey; +static gdouble trace_zoom; +static SPDocument *trace_doc = NULL; -enum { - PICK_COLOR, - PICK_OPACITY, - PICK_R, - PICK_G, - PICK_B, - PICK_H, - PICK_S, - PICK_L -}; -static GtkSizeGroup* table_row_labels = NULL; +CloneTiler::CloneTiler (void) : + UI::Widget::Panel ("", "/dialogs/clonetiler/", SP_VERB_DIALOG_CLONETILER), + dlg(NULL), + desktop(NULL), + deskTrack(), + table_row_labels(NULL), + selectChangedConn(), + subselChangedConn(), + selectModifiedConn() +{ + Gtk::Box *contents = _getContents(); + contents->set_spacing(0); -static sigc::connection _shutdown_connection; -static sigc::connection _dialogs_hidden_connection; -static sigc::connection _dialogs_unhidden_connection; -static sigc::connection _desktop_activated_connection; -static sigc::connection _color_changed_connection; + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -static Inkscape::UI::Widget::ColorPicker *color_picker; + dlg = GTK_WIDGET(gobj()); -static void clonetiler_dialog_destroy(GtkObject */*object*/, gpointer /*data*/) -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setInt(prefs_path + "visible", 0); + GtkWidget *mainbox = gtk_vbox_new(FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER (mainbox), 6); - sp_signal_disconnect_by_data (INKSCAPE, dlg); - _color_changed_connection.disconnect(); + contents->pack_start (*Gtk::manage(Glib::wrap(mainbox)), true, true, 0); - delete color_picker; + GtkWidget *nb = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX (mainbox), nb, FALSE, FALSE, 0); - wd.win = dlg = NULL; - wd.stop = 0; -} + // Symmetry + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Symmetry")); -static gboolean clonetiler_dialog_delete(GtkObject */*object*/, GdkEvent * /*event*/, gpointer /*data*/) -{ - gtk_window_get_position ((GtkWindow *) dlg, &x, &y); - gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + /* TRANSLATORS: For the following 17 symmetry groups, see + * http://www.bib.ulb.ac.be/coursmath/doc/17.htm (visual examples); + * http://www.clarku.edu/~djoyce/wallpaper/seventeen.html (English vocabulary); or + * http://membres.lycos.fr/villemingerard/Geometri/Sym1D.htm (French vocabulary). + */ + struct SymGroups { + gint group; + gchar const *label; + } const sym_groups[] = { + // TRANSLATORS: "translation" means "shift" / "displacement" here. + {TILE_P1, _("<b>P1</b>: simple translation")}, + {TILE_P2, _("<b>P2</b>: 180° rotation")}, + {TILE_PM, _("<b>PM</b>: reflection")}, + // TRANSLATORS: "glide reflection" is a reflection and a translation combined. + // For more info, see http://mathforum.org/sum95/suzanne/symsusan.html + {TILE_PG, _("<b>PG</b>: glide reflection")}, + {TILE_CM, _("<b>CM</b>: reflection + glide reflection")}, + {TILE_PMM, _("<b>PMM</b>: reflection + reflection")}, + {TILE_PMG, _("<b>PMG</b>: reflection + 180° rotation")}, + {TILE_PGG, _("<b>PGG</b>: glide reflection + 180° rotation")}, + {TILE_CMM, _("<b>CMM</b>: reflection + reflection + 180° rotation")}, + {TILE_P4, _("<b>P4</b>: 90° rotation")}, + {TILE_P4M, _("<b>P4M</b>: 90° rotation + 45° reflection")}, + {TILE_P4G, _("<b>P4G</b>: 90° rotation + 90° reflection")}, + {TILE_P3, _("<b>P3</b>: 120° rotation")}, + {TILE_P31M, _("<b>P31M</b>: reflection + 120° rotation, dense")}, + {TILE_P3M1, _("<b>P3M1</b>: reflection + 120° rotation, sparse")}, + {TILE_P6, _("<b>P6</b>: 60° rotation")}, + {TILE_P6M, _("<b>P6M</b>: reflection + 60° rotation")}, + }; + + gint current = prefs->getInt(prefs_path + "symmetrygroup", 0); + + // Create a list structure containing all the data to be displayed in + // the symmetry group combo box. + GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); + GtkTreeIter iter; + + for (unsigned j = 0; j < G_N_ELEMENTS(sym_groups); ++j) { + SymGroups const &sg = sym_groups[j]; + + // Add the description of the symgroup to a new row + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, sg.label, -1); + } + + // Add a new combo box widget with the list of symmetry groups to the vbox + GtkWidget *combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + gtk_widget_set_tooltip_text (combo, _("Select one of the 17 symmetry groups for the tiling")); + gtk_box_pack_start (GTK_BOX (vb), combo, FALSE, FALSE, SB_MARGIN); + + // Specify the rendering of data from the list in a combo box cell + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, "markup", 0, NULL); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), current); + + g_signal_connect(G_OBJECT(combo), "changed", + G_CALLBACK(clonetiler_symgroup_changed), NULL); + } + + table_row_labels = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + // Shift + { + GtkWidget *vb = clonetiler_new_tab (nb, _("S_hift")); + + GtkWidget *table = clonetiler_table_x_y_rand (3); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // X + { + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "shift" means: the tiles will be shifted (offset) horizontally by this amount + // xgettext:no-c-format + gtk_label_set_markup (GTK_LABEL(l), _("<b>Shift X:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Horizontal shift per row (in % of tile width)"), "shiftx_per_j", + -10000, 10000, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Horizontal shift per column (in % of tile width)"), "shiftx_per_i", + -10000, 10000, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the horizontal shift by this percentage"), "shiftx_rand", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + // Y + { + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "shift" means: the tiles will be shifted (offset) vertically by this amount + // xgettext:no-c-format + gtk_label_set_markup (GTK_LABEL(l), _("<b>Shift Y:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Vertical shift per row (in % of tile height)"), "shifty_per_j", + -10000, 10000, "%"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Vertical shift per column (in % of tile height)"), "shifty_per_i", + -10000, 10000, "%"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { + GtkWidget *l = clonetiler_spinbox ( + _("Randomize the vertical shift by this percentage"), "shifty_rand", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 4); + } + + // Exponent + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Exponent:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_spinbox ( + _("Whether rows are spaced evenly (1), converge (<1) or diverge (>1)"), "shifty_exp", + 0, 10, "", true); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_spinbox ( + _("Whether columns are spaced evenly (1), converge (<1) or diverge (>1)"), "shiftx_exp", + 0, 10, "", true); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 5, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of shifts for each row"), "shifty_alternate"); + clonetiler_table_attach (table, l, 0, 5, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of shifts for each column"), "shiftx_alternate"); + clonetiler_table_attach (table, l, 0, 5, 3); + } + + { // Cumulate + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Cumulate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 6, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Cumulate the shifts for each row"), "shifty_cumulate"); + clonetiler_table_attach (table, l, 0, 6, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Cumulate the shifts for each column"), "shiftx_cumulate"); + clonetiler_table_attach (table, l, 0, 6, 3); + } + + { // Exclude tile width and height in shift + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Cumulate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Exclude tile:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 7, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Exclude tile height in shift"), "shifty_excludeh"); + clonetiler_table_attach (table, l, 0, 7, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Exclude tile width in shift"), "shiftx_excludew"); + clonetiler_table_attach (table, l, 0, 7, 3); + } + + } + + + // Scale + { + GtkWidget *vb = clonetiler_new_tab (nb, _("Sc_ale")); + + GtkWidget *table = clonetiler_table_x_y_rand (2); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // X + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Scale X:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Horizontal scale per row (in % of tile width)"), "scalex_per_j", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Horizontal scale per column (in % of tile width)"), "scalex_per_i", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the horizontal scale by this percentage"), "scalex_rand", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + // Y + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Scale Y:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Vertical scale per row (in % of tile height)"), "scaley_per_j", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Vertical scale per column (in % of tile height)"), "scaley_per_i", + -100, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the vertical scale by this percentage"), "scaley_rand", + 0, 1000, "%"); + clonetiler_table_attach (table, l, 0, 3, 4); + } + + // Exponent + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Exponent:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Whether row scaling is uniform (1), converge (<1) or diverge (>1)"), "scaley_exp", + 0, 10, "", true); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Whether column scaling is uniform (1), converge (<1) or diverge (>1)"), "scalex_exp", + 0, 10, "", true); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + // Logarithmic (as in logarithmic spiral) + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Base:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 5, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scaley_log", + 0, 10, "", false); + clonetiler_table_attach (table, l, 0, 5, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scalex_log", + 0, 10, "", false); + clonetiler_table_attach (table, l, 0, 5, 3); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 6, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of scales for each row"), "scaley_alternate"); + clonetiler_table_attach (table, l, 0, 6, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of scales for each column"), "scalex_alternate"); + clonetiler_table_attach (table, l, 0, 6, 3); + } + + { // Cumulate + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Cumulate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 7, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Cumulate the scales for each row"), "scaley_cumulate"); + clonetiler_table_attach (table, l, 0, 7, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Cumulate the scales for each column"), "scalex_cumulate"); + clonetiler_table_attach (table, l, 0, 7, 3); + } + + } + + + // Rotation + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Rotation")); + + GtkWidget *table = clonetiler_table_x_y_rand (1); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // Angle + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Angle:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Rotate tiles by this angle for each row"), "rotate_per_j", + -180, 180, "°"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox ( + // xgettext:no-c-format + _("Rotate tiles by this angle for each column"), "rotate_per_i", + -180, 180, "°"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the rotation angle by this percentage"), "rotate_rand", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the rotation direction for each row"), "rotate_alternatej"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the rotation direction for each column"), "rotate_alternatei"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { // Cumulate + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Cumulate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Cumulate the rotation for each row"), "rotate_cumulatej"); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Cumulate the rotation for each column"), "rotate_cumulatei"); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + } + + + // Blur and opacity + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Blur & opacity")); + + GtkWidget *table = clonetiler_table_x_y_rand (1); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + + // Blur + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Blur:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Blur tiles by this percentage for each row"), "blur_per_j", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Blur tiles by this percentage for each column"), "blur_per_i", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the tile blur by this percentage"), "blur_rand", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of blur change for each row"), "blur_alternatej"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of blur change for each column"), "blur_alternatei"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + + + // Dissolve + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>Opacity:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Decrease tile opacity by this percentage for each row"), "opacity_per_j", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Decrease tile opacity by this percentage for each column"), "opacity_per_i", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the tile opacity by this percentage"), "opacity_rand", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 4); + } + + { // alternates + GtkWidget *l = gtk_label_new (""); + // TRANSLATORS: "Alternate" is a verb here + gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 5, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of opacity change for each row"), "opacity_alternatej"); + clonetiler_table_attach (table, l, 0, 5, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of opacity change for each column"), "opacity_alternatei"); + clonetiler_table_attach (table, l, 0, 5, 3); + } + } + + + // Color + { + GtkWidget *vb = clonetiler_new_tab (nb, _("Co_lor")); + + { + GtkWidget *hb = gtk_hbox_new (FALSE, 0); + + GtkWidget *l = gtk_label_new (_("Initial color: ")); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + + guint32 rgba = 0x000000ff | sp_svg_read_color (prefs->getString(prefs_path + "initial_color").data(), 0x000000ff); + color_picker = new Inkscape::UI::Widget::ColorPicker (*new Glib::ustring(_("Initial color of tiled clones")), *new Glib::ustring(_("Initial color for clones (works only if the original has unset fill or stroke)")), rgba, false); + color_changed_connection = color_picker->connectChanged (sigc::ptr_fun(on_picker_color_changed)); + + gtk_box_pack_start (GTK_BOX (hb), reinterpret_cast<GtkWidget*>(color_picker->gobj()), FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + } + + + GtkWidget *table = clonetiler_table_x_y_rand (3); + gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); + + // Hue + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>H:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 2, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Change the tile hue by this percentage for each row"), "hue_per_j", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Change the tile hue by this percentage for each column"), "hue_per_i", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the tile hue by this percentage"), "hue_rand", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 2, 4); + } + + + // Saturation + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>S:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 3, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Change the color saturation by this percentage for each row"), "saturation_per_j", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 3, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Change the color saturation by this percentage for each column"), "saturation_per_i", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 3, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the color saturation by this percentage"), "saturation_rand", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 3, 4); + } + + // Lightness + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<b>L:</b>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 4, 1); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Change the color lightness by this percentage for each row"), "lightness_per_j", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 2); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Change the color lightness by this percentage for each column"), "lightness_per_i", + -100, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 3); + } + + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the color lightness by this percentage"), "lightness_rand", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0, 4, 4); + } + + + { // alternates + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); + gtk_size_group_add_widget(table_row_labels, l); + clonetiler_table_attach (table, l, 1, 5, 1); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of color changes for each row"), "color_alternatej"); + clonetiler_table_attach (table, l, 0, 5, 2); + } + + { + GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of color changes for each column"), "color_alternatei"); + clonetiler_table_attach (table, l, 0, 5, 3); + } + + } + + // Trace + { + GtkWidget *vb = clonetiler_new_tab (nb, _("_Trace")); + + + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); + + GtkWidget *b = gtk_check_button_new_with_label (_("Trace the drawing under the tiles")); + g_object_set_data (G_OBJECT(b), "uncheckable", GINT_TO_POINTER(TRUE)); + bool old = prefs->getBool(prefs_path + "dotrace"); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old); + gtk_widget_set_tooltip_text (b, _("For each clone, pick a value from the drawing in that clone's location and apply it to the clone")); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(clonetiler_do_pick_toggled), (gpointer)dlg); + } + + { + GtkWidget *vvb = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vb), vvb, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT(dlg), "dotrace", (gpointer) vvb); + + + { + GtkWidget *frame = gtk_frame_new (_("1. Pick from the drawing:")); + gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0); + + GtkWidget *table = gtk_table_new (3, 3, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_add(GTK_CONTAINER(frame), table); + + + GtkWidget* radio; + { + radio = gtk_radio_button_new_with_label (NULL, _("Color")); + gtk_widget_set_tooltip_text (radio, _("Pick the visible color and opacity")); + clonetiler_table_attach (table, radio, 0.0, 1, 1); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_COLOR)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_COLOR); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("Opacity")); + gtk_widget_set_tooltip_text (radio, _("Pick the total accumulated opacity")); + clonetiler_table_attach (table, radio, 0.0, 2, 1); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_OPACITY)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_OPACITY); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("R")); + gtk_widget_set_tooltip_text (radio, _("Pick the Red component of the color")); + clonetiler_table_attach (table, radio, 0.0, 1, 2); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_R)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_R); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("G")); + gtk_widget_set_tooltip_text (radio, _("Pick the Green component of the color")); + clonetiler_table_attach (table, radio, 0.0, 2, 2); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_G)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_G); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("B")); + gtk_widget_set_tooltip_text (radio, _("Pick the Blue component of the color")); + clonetiler_table_attach (table, radio, 0.0, 3, 2); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_B)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_B); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color hue", "H")); + gtk_widget_set_tooltip_text (radio, _("Pick the hue of the color")); + clonetiler_table_attach (table, radio, 0.0, 1, 3); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_H)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_H); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color saturation", "S")); + gtk_widget_set_tooltip_text (radio, _("Pick the saturation of the color")); + clonetiler_table_attach (table, radio, 0.0, 2, 3); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_S)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_S); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color lightness", "L")); + gtk_widget_set_tooltip_text (radio, _("Pick the lightness of the color")); + clonetiler_table_attach (table, radio, 0.0, 3, 3); + g_signal_connect (G_OBJECT (radio), "toggled", + G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_L)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_L); + } + + } + + { + GtkWidget *frame = gtk_frame_new (_("2. Tweak the picked value:")); + gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, VB_MARGIN); + + GtkWidget *table = gtk_table_new (4, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_add(GTK_CONTAINER(frame), table); + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Gamma-correct:")); + clonetiler_table_attach (table, l, 1.0, 1, 1); + } + { + GtkWidget *l = clonetiler_spinbox (_("Shift the mid-range of the picked value upwards (>0) or downwards (<0)"), "gamma_picked", + -10, 10, ""); + clonetiler_table_attach (table, l, 0.0, 1, 2); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Randomize:")); + clonetiler_table_attach (table, l, 1.0, 1, 3); + } + { + GtkWidget *l = clonetiler_spinbox (_("Randomize the picked value by this percentage"), "rand_picked", + 0, 100, "%"); + clonetiler_table_attach (table, l, 0.0, 1, 4); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), _("Invert:")); + clonetiler_table_attach (table, l, 1.0, 2, 1); + } + { + GtkWidget *l = clonetiler_checkbox (_("Invert the picked value"), "invert_picked"); + clonetiler_table_attach (table, l, 0.0, 2, 2); + } + } + + { + GtkWidget *frame = gtk_frame_new (_("3. Apply the value to the clones':")); + gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0); + + + GtkWidget *table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_container_add(GTK_CONTAINER(frame), table); + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Presence")); + bool old = prefs->getBool(prefs_path + "pick_to_presence", true); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old); + gtk_widget_set_tooltip_text (b, _("Each clone is created with the probability determined by the picked value in that point")); + clonetiler_table_attach (table, b, 0.0, 1, 1); + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_presence"); + } + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Size")); + bool old = prefs->getBool(prefs_path + "pick_to_size"); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old); + gtk_widget_set_tooltip_text (b, _("Each clone's size is determined by the picked value in that point")); + clonetiler_table_attach (table, b, 0.0, 2, 1); + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_size"); + } + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Color")); + bool old = prefs->getBool(prefs_path + "pick_to_color", 0); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old); + gtk_widget_set_tooltip_text (b, _("Each clone is painted by the picked color (the original must have unset fill or stroke)")); + clonetiler_table_attach (table, b, 0.0, 1, 2); + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_color"); + } + + { + GtkWidget *b = gtk_check_button_new_with_label (_("Opacity")); + bool old = prefs->getBool(prefs_path + "pick_to_opacity", 0); + gtk_toggle_button_set_active ((GtkToggleButton *) b, old); + gtk_widget_set_tooltip_text (b, _("Each clone's opacity is determined by the picked value in that point")); + clonetiler_table_attach (table, b, 0.0, 2, 2); + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_opacity"); + } + } + gtk_widget_set_sensitive (vvb, prefs->getBool(prefs_path + "dotrace")); + } + } + + // Rows/columns, width/height + { + GtkWidget *table = gtk_table_new (2, 2, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (mainbox), table, FALSE, FALSE, 0); + + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + g_object_set_data (G_OBJECT(dlg), "rowscols", (gpointer) hb); + + { + Gtk::Adjustment *a = new Gtk::Adjustment (0.0, 1, 500, 1, 10, 0); + int value = prefs->getInt(prefs_path + "jmax", 2); + a->set_value (value); + + Inkscape::UI::Widget::SpinButton *sb = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 0); + sb->set_tooltip_text (_("How many rows in the tiling")); + sb->set_width_chars (7); + gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), TRUE, TRUE, 0); + + // TODO: C++ification + g_signal_connect(G_OBJECT(a->gobj()), "value_changed", + G_CALLBACK(clonetiler_xy_changed), (gpointer) "jmax"); + } + + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), "×"); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } + + { + Gtk::Adjustment *a = new Gtk::Adjustment (0.0, 1, 500, 1, 10, 0); + int value = prefs->getInt(prefs_path + "imax", 2); + a->set_value (value); + + Inkscape::UI::Widget::SpinButton *sb = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 0); + sb->set_tooltip_text (_("How many columns in the tiling")); + sb->set_width_chars (7); + gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), TRUE, TRUE, 0); + + // TODO: C++ification + g_signal_connect(G_OBJECT(a->gobj()), "value_changed", + G_CALLBACK(clonetiler_xy_changed), (gpointer) "imax"); + } + + clonetiler_table_attach (table, hb, 0.0, 1, 2); + } + + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + g_object_set_data (G_OBJECT(dlg), "widthheight", (gpointer) hb); + + // unitmenu + GtkWidget *u = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); + sp_unit_selector_set_unit (SP_UNIT_SELECTOR(u), sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units); + + { + // Width spinbutton + Gtk::Adjustment *a = new Gtk::Adjustment (0.0, -1e6, 1e6, 1.0, 10.0, 0); + sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a->gobj())); + + double value = prefs->getDouble(prefs_path + "fillwidth", 50.0); + SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); + gdouble const units = sp_pixels_get_units (value, unit); + a->set_value (units); + + Inkscape::UI::Widget::SpinButton *e = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 2); + e->set_tooltip_text (_("Width of the rectangle to be filled")); + e->set_width_chars (7); + e->set_digits (4); + gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(e->gobj()), TRUE, TRUE, 0); + // TODO: C++ification + g_signal_connect(G_OBJECT(a->gobj()), "value_changed", + G_CALLBACK(clonetiler_fill_width_changed), u); + } + { + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL(l), "×"); + gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); + gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); + } + + { + // Height spinbutton + Gtk::Adjustment *a = new Gtk::Adjustment (0.0, -1e6, 1e6, 1.0, 10.0, 0); + sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a->gobj())); + + double value = prefs->getDouble(prefs_path + "fillheight", 50.0); + SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); + gdouble const units = sp_pixels_get_units (value, unit); + a->set_value (units); + + Inkscape::UI::Widget::SpinButton *e = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 2); + e->set_tooltip_text (_("Height of the rectangle to be filled")); + e->set_width_chars (7); + e->set_digits (4); + gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(e->gobj()), TRUE, TRUE, 0); + // TODO: C++ification + g_signal_connect(G_OBJECT(a->gobj()), "value_changed", + G_CALLBACK(clonetiler_fill_height_changed), u); + } + + gtk_box_pack_start (GTK_BOX (hb), u, TRUE, TRUE, 0); + clonetiler_table_attach (table, hb, 0.0, 2, 2); + + } + + // Switch + GtkWidget* radio; + { + radio = gtk_radio_button_new_with_label (NULL, _("Rows, columns: ")); + gtk_widget_set_tooltip_text (radio, _("Create the specified number of rows and columns")); + clonetiler_table_attach (table, radio, 0.0, 1, 1); + g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (clonetiler_switch_to_create), (gpointer) dlg); + } + if (!prefs->getBool(prefs_path + "fillrect")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio)); + } + { + radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("Width, height: ")); + gtk_widget_set_tooltip_text (radio, _("Fill the specified width and height with the tiling")); + clonetiler_table_attach (table, radio, 0.0, 2, 1); + g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (clonetiler_switch_to_fill), (gpointer) dlg); + } + if (prefs->getBool(prefs_path + "fillrect")) { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio)); + } + } + + + // Use saved pos + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); + + GtkWidget *b = gtk_check_button_new_with_label (_("Use saved size and position of the tile")); + bool keepbbox = prefs->getBool(prefs_path + "keepbbox", true); + gtk_toggle_button_set_active ((GtkToggleButton *) b, keepbbox); + gtk_widget_set_tooltip_text (b, _("Pretend that the size and position of the tile are the same as the last time you tiled it (if any), instead of using the current size")); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(clonetiler_keep_bbox_toggled), NULL); + } + + // Statusbar + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_end (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); + GtkWidget *l = gtk_label_new(""); + g_object_set_data (G_OBJECT(dlg), "status", (gpointer) l); + gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); + } + + // Buttons + { + GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); + gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); + + { + GtkWidget *b = gtk_button_new (); + GtkWidget *l = gtk_label_new (""); + gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Create</b> ")); + gtk_container_add (GTK_CONTAINER(b), l); + gtk_widget_set_tooltip_text (b, _("Create and tile the clones of the selection")); + g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_apply), dlg); + gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + + { // buttons which are enabled only when there are tiled clones + GtkWidget *sb = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end (GTK_BOX (hb), sb, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT(dlg), "buttons_on_tiles", (gpointer) sb); + { + // TRANSLATORS: if a group of objects are "clumped" together, then they + // are unevenly spread in the given amount of space - as shown in the + // diagrams on the left in the following screenshot: + // http://www.inkscape.org/screenshots/gallery/inkscape-0.42-CVS-tiles-unclump.png + // So unclumping is the process of spreading a number of objects out more evenly. + GtkWidget *b = gtk_button_new_with_mnemonic (_(" _Unclump ")); + gtk_widget_set_tooltip_text (b, _("Spread out clones to reduce clumping; can be applied repeatedly")); + g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_unclump), NULL); + gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0); + } + + { + GtkWidget *b = gtk_button_new_with_mnemonic (_(" Re_move ")); + gtk_widget_set_tooltip_text (b, _("Remove existing tiled clones of the selected object (siblings only)")); + g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_remove), gpointer(dlg)); + gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0); + } + + // connect to global selection changed signal (so we can change desktops) and + // external_change (so we're not fooled by undo) + g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (clonetiler_change_selection), dlg); + g_signal_connect (G_OBJECT (INKSCAPE), "external_change", G_CALLBACK (clonetiler_external_change), dlg); + g_signal_connect(G_OBJECT(dlg), "destroy", G_CALLBACK(clonetiler_disconnect_gsignal), G_OBJECT (INKSCAPE)); + + // update now + clonetiler_change_selection (NULL, sp_desktop_selection(SP_ACTIVE_DESKTOP), dlg); + } + + { + GtkWidget *b = gtk_button_new_with_mnemonic (_(" R_eset ")); + // TRANSLATORS: "change" is a noun here + gtk_widget_set_tooltip_text (b, _("Reset all shifts, scales, rotates, opacity and color changes in the dialog to zero")); + g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_reset), dlg); + gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); + } + } + + gtk_widget_show_all (mainbox); - if (x < 0) { - x = 0; - } - if (y < 0) { - y = 0; } - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setInt(prefs_path + "x", x); - prefs->setInt(prefs_path + "y", y); - prefs->setInt(prefs_path + "w", w); - prefs->setInt(prefs_path + "h", h); + show_all(); - return FALSE; // which means, go ahead and destroy it + desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &CloneTiler::setTargetDesktop) ); + deskTrack.connect(GTK_WIDGET(gobj())); } -static void on_picker_color_changed(guint rgba) +CloneTiler::~CloneTiler (void) +{ + //subselChangedConn.disconnect(); + //selectChangedConn.disconnect(); + //selectModifiedConn.disconnect(); + desktopChangeConn.disconnect(); + deskTrack.disconnect(); + color_changed_connection.disconnect(); +} + +void CloneTiler::setDesktop(SPDesktop *desktop) +{ + Panel::setDesktop(desktop); + deskTrack.setBase(desktop); +} + +void CloneTiler::setTargetDesktop(SPDesktop *desktop) +{ + if (this->desktop != desktop) { + if (this->desktop) { + //selectModifiedConn.disconnect(); + //subselChangedConn.disconnect(); + //selectChangedConn.disconnect(); + } + this->desktop = desktop; + if (desktop && desktop->selection) { + //selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &CloneTiler::clonetiler_change_selection))); + //subselChangedConn = desktop->connectToolSubselectionChanged(sigc::hide(sigc::mem_fun(*this, &CloneTiler::clonetiler_change_selection))); + //selectModifiedConn = desktop->selection->connectModified(sigc::hide<0>(sigc::mem_fun(*this, &CloneTiler::clonetiler_change_selection))); + } + } +} + +void CloneTiler::on_picker_color_changed(guint rgba) { static bool is_updating = false; if (is_updating || !SP_ACTIVE_DESKTOP) @@ -148,9 +1242,7 @@ static void on_picker_color_changed(guint rgba) is_updating = false; } -static guint clonetiler_number_of_clones(SPObject *obj); - -static void clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg) +void CloneTiler::clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg) { GtkWidget *buttons = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "buttons_on_tiles"); GtkWidget *status = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "status"); @@ -179,41 +1271,19 @@ static void clonetiler_change_selection(Inkscape::Application * /*inkscape*/, In } } -static void clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg) +void CloneTiler::clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg) { clonetiler_change_selection (NULL, sp_desktop_selection(SP_ACTIVE_DESKTOP), dlg); } -static void clonetiler_disconnect_gsignal(GObject *widget, gpointer source) +void CloneTiler::clonetiler_disconnect_gsignal(GObject *widget, gpointer source) { if (source && G_IS_OBJECT(source)) { sp_signal_disconnect_by_data (source, widget); } } - -enum { - TILE_P1, - TILE_P2, - TILE_PM, - TILE_PG, - TILE_CM, - TILE_PMM, - TILE_PMG, - TILE_PGG, - TILE_CMM, - TILE_P4, - TILE_P4M, - TILE_P4G, - TILE_P3, - TILE_P31M, - TILE_P3M1, - TILE_P6, - TILE_P6M -}; - - -static Geom::Affine clonetiler_get_transform( +Geom::Affine CloneTiler::clonetiler_get_transform( // symmetry group int type, @@ -815,7 +1885,7 @@ static Geom::Affine clonetiler_get_transform( return Geom::identity(); } -static bool clonetiler_is_a_clone_of(SPObject *tile, SPObject *obj) +bool CloneTiler::clonetiler_is_a_clone_of(SPObject *tile, SPObject *obj) { bool result = false; char *id_href = NULL; @@ -842,12 +1912,7 @@ static bool clonetiler_is_a_clone_of(SPObject *tile, SPObject *obj) return result; } -static Inkscape::Drawing *trace_drawing = NULL; -static unsigned trace_visionkey; -static gdouble trace_zoom; -static SPDocument *trace_doc = NULL; - -static void clonetiler_trace_hide_tiled_clones_recursively(SPObject *from) +void CloneTiler::clonetiler_trace_hide_tiled_clones_recursively(SPObject *from) { if (!trace_drawing) return; @@ -859,7 +1924,7 @@ static void clonetiler_trace_hide_tiled_clones_recursively(SPObject *from) } } -static void clonetiler_trace_setup(SPDocument *doc, gdouble zoom, SPItem *original) +void CloneTiler::clonetiler_trace_setup(SPDocument *doc, gdouble zoom, SPItem *original) { trace_drawing = new Inkscape::Drawing(); /* Create ArenaItem and set transform */ @@ -877,7 +1942,7 @@ static void clonetiler_trace_setup(SPDocument *doc, gdouble zoom, SPItem *origin trace_zoom = zoom; } -static guint32 clonetiler_trace_pick(Geom::Rect box) +guint32 CloneTiler::clonetiler_trace_pick(Geom::Rect box) { if (!trace_drawing) { return 0; @@ -901,7 +1966,7 @@ static guint32 clonetiler_trace_pick(Geom::Rect box) return SP_RGBA32_F_COMPOSE (R, G, B, A); } -static void clonetiler_trace_finish() +void CloneTiler::clonetiler_trace_finish() { if (trace_doc) { trace_doc->getRoot()->invoke_hide(trace_visionkey); @@ -911,7 +1976,7 @@ static void clonetiler_trace_finish() } } -static void clonetiler_unclump(GtkWidget */*widget*/, void *) +void CloneTiler::clonetiler_unclump(GtkWidget */*widget*/, void *) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop == NULL) { @@ -947,7 +2012,7 @@ static void clonetiler_unclump(GtkWidget */*widget*/, void *) _("Unclump tiled clones")); } -static guint clonetiler_number_of_clones(SPObject *obj) +guint CloneTiler::clonetiler_number_of_clones(SPObject *obj) { SPObject *parent = obj->parent; @@ -962,7 +2027,7 @@ static guint clonetiler_number_of_clones(SPObject *obj) return n; } -static void clonetiler_remove(GtkWidget */*widget*/, void *, bool do_undo = true) +void CloneTiler::clonetiler_remove(GtkWidget */*widget*/, GtkWidget *dlg, bool do_undo/* = true*/) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop == NULL) { @@ -1000,7 +2065,7 @@ static void clonetiler_remove(GtkWidget */*widget*/, void *, bool do_undo = true } } -static Geom::Rect transform_rect(Geom::Rect const &r, Geom::Affine const &m) +Geom::Rect CloneTiler::transform_rect(Geom::Rect const &r, Geom::Affine const &m) { using Geom::X; using Geom::Y; @@ -1021,7 +2086,7 @@ static Geom::Rect transform_rect(Geom::Rect const &r, Geom::Affine const &m) Randomizes \a val by \a rand, with 0 < val < 1 and all values (including 0, 1) having the same probability of being displaced. */ -static double randomize01(double val, double rand) +double CloneTiler::randomize01(double val, double rand) { double base = MIN (val - rand, 1 - 2*rand); if (base < 0) { @@ -1032,7 +2097,7 @@ static double randomize01(double val, double rand) } -static void clonetiler_apply(GtkWidget */*widget*/, void *) +void CloneTiler::clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop == NULL) { @@ -1068,7 +2133,7 @@ static void clonetiler_apply(GtkWidget */*widget*/, void *) const char *id_href = g_strdup_printf("#%s", obj_repr->attribute("id")); SPObject *parent = obj->parent; - clonetiler_remove (NULL, NULL, false); + clonetiler_remove (NULL, dlg, false); double shiftx_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shiftx_per_i", 0, -10000, 10000); double shifty_per_i = 0.01 * prefs->getDoubleLimited(prefs_path + "shifty_per_i", 0, -10000, 10000); @@ -1466,7 +2531,7 @@ static void clonetiler_apply(GtkWidget */*widget*/, void *) _("Create tiled clones")); } -static GtkWidget * clonetiler_new_tab(GtkWidget *nb, const gchar *label) +GtkWidget * CloneTiler::clonetiler_new_tab(GtkWidget *nb, const gchar *label) { GtkWidget *l = gtk_label_new_with_mnemonic (label); GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN); @@ -1475,14 +2540,14 @@ static GtkWidget * clonetiler_new_tab(GtkWidget *nb, const gchar *label) return vb; } -static void clonetiler_checkbox_toggled(GtkToggleButton *tb, gpointer *data) +void CloneTiler::clonetiler_checkbox_toggled(GtkToggleButton *tb, gpointer *data) { const gchar *attr = (const gchar *) data; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setBool(prefs_path + attr, gtk_toggle_button_get_active(tb)); } -static GtkWidget * clonetiler_checkbox(const char *tip, const char *attr) +GtkWidget * CloneTiler::clonetiler_checkbox(const char *tip, const char *attr) { GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); @@ -1502,14 +2567,14 @@ static GtkWidget * clonetiler_checkbox(const char *tip, const char *attr) return hb; } -static void clonetiler_value_changed(GtkAdjustment *adj, gpointer data) +void CloneTiler::clonetiler_value_changed(GtkAdjustment *adj, gpointer data) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); const gchar *pref = (const gchar *) data; prefs->setDouble(prefs_path + pref, gtk_adjustment_get_value (adj)); } -static GtkWidget * clonetiler_spinbox(const char *tip, const char *attr, double lower, double upper, const gchar *suffix, bool exponent = false) +GtkWidget * CloneTiler::clonetiler_spinbox(const char *tip, const char *attr, double lower, double upper, const gchar *suffix, bool exponent/* = false*/) { GtkWidget *hb = gtk_hbox_new(FALSE, 0); @@ -1557,27 +2622,27 @@ static GtkWidget * clonetiler_spinbox(const char *tip, const char *attr, double return hb; } -static void clonetiler_symgroup_changed(GtkComboBox *cb, gpointer /*data*/) +void CloneTiler::clonetiler_symgroup_changed(GtkComboBox *cb, gpointer /*data*/) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gint group_new = gtk_combo_box_get_active (cb); prefs->setInt(prefs_path + "symmetrygroup", group_new); } -static void clonetiler_xy_changed(GtkAdjustment *adj, gpointer data) +void CloneTiler::clonetiler_xy_changed(GtkAdjustment *adj, gpointer data) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); const gchar *pref = (const gchar *) data; prefs->setInt(prefs_path + pref, (int) floor(gtk_adjustment_get_value (adj) + 0.5)); } -static void clonetiler_keep_bbox_toggled(GtkToggleButton *tb, gpointer /*data*/) +void CloneTiler::clonetiler_keep_bbox_toggled(GtkToggleButton *tb, gpointer /*data*/) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setBool(prefs_path + "keepbbox", gtk_toggle_button_get_active(tb)); } -static void clonetiler_pick_to(GtkToggleButton *tb, gpointer data) +void CloneTiler::clonetiler_pick_to(GtkToggleButton *tb, gpointer data) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); const gchar *pref = (const gchar *) data; @@ -1585,7 +2650,7 @@ static void clonetiler_pick_to(GtkToggleButton *tb, gpointer data) } -static void clonetiler_reset_recursive(GtkWidget *w) +void CloneTiler::clonetiler_reset_recursive(GtkWidget *w) { if (w && GTK_IS_OBJECT(w)) { { @@ -1619,19 +2684,19 @@ static void clonetiler_reset_recursive(GtkWidget *w) } } -static void clonetiler_reset(GtkWidget */*widget*/, void *) +void CloneTiler::clonetiler_reset(GtkWidget */*widget*/, GtkWidget *dlg) { clonetiler_reset_recursive (dlg); } -static void clonetiler_table_attach(GtkWidget *table, GtkWidget *widget, float align, int row, int col) +void CloneTiler::clonetiler_table_attach(GtkWidget *table, GtkWidget *widget, float align, int row, int col) { GtkWidget *a = gtk_alignment_new (align, 0, 0, 0); gtk_container_add(GTK_CONTAINER(a), widget); gtk_table_attach ( GTK_TABLE (table), a, col, col + 1, row, row + 1, (GtkAttachOptions)4, (GtkAttachOptions)0, 0, 0 ); } -static GtkWidget * clonetiler_table_x_y_rand(int values) +GtkWidget * CloneTiler::clonetiler_table_x_y_rand(int values) { GtkWidget *table = gtk_table_new (values + 2, 5, FALSE); gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN); @@ -1673,7 +2738,7 @@ static GtkWidget * clonetiler_table_x_y_rand(int values) return table; } -static void clonetiler_pick_switched(GtkToggleButton */*tb*/, gpointer data) +void CloneTiler::clonetiler_pick_switched(GtkToggleButton */*tb*/, gpointer data) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint v = GPOINTER_TO_INT (data); @@ -1681,7 +2746,7 @@ static void clonetiler_pick_switched(GtkToggleButton */*tb*/, gpointer data) } -static void clonetiler_switch_to_create(GtkToggleButton */*tb*/, GtkWidget *dlg) +void CloneTiler::clonetiler_switch_to_create(GtkToggleButton */*tb*/, GtkWidget *dlg) { GtkWidget *rowscols = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "rowscols"); GtkWidget *widthheight = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "widthheight"); @@ -1698,7 +2763,7 @@ static void clonetiler_switch_to_create(GtkToggleButton */*tb*/, GtkWidget *dlg) } -static void clonetiler_switch_to_fill(GtkToggleButton */*tb*/, GtkWidget *dlg) +void CloneTiler::clonetiler_switch_to_fill(GtkToggleButton */*tb*/, GtkWidget *dlg) { GtkWidget *rowscols = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "rowscols"); GtkWidget *widthheight = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "widthheight"); @@ -1717,7 +2782,7 @@ static void clonetiler_switch_to_fill(GtkToggleButton */*tb*/, GtkWidget *dlg) -static void clonetiler_fill_width_changed(GtkAdjustment *adj, GtkWidget *u) +void CloneTiler::clonetiler_fill_width_changed(GtkAdjustment *adj, GtkWidget *u) { gdouble const raw_dist = gtk_adjustment_get_value (adj); SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); @@ -1727,7 +2792,7 @@ static void clonetiler_fill_width_changed(GtkAdjustment *adj, GtkWidget *u) prefs->setDouble(prefs_path + "fillwidth", pixels); } -static void clonetiler_fill_height_changed(GtkAdjustment *adj, GtkWidget *u) +void CloneTiler::clonetiler_fill_height_changed(GtkAdjustment *adj, GtkWidget *u) { gdouble const raw_dist = gtk_adjustment_get_value (adj); SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); @@ -1738,7 +2803,7 @@ static void clonetiler_fill_height_changed(GtkAdjustment *adj, GtkWidget *u) } -static void clonetiler_do_pick_toggled(GtkToggleButton *tb, gpointer /*data*/) +void CloneTiler::clonetiler_do_pick_toggled(GtkToggleButton *tb, GtkWidget *dlg) { GtkWidget *vvb = (GtkWidget *) g_object_get_data (G_OBJECT(dlg), "dotrace"); @@ -1751,1156 +2816,8 @@ static void clonetiler_do_pick_toggled(GtkToggleButton *tb, gpointer /*data*/) } - - -void clonetiler_dialog(void) -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (!dlg) - { - gchar title[500]; - sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_CLONETILER), title); - - dlg = sp_window_new (title, TRUE); - if (x == -1000 || y == -1000) { - x = prefs->getInt(prefs_path + "x", -1000); - y = prefs->getInt(prefs_path + "y", -1000); - } - - if (w ==0 || h == 0) { - w = prefs->getInt(prefs_path + "w", 0); - h = prefs->getInt(prefs_path + "h", 0); - } - - prefs->setInt(prefs_path + "visible", 1); - -// if (x<0) x=0; -// if (y<0) y=0; - - if (w && h) { - gtk_window_resize ((GtkWindow *) dlg, w, h); - } - if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) { - gtk_window_move ((GtkWindow *) dlg, x, y); - - } else { - gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - } - - - sp_transientize (dlg); - wd.win = dlg; - wd.stop = 0; - - - g_signal_connect ( G_OBJECT (dlg), "event", G_CALLBACK (sp_dialog_event_handler), dlg); - - g_signal_connect ( G_OBJECT (dlg), "destroy", G_CALLBACK (clonetiler_dialog_destroy), dlg); - g_signal_connect ( G_OBJECT (dlg), "delete_event", G_CALLBACK (clonetiler_dialog_delete), dlg); - - g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (clonetiler_dialog_delete), dlg); - g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg); - g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg); - g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd); - - GtkWidget *mainbox = gtk_vbox_new(FALSE, 4); - gtk_container_set_border_width (GTK_CONTAINER (mainbox), 6); - gtk_container_add (GTK_CONTAINER (dlg), mainbox); - - GtkWidget *nb = gtk_notebook_new (); - gtk_box_pack_start (GTK_BOX (mainbox), nb, FALSE, FALSE, 0); - - -// Symmetry - { - GtkWidget *vb = clonetiler_new_tab (nb, _("_Symmetry")); - - /* TRANSLATORS: For the following 17 symmetry groups, see - * http://www.bib.ulb.ac.be/coursmath/doc/17.htm (visual examples); - * http://www.clarku.edu/~djoyce/wallpaper/seventeen.html (English vocabulary); or - * http://membres.lycos.fr/villemingerard/Geometri/Sym1D.htm (French vocabulary). - */ - struct SymGroups { - gint group; - gchar const *label; - } const sym_groups[] = { - // TRANSLATORS: "translation" means "shift" / "displacement" here. - {TILE_P1, _("<b>P1</b>: simple translation")}, - {TILE_P2, _("<b>P2</b>: 180° rotation")}, - {TILE_PM, _("<b>PM</b>: reflection")}, - // TRANSLATORS: "glide reflection" is a reflection and a translation combined. - // For more info, see http://mathforum.org/sum95/suzanne/symsusan.html - {TILE_PG, _("<b>PG</b>: glide reflection")}, - {TILE_CM, _("<b>CM</b>: reflection + glide reflection")}, - {TILE_PMM, _("<b>PMM</b>: reflection + reflection")}, - {TILE_PMG, _("<b>PMG</b>: reflection + 180° rotation")}, - {TILE_PGG, _("<b>PGG</b>: glide reflection + 180° rotation")}, - {TILE_CMM, _("<b>CMM</b>: reflection + reflection + 180° rotation")}, - {TILE_P4, _("<b>P4</b>: 90° rotation")}, - {TILE_P4M, _("<b>P4M</b>: 90° rotation + 45° reflection")}, - {TILE_P4G, _("<b>P4G</b>: 90° rotation + 90° reflection")}, - {TILE_P3, _("<b>P3</b>: 120° rotation")}, - {TILE_P31M, _("<b>P31M</b>: reflection + 120° rotation, dense")}, - {TILE_P3M1, _("<b>P3M1</b>: reflection + 120° rotation, sparse")}, - {TILE_P6, _("<b>P6</b>: 60° rotation")}, - {TILE_P6M, _("<b>P6M</b>: reflection + 60° rotation")}, - }; - - gint current = prefs->getInt(prefs_path + "symmetrygroup", 0); - - // Create a list structure containing all the data to be displayed in - // the symmetry group combo box. - GtkListStore *store = gtk_list_store_new (1, G_TYPE_STRING); - GtkTreeIter iter; - - for (unsigned j = 0; j < G_N_ELEMENTS(sym_groups); ++j) { - SymGroups const &sg = sym_groups[j]; - - // Add the description of the symgroup to a new row - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - 0, sg.label, - -1); - } - - // Add a new combo box widget with the list of symmetry groups to the vbox - GtkWidget *combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); - gtk_widget_set_tooltip_text (combo, _("Select one of the 17 symmetry groups for the tiling")); - gtk_box_pack_start (GTK_BOX (vb), combo, FALSE, FALSE, SB_MARGIN); - - // Specify the rendering of data from the list in a combo box cell - GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, - "markup", 0, - NULL); - - gtk_combo_box_set_active (GTK_COMBO_BOX (combo), current); - - g_signal_connect (G_OBJECT (combo), "changed", - G_CALLBACK (clonetiler_symgroup_changed), - NULL); - } - - table_row_labels = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); - -// Shift - { - GtkWidget *vb = clonetiler_new_tab (nb, _("S_hift")); - - GtkWidget *table = clonetiler_table_x_y_rand (3); - gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); - - // X - { - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "shift" means: the tiles will be shifted (offset) horizontally by this amount - // xgettext:no-c-format - gtk_label_set_markup (GTK_LABEL(l), _("<b>Shift X:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 2, 1); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Horizontal shift per row (in % of tile width)"), "shiftx_per_j", - -10000, 10000, "%"); - clonetiler_table_attach (table, l, 0, 2, 2); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Horizontal shift per column (in % of tile width)"), "shiftx_per_i", - -10000, 10000, "%"); - clonetiler_table_attach (table, l, 0, 2, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the horizontal shift by this percentage"), "shiftx_rand", - 0, 1000, "%"); - clonetiler_table_attach (table, l, 0, 2, 4); - } - - // Y - { - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "shift" means: the tiles will be shifted (offset) vertically by this amount - // xgettext:no-c-format - gtk_label_set_markup (GTK_LABEL(l), _("<b>Shift Y:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 3, 1); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Vertical shift per row (in % of tile height)"), "shifty_per_j", - -10000, 10000, "%"); - clonetiler_table_attach (table, l, 0, 3, 2); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Vertical shift per column (in % of tile height)"), "shifty_per_i", - -10000, 10000, "%"); - clonetiler_table_attach (table, l, 0, 3, 3); - } - - { - GtkWidget *l = clonetiler_spinbox ( - _("Randomize the vertical shift by this percentage"), "shifty_rand", - 0, 1000, "%"); - clonetiler_table_attach (table, l, 0, 3, 4); - } - - // Exponent - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Exponent:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 4, 1); - } - - { - GtkWidget *l = clonetiler_spinbox ( - _("Whether rows are spaced evenly (1), converge (<1) or diverge (>1)"), "shifty_exp", - 0, 10, "", true); - clonetiler_table_attach (table, l, 0, 4, 2); - } - - { - GtkWidget *l = clonetiler_spinbox ( - _("Whether columns are spaced evenly (1), converge (<1) or diverge (>1)"), "shiftx_exp", - 0, 10, "", true); - clonetiler_table_attach (table, l, 0, 4, 3); - } - - { // alternates - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Alternate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 5, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of shifts for each row"), "shifty_alternate"); - clonetiler_table_attach (table, l, 0, 5, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of shifts for each column"), "shiftx_alternate"); - clonetiler_table_attach (table, l, 0, 5, 3); - } - - { // Cumulate - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Cumulate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 6, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Cumulate the shifts for each row"), "shifty_cumulate"); - clonetiler_table_attach (table, l, 0, 6, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Cumulate the shifts for each column"), "shiftx_cumulate"); - clonetiler_table_attach (table, l, 0, 6, 3); - } - - { // Exclude tile width and height in shift - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Cumulate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Exclude tile:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 7, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Exclude tile height in shift"), "shifty_excludeh"); - clonetiler_table_attach (table, l, 0, 7, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Exclude tile width in shift"), "shiftx_excludew"); - clonetiler_table_attach (table, l, 0, 7, 3); - } - - } - - -// Scale - { - GtkWidget *vb = clonetiler_new_tab (nb, _("Sc_ale")); - - GtkWidget *table = clonetiler_table_x_y_rand (2); - gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); - - // X - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Scale X:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 2, 1); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Horizontal scale per row (in % of tile width)"), "scalex_per_j", - -100, 1000, "%"); - clonetiler_table_attach (table, l, 0, 2, 2); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Horizontal scale per column (in % of tile width)"), "scalex_per_i", - -100, 1000, "%"); - clonetiler_table_attach (table, l, 0, 2, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the horizontal scale by this percentage"), "scalex_rand", - 0, 1000, "%"); - clonetiler_table_attach (table, l, 0, 2, 4); - } - - // Y - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Scale Y:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 3, 1); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Vertical scale per row (in % of tile height)"), "scaley_per_j", - -100, 1000, "%"); - clonetiler_table_attach (table, l, 0, 3, 2); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Vertical scale per column (in % of tile height)"), "scaley_per_i", - -100, 1000, "%"); - clonetiler_table_attach (table, l, 0, 3, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the vertical scale by this percentage"), "scaley_rand", - 0, 1000, "%"); - clonetiler_table_attach (table, l, 0, 3, 4); - } - - // Exponent - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Exponent:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 4, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Whether row scaling is uniform (1), converge (<1) or diverge (>1)"), "scaley_exp", - 0, 10, "", true); - clonetiler_table_attach (table, l, 0, 4, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Whether column scaling is uniform (1), converge (<1) or diverge (>1)"), "scalex_exp", - 0, 10, "", true); - clonetiler_table_attach (table, l, 0, 4, 3); - } - - // Logarithmic (as in logarithmic spiral) - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Base:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 5, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scaley_log", - 0, 10, "", false); - clonetiler_table_attach (table, l, 0, 5, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Base for a logarithmic spiral: not used (0), converge (<1), or diverge (>1)"), "scalex_log", - 0, 10, "", false); - clonetiler_table_attach (table, l, 0, 5, 3); - } - - { // alternates - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Alternate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 6, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of scales for each row"), "scaley_alternate"); - clonetiler_table_attach (table, l, 0, 6, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of scales for each column"), "scalex_alternate"); - clonetiler_table_attach (table, l, 0, 6, 3); - } - - { // Cumulate - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Cumulate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 7, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Cumulate the scales for each row"), "scaley_cumulate"); - clonetiler_table_attach (table, l, 0, 7, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Cumulate the scales for each column"), "scalex_cumulate"); - clonetiler_table_attach (table, l, 0, 7, 3); - } - - } - - -// Rotation - { - GtkWidget *vb = clonetiler_new_tab (nb, _("_Rotation")); - - GtkWidget *table = clonetiler_table_x_y_rand (1); - gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); - - // Angle - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Angle:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 2, 1); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Rotate tiles by this angle for each row"), "rotate_per_j", - -180, 180, "°"); - clonetiler_table_attach (table, l, 0, 2, 2); - } - - { - GtkWidget *l = clonetiler_spinbox ( - // xgettext:no-c-format - _("Rotate tiles by this angle for each column"), "rotate_per_i", - -180, 180, "°"); - clonetiler_table_attach (table, l, 0, 2, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the rotation angle by this percentage"), "rotate_rand", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 4); - } - - { // alternates - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Alternate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 3, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the rotation direction for each row"), "rotate_alternatej"); - clonetiler_table_attach (table, l, 0, 3, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the rotation direction for each column"), "rotate_alternatei"); - clonetiler_table_attach (table, l, 0, 3, 3); - } - - { // Cumulate - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Cumulate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Cumulate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 4, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Cumulate the rotation for each row"), "rotate_cumulatej"); - clonetiler_table_attach (table, l, 0, 4, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Cumulate the rotation for each column"), "rotate_cumulatei"); - clonetiler_table_attach (table, l, 0, 4, 3); - } - - } - - -// Blur and opacity - { - GtkWidget *vb = clonetiler_new_tab (nb, _("_Blur & opacity")); - - GtkWidget *table = clonetiler_table_x_y_rand (1); - gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); - - - // Blur - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Blur:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 2, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Blur tiles by this percentage for each row"), "blur_per_j", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Blur tiles by this percentage for each column"), "blur_per_i", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the tile blur by this percentage"), "blur_rand", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 4); - } - - { // alternates - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Alternate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 3, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of blur change for each row"), "blur_alternatej"); - clonetiler_table_attach (table, l, 0, 3, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of blur change for each column"), "blur_alternatei"); - clonetiler_table_attach (table, l, 0, 3, 3); - } - - - - // Dissolve - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>Opacity:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 4, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Decrease tile opacity by this percentage for each row"), "opacity_per_j", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 4, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Decrease tile opacity by this percentage for each column"), "opacity_per_i", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 4, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the tile opacity by this percentage"), "opacity_rand", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 4, 4); - } - - { // alternates - GtkWidget *l = gtk_label_new (""); - // TRANSLATORS: "Alternate" is a verb here - gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 5, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of opacity change for each row"), "opacity_alternatej"); - clonetiler_table_attach (table, l, 0, 5, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of opacity change for each column"), "opacity_alternatei"); - clonetiler_table_attach (table, l, 0, 5, 3); - } - } - - -// Color - { - GtkWidget *vb = clonetiler_new_tab (nb, _("Co_lor")); - - { - GtkWidget *hb = gtk_hbox_new (FALSE, 0); - - GtkWidget *l = gtk_label_new (_("Initial color: ")); - gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); - - guint32 rgba = 0x000000ff | sp_svg_read_color (prefs->getString(prefs_path + "initial_color").data(), 0x000000ff); - color_picker = new Inkscape::UI::Widget::ColorPicker (*new Glib::ustring(_("Initial color of tiled clones")), *new Glib::ustring(_("Initial color for clones (works only if the original has unset fill or stroke)")), rgba, false); - _color_changed_connection = color_picker->connectChanged (sigc::ptr_fun(on_picker_color_changed)); - - gtk_box_pack_start (GTK_BOX (hb), reinterpret_cast<GtkWidget*>(color_picker->gobj()), FALSE, FALSE, 0); - - gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); - } - - - GtkWidget *table = clonetiler_table_x_y_rand (3); - gtk_box_pack_start (GTK_BOX (vb), table, FALSE, FALSE, 0); - - // Hue - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>H:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 2, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Change the tile hue by this percentage for each row"), "hue_per_j", - -100, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Change the tile hue by this percentage for each column"), "hue_per_i", - -100, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the tile hue by this percentage"), "hue_rand", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 2, 4); - } - - - // Saturation - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>S:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 3, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Change the color saturation by this percentage for each row"), "saturation_per_j", - -100, 100, "%"); - clonetiler_table_attach (table, l, 0, 3, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Change the color saturation by this percentage for each column"), "saturation_per_i", - -100, 100, "%"); - clonetiler_table_attach (table, l, 0, 3, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the color saturation by this percentage"), "saturation_rand", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 3, 4); - } - - // Lightness - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<b>L:</b>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 4, 1); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Change the color lightness by this percentage for each row"), "lightness_per_j", - -100, 100, "%"); - clonetiler_table_attach (table, l, 0, 4, 2); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Change the color lightness by this percentage for each column"), "lightness_per_i", - -100, 100, "%"); - clonetiler_table_attach (table, l, 0, 4, 3); - } - - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the color lightness by this percentage"), "lightness_rand", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0, 4, 4); - } - - - { // alternates - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("<small>Alternate:</small>")); - gtk_size_group_add_widget(table_row_labels, l); - clonetiler_table_attach (table, l, 1, 5, 1); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of color changes for each row"), "color_alternatej"); - clonetiler_table_attach (table, l, 0, 5, 2); - } - - { - GtkWidget *l = clonetiler_checkbox (_("Alternate the sign of color changes for each column"), "color_alternatei"); - clonetiler_table_attach (table, l, 0, 5, 3); - } - - } - -// Trace - { - GtkWidget *vb = clonetiler_new_tab (nb, _("_Trace")); - - - { - GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); - gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, 0); - - GtkWidget *b = gtk_check_button_new_with_label (_("Trace the drawing under the tiles")); - g_object_set_data (G_OBJECT(b), "uncheckable", GINT_TO_POINTER(TRUE)); - bool old = prefs->getBool(prefs_path + "dotrace"); - gtk_toggle_button_set_active ((GtkToggleButton *) b, old); - gtk_widget_set_tooltip_text (b, _("For each clone, pick a value from the drawing in that clone's location and apply it to the clone")); - gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(clonetiler_do_pick_toggled), dlg); - } - - { - GtkWidget *vvb = gtk_vbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (vb), vvb, FALSE, FALSE, 0); - g_object_set_data (G_OBJECT(dlg), "dotrace", (gpointer) vvb); - - - { - GtkWidget *frame = gtk_frame_new (_("1. Pick from the drawing:")); - gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0); - - GtkWidget *table = gtk_table_new (3, 3, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 6); - gtk_container_add(GTK_CONTAINER(frame), table); - - - GtkWidget* radio; - { - radio = gtk_radio_button_new_with_label (NULL, _("Color")); - gtk_widget_set_tooltip_text (radio, _("Pick the visible color and opacity")); - clonetiler_table_attach (table, radio, 0.0, 1, 1); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_COLOR)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_COLOR); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("Opacity")); - gtk_widget_set_tooltip_text (radio, _("Pick the total accumulated opacity")); - clonetiler_table_attach (table, radio, 0.0, 2, 1); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_OPACITY)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_OPACITY); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("R")); - gtk_widget_set_tooltip_text (radio, _("Pick the Red component of the color")); - clonetiler_table_attach (table, radio, 0.0, 1, 2); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_R)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_R); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("G")); - gtk_widget_set_tooltip_text (radio, _("Pick the Green component of the color")); - clonetiler_table_attach (table, radio, 0.0, 2, 2); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_G)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_G); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("B")); - gtk_widget_set_tooltip_text (radio, _("Pick the Blue component of the color")); - clonetiler_table_attach (table, radio, 0.0, 3, 2); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_B)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_B); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color hue", "H")); - gtk_widget_set_tooltip_text (radio, _("Pick the hue of the color")); - clonetiler_table_attach (table, radio, 0.0, 1, 3); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_H)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_H); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color saturation", "S")); - gtk_widget_set_tooltip_text (radio, _("Pick the saturation of the color")); - clonetiler_table_attach (table, radio, 0.0, 2, 3); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_S)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_S); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), C_("Clonetiler color lightness", "L")); - gtk_widget_set_tooltip_text (radio, _("Pick the lightness of the color")); - clonetiler_table_attach (table, radio, 0.0, 3, 3); - g_signal_connect (G_OBJECT (radio), "toggled", - G_CALLBACK (clonetiler_pick_switched), GINT_TO_POINTER(PICK_L)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), prefs->getInt(prefs_path + "pick", 0) == PICK_L); - } - - } - - { - GtkWidget *frame = gtk_frame_new (_("2. Tweak the picked value:")); - gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, VB_MARGIN); - - GtkWidget *table = gtk_table_new (4, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 6); - gtk_container_add(GTK_CONTAINER(frame), table); - - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("Gamma-correct:")); - clonetiler_table_attach (table, l, 1.0, 1, 1); - } - { - GtkWidget *l = clonetiler_spinbox (_("Shift the mid-range of the picked value upwards (>0) or downwards (<0)"), "gamma_picked", - -10, 10, ""); - clonetiler_table_attach (table, l, 0.0, 1, 2); - } - - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("Randomize:")); - clonetiler_table_attach (table, l, 1.0, 1, 3); - } - { - GtkWidget *l = clonetiler_spinbox (_("Randomize the picked value by this percentage"), "rand_picked", - 0, 100, "%"); - clonetiler_table_attach (table, l, 0.0, 1, 4); - } - - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), _("Invert:")); - clonetiler_table_attach (table, l, 1.0, 2, 1); - } - { - GtkWidget *l = clonetiler_checkbox (_("Invert the picked value"), "invert_picked"); - clonetiler_table_attach (table, l, 0.0, 2, 2); - } - } - - { - GtkWidget *frame = gtk_frame_new (_("3. Apply the value to the clones':")); - gtk_box_pack_start (GTK_BOX (vvb), frame, FALSE, FALSE, 0); - - - GtkWidget *table = gtk_table_new (2, 2, FALSE); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 6); - gtk_container_add(GTK_CONTAINER(frame), table); - - { - GtkWidget *b = gtk_check_button_new_with_label (_("Presence")); - bool old = prefs->getBool(prefs_path + "pick_to_presence", true); - gtk_toggle_button_set_active ((GtkToggleButton *) b, old); - gtk_widget_set_tooltip_text (b, _("Each clone is created with the probability determined by the picked value in that point")); - clonetiler_table_attach (table, b, 0.0, 1, 1); - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_presence"); - } - - { - GtkWidget *b = gtk_check_button_new_with_label (_("Size")); - bool old = prefs->getBool(prefs_path + "pick_to_size"); - gtk_toggle_button_set_active ((GtkToggleButton *) b, old); - gtk_widget_set_tooltip_text (b, _("Each clone's size is determined by the picked value in that point")); - clonetiler_table_attach (table, b, 0.0, 2, 1); - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_size"); - } - - { - GtkWidget *b = gtk_check_button_new_with_label (_("Color")); - bool old = prefs->getBool(prefs_path + "pick_to_color", 0); - gtk_toggle_button_set_active ((GtkToggleButton *) b, old); - gtk_widget_set_tooltip_text (b, _("Each clone is painted by the picked color (the original must have unset fill or stroke)")); - clonetiler_table_attach (table, b, 0.0, 1, 2); - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_color"); - } - - { - GtkWidget *b = gtk_check_button_new_with_label (_("Opacity")); - bool old = prefs->getBool(prefs_path + "pick_to_opacity", 0); - gtk_toggle_button_set_active ((GtkToggleButton *) b, old); - gtk_widget_set_tooltip_text (b, _("Each clone's opacity is determined by the picked value in that point")); - clonetiler_table_attach (table, b, 0.0, 2, 2); - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(clonetiler_pick_to), (gpointer) "pick_to_opacity"); - } - } - gtk_widget_set_sensitive (vvb, prefs->getBool(prefs_path + "dotrace")); - } - } - -// Rows/columns, width/height - { - GtkWidget *table = gtk_table_new (2, 2, FALSE); - gtk_container_set_border_width (GTK_CONTAINER (table), VB_MARGIN); - gtk_table_set_row_spacings (GTK_TABLE (table), 4); - gtk_table_set_col_spacings (GTK_TABLE (table), 6); - gtk_box_pack_start (GTK_BOX (mainbox), table, FALSE, FALSE, 0); - - { - GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); - g_object_set_data (G_OBJECT(dlg), "rowscols", (gpointer) hb); - - { - Gtk::Adjustment *a = new Gtk::Adjustment (0.0, 1, 500, 1, 10, 0); - int value = prefs->getInt(prefs_path + "jmax", 2); - a->set_value (value); - - Inkscape::UI::Widget::SpinButton *sb = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 0); - sb->set_tooltip_text (_("How many rows in the tiling")); - sb->set_width_chars (7); - gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), TRUE, TRUE, 0); - - // TODO: C++ification - g_signal_connect(G_OBJECT(a->gobj()), "value_changed", - G_CALLBACK(clonetiler_xy_changed), (gpointer) "jmax"); - } - - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), "×"); - gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); - gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); - } - - { - Gtk::Adjustment *a = new Gtk::Adjustment (0.0, 1, 500, 1, 10, 0); - int value = prefs->getInt(prefs_path + "imax", 2); - a->set_value (value); - - Inkscape::UI::Widget::SpinButton *sb = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 0); - sb->set_tooltip_text (_("How many columns in the tiling")); - sb->set_width_chars (7); - gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(sb->gobj()), TRUE, TRUE, 0); - - // TODO: C++ification - g_signal_connect(G_OBJECT(a->gobj()), "value_changed", - G_CALLBACK(clonetiler_xy_changed), (gpointer) "imax"); - } - - clonetiler_table_attach (table, hb, 0.0, 1, 2); - } - - { - GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); - g_object_set_data (G_OBJECT(dlg), "widthheight", (gpointer) hb); - - // unitmenu - GtkWidget *u = sp_unit_selector_new (SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE); - sp_unit_selector_set_unit (SP_UNIT_SELECTOR(u), sp_desktop_namedview(SP_ACTIVE_DESKTOP)->doc_units); - - { - // Width spinbutton - Gtk::Adjustment *a = new Gtk::Adjustment (0.0, -1e6, 1e6, 1.0, 10.0, 0); - sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a->gobj())); - - double value = prefs->getDouble(prefs_path + "fillwidth", 50.0); - SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); - gdouble const units = sp_pixels_get_units (value, unit); - a->set_value (units); - - Inkscape::UI::Widget::SpinButton *e = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 2); - e->set_tooltip_text (_("Width of the rectangle to be filled")); - e->set_width_chars (7); - e->set_digits (4); - gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(e->gobj()), TRUE, TRUE, 0); - // TODO: C++ification - g_signal_connect(G_OBJECT(a->gobj()), "value_changed", - G_CALLBACK(clonetiler_fill_width_changed), u); - } - { - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup (GTK_LABEL(l), "×"); - gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); - gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); - } - - { - // Height spinbutton - Gtk::Adjustment *a = new Gtk::Adjustment (0.0, -1e6, 1e6, 1.0, 10.0, 0); - sp_unit_selector_add_adjustment (SP_UNIT_SELECTOR (u), GTK_ADJUSTMENT (a->gobj())); - - double value = prefs->getDouble(prefs_path + "fillheight", 50.0); - SPUnit const &unit = *sp_unit_selector_get_unit(SP_UNIT_SELECTOR(u)); - gdouble const units = sp_pixels_get_units (value, unit); - a->set_value (units); - - Inkscape::UI::Widget::SpinButton *e = new Inkscape::UI::Widget::SpinButton (*a, 1.0, 2); - e->set_tooltip_text (_("Height of the rectangle to be filled")); - e->set_width_chars (7); - e->set_digits (4); - gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET(e->gobj()), TRUE, TRUE, 0); - // TODO: C++ification - g_signal_connect(G_OBJECT(a->gobj()), "value_changed", - G_CALLBACK(clonetiler_fill_height_changed), u); - } - - gtk_box_pack_start (GTK_BOX (hb), u, TRUE, TRUE, 0); - clonetiler_table_attach (table, hb, 0.0, 2, 2); - - } - - // Switch - GtkWidget* radio; - { - radio = gtk_radio_button_new_with_label (NULL, _("Rows, columns: ")); - gtk_widget_set_tooltip_text (radio, _("Create the specified number of rows and columns")); - clonetiler_table_attach (table, radio, 0.0, 1, 1); - g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (clonetiler_switch_to_create), (gpointer) dlg); - } - if (!prefs->getBool(prefs_path + "fillrect")) { - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); - gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio)); - } - { - radio = gtk_radio_button_new_with_label (gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio)), _("Width, height: ")); - gtk_widget_set_tooltip_text (radio, _("Fill the specified width and height with the tiling")); - clonetiler_table_attach (table, radio, 0.0, 2, 1); - g_signal_connect (G_OBJECT (radio), "toggled", G_CALLBACK (clonetiler_switch_to_fill), (gpointer) dlg); - } - if (prefs->getBool(prefs_path + "fillrect")) { - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); - gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (radio)); - } - } - - -// Use saved pos - { - GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); - gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); - - GtkWidget *b = gtk_check_button_new_with_label (_("Use saved size and position of the tile")); - bool keepbbox = prefs->getBool(prefs_path + "keepbbox", true); - gtk_toggle_button_set_active ((GtkToggleButton *) b, keepbbox); - gtk_widget_set_tooltip_text (b, _("Pretend that the size and position of the tile are the same as the last time you tiled it (if any), instead of using the current size")); - gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); - - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(clonetiler_keep_bbox_toggled), NULL); - } - -// Statusbar - { - GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); - gtk_box_pack_end (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); - GtkWidget *l = gtk_label_new(""); - g_object_set_data (G_OBJECT(dlg), "status", (gpointer) l); - gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); - } - -// Buttons - { - GtkWidget *hb = gtk_hbox_new(FALSE, VB_MARGIN); - gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); - - { - GtkWidget *b = gtk_button_new (); - GtkWidget *l = gtk_label_new (""); - gtk_label_set_markup_with_mnemonic (GTK_LABEL(l), _(" <b>_Create</b> ")); - gtk_container_add (GTK_CONTAINER(b), l); - gtk_widget_set_tooltip_text (b, _("Create and tile the clones of the selection")); - g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_apply), NULL); - gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0); - } - - { // buttons which are enabled only when there are tiled clones - GtkWidget *sb = gtk_hbox_new(FALSE, 0); - gtk_box_pack_end (GTK_BOX (hb), sb, FALSE, FALSE, 0); - g_object_set_data (G_OBJECT(dlg), "buttons_on_tiles", (gpointer) sb); - { - // TRANSLATORS: if a group of objects are "clumped" together, then they - // are unevenly spread in the given amount of space - as shown in the - // diagrams on the left in the following screenshot: - // http://www.inkscape.org/screenshots/gallery/inkscape-0.42-CVS-tiles-unclump.png - // So unclumping is the process of spreading a number of objects out more evenly. - GtkWidget *b = gtk_button_new_with_mnemonic (_(" _Unclump ")); - gtk_widget_set_tooltip_text (b, _("Spread out clones to reduce clumping; can be applied repeatedly")); - g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_unclump), NULL); - gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0); - } - - { - GtkWidget *b = gtk_button_new_with_mnemonic (_(" Re_move ")); - gtk_widget_set_tooltip_text (b, _("Remove existing tiled clones of the selected object (siblings only)")); - g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_remove), NULL); - gtk_box_pack_end (GTK_BOX (sb), b, FALSE, FALSE, 0); - } - - // connect to global selection changed signal (so we can change desktops) and - // external_change (so we're not fooled by undo) - g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (clonetiler_change_selection), dlg); - g_signal_connect (G_OBJECT (INKSCAPE), "external_change", G_CALLBACK (clonetiler_external_change), dlg); - g_signal_connect(G_OBJECT(dlg), "destroy", G_CALLBACK(clonetiler_disconnect_gsignal), G_OBJECT (INKSCAPE)); - - // update now - clonetiler_change_selection (NULL, sp_desktop_selection(SP_ACTIVE_DESKTOP), dlg); - } - - { - GtkWidget *b = gtk_button_new_with_mnemonic (_(" R_eset ")); - // TRANSLATORS: "change" is a noun here - gtk_widget_set_tooltip_text (b, _("Reset all shifts, scales, rotates, opacity and color changes in the dialog to zero")); - g_signal_connect (G_OBJECT (b), "clicked", G_CALLBACK (clonetiler_reset), NULL); - gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0); - } - } - - gtk_widget_show_all (mainbox); - - } // end of if (!dlg) - - gtk_window_present ((GtkWindow *) dlg); +} +} } diff --git a/src/ui/dialog/clonetiler.h b/src/ui/dialog/clonetiler.h new file mode 100644 index 000000000..2cad30e9e --- /dev/null +++ b/src/ui/dialog/clonetiler.h @@ -0,0 +1,183 @@ +/** @file + * @brief Clone tiling dialog + */ +/* Authors: + * bulia byak <buliabyak@users.sf.net> + * + * Copyright (C) 2004 Authors + * Released under the GNU GPL, read the file 'COPYING' for more information + */ +#ifndef __SP_CLONE_TILER_H__ +#define __SP_CLONE_TILER_H__ + +#include "ui/widget/panel.h" +#include <glib.h> +#include <gtk/gtk.h> + +#include "ui/dialog/desktop-tracker.h" +#include "ui/widget/color-picker.h" +#include "sp-root.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class CloneTiler : public Widget::Panel { +public: + CloneTiler(); + virtual ~CloneTiler(); + + static CloneTiler &getInstance() { return *new CloneTiler(); } + +protected: + + GtkWidget * clonetiler_new_tab(GtkWidget *nb, const gchar *label); + GtkWidget * clonetiler_table_x_y_rand(int values); + GtkWidget * clonetiler_spinbox(const char *tip, const char *attr, double lower, double upper, const gchar *suffix, bool exponent = false); + GtkWidget * clonetiler_checkbox(const char *tip, const char *attr); + void clonetiler_table_attach(GtkWidget *table, GtkWidget *widget, float align, int row, int col); + + static void clonetiler_symgroup_changed(GtkComboBox *cb, gpointer /*data*/); + static void clonetiler_remove(GtkWidget */*widget*/, GtkWidget *dlg, bool do_undo = true); + static void on_picker_color_changed(guint rgba); + static void clonetiler_trace_hide_tiled_clones_recursively(SPObject *from); + static void clonetiler_checkbox_toggled(GtkToggleButton *tb, gpointer *data); + static void clonetiler_pick_switched(GtkToggleButton */*tb*/, gpointer data); + static void clonetiler_do_pick_toggled(GtkToggleButton *tb, GtkWidget *dlg); + static void clonetiler_pick_to(GtkToggleButton *tb, gpointer data); + static void clonetiler_xy_changed(GtkAdjustment *adj, gpointer data); + static void clonetiler_fill_width_changed(GtkAdjustment *adj, GtkWidget *u); + static void clonetiler_fill_height_changed(GtkAdjustment *adj, GtkWidget *u); + static void clonetiler_switch_to_create(GtkToggleButton */*tb*/, GtkWidget *dlg); + static void clonetiler_switch_to_fill(GtkToggleButton */*tb*/, GtkWidget *dlg); + static void clonetiler_keep_bbox_toggled(GtkToggleButton *tb, gpointer /*data*/); + static void clonetiler_apply(GtkWidget */*widget*/, GtkWidget *dlg); + static void clonetiler_unclump(GtkWidget */*widget*/, void *); + static void clonetiler_change_selection(Inkscape::Application * /*inkscape*/, Inkscape::Selection *selection, GtkWidget *dlg); + static void clonetiler_external_change(Inkscape::Application * /*inkscape*/, GtkWidget *dlg); + static void clonetiler_disconnect_gsignal(GObject *widget, gpointer source); + static void clonetiler_reset(GtkWidget */*widget*/, GtkWidget *dlg); + static guint clonetiler_number_of_clones(SPObject *obj); + static void clonetiler_trace_setup(SPDocument *doc, gdouble zoom, SPItem *original); + static guint32 clonetiler_trace_pick(Geom::Rect box); + static void clonetiler_trace_finish(); + static bool clonetiler_is_a_clone_of(SPObject *tile, SPObject *obj); + static Geom::Rect transform_rect(Geom::Rect const &r, Geom::Affine const &m); + static double randomize01(double val, double rand); + static void clonetiler_value_changed(GtkAdjustment *adj, gpointer data); + static void clonetiler_reset_recursive(GtkWidget *w); + + static Geom::Affine clonetiler_get_transform( // symmetry group + int type, + + // row, column + int i, int j, + + // center, width, height of the tile + double cx, double cy, + double w, double h, + + // values from the dialog: + // Shift + double shiftx_per_i, double shifty_per_i, + double shiftx_per_j, double shifty_per_j, + double shiftx_rand, double shifty_rand, + double shiftx_exp, double shifty_exp, + int shiftx_alternate, int shifty_alternate, + int shiftx_cumulate, int shifty_cumulate, + int shiftx_excludew, int shifty_excludeh, + + // Scale + double scalex_per_i, double scaley_per_i, + double scalex_per_j, double scaley_per_j, + double scalex_rand, double scaley_rand, + double scalex_exp, double scaley_exp, + double scalex_log, double scaley_log, + int scalex_alternate, int scaley_alternate, + int scalex_cumulate, int scaley_cumulate, + + // Rotation + double rotate_per_i, double rotate_per_j, + double rotate_rand, + int rotate_alternatei, int rotate_alternatej, + int rotate_cumulatei, int rotate_cumulatej + ); + + +private: + CloneTiler(CloneTiler const &d); + CloneTiler& operator=(CloneTiler const &d); + + GtkWidget *dlg; + SPDesktop *desktop; + DesktopTracker deskTrack; + Inkscape::UI::Widget::ColorPicker *color_picker; + GtkSizeGroup* table_row_labels; + + sigc::connection desktopChangeConn; + sigc::connection selectChangedConn; + sigc::connection subselChangedConn; + sigc::connection selectModifiedConn; + sigc::connection color_changed_connection; + + /** + * Can be invoked for setting the desktop. Currently not used. + */ + void setDesktop(SPDesktop *desktop); + + /** + * Is invoked by the desktop tracker when the desktop changes. + */ + void setTargetDesktop(SPDesktop *desktop); + +}; + + +enum { + PICK_COLOR, + PICK_OPACITY, + PICK_R, + PICK_G, + PICK_B, + PICK_H, + PICK_S, + PICK_L +}; + +enum { + TILE_P1, + TILE_P2, + TILE_PM, + TILE_PG, + TILE_CM, + TILE_PMM, + TILE_PMG, + TILE_PGG, + TILE_CMM, + TILE_P4, + TILE_P4M, + TILE_P4G, + TILE_P3, + TILE_P31M, + TILE_P3M1, + TILE_P6, + TILE_P6M +}; + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 2e7ffa1c2..60011ca9d 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -51,6 +51,7 @@ #include "ui/dialog/spellcheck.h" #include "ui/dialog/export.h" #include "ui/dialog/xml-tree.h" +#include "ui/dialog/clonetiler.h" #ifdef ENABLE_SVG_FONTS #include "ui/dialog/svg-fonts-dialog.h" @@ -129,6 +130,7 @@ DialogManager::DialogManager() { registerFactory("SpellCheck", &create<SpellCheck, FloatingBehavior>); registerFactory("Export", &create<Export, FloatingBehavior>); registerFactory("XmlTree", &create<XmlTree, FloatingBehavior>); + registerFactory("CloneTiler", &create<CloneTiler, FloatingBehavior>); } else { @@ -163,6 +165,7 @@ DialogManager::DialogManager() { registerFactory("SpellCheck", &create<SpellCheck, DockBehavior>); registerFactory("Export", &create<Export, DockBehavior>); registerFactory("XmlTree", &create<XmlTree, DockBehavior>); + registerFactory("CloneTiler", &create<CloneTiler, DockBehavior>); } } diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp index 9d3508c53..f5745e02b 100644 --- a/src/ui/dialog/find.cpp +++ b/src/ui/dialog/find.cpp @@ -24,12 +24,14 @@ #include "inkscape.h" #include "desktop.h" #include "document.h" +#include "document-undo.h" #include "selection.h" #include "desktop-handles.h" #include "dialogs/dialog-events.h" #include "verbs.h" #include "interface.h" +#include "preferences.h" #include "sp-text.h" #include "sp-flowtext.h" #include "text-editing.h" @@ -50,6 +52,8 @@ #include "sp-offset.h" #include "sp-root.h" #include "xml/repr.h" +#include "xml/node-iterators.h" +#include "xml/attribute-record.h" #include <glibmm/i18n.h> @@ -59,112 +63,278 @@ namespace Dialog { Find::Find() : UI::Widget::Panel("", "/dialogs/find", SP_VERB_DIALOG_FIND), - _entry_text(_("_Text:"), _("Find objects by their text content (exact or partial match)")), - _entry_id(_("_ID:"), _("Find objects by the value of the id attribute (exact or partial match)")), - _entry_style(_("_Style:"), _("Find objects by the value of the style attribute (exact or partial match)")), - _entry_attribute(_("_Attribute:"), _("Find objects by the name of an attribute (exact or partial match)")), - _check_search_selection(_("Search in s_election"), _("Limit search to the current selection")), - _check_search_layer(_("Search in current _layer"), _("Limit search to the current layer")), - _check_include_hidden(_("Include _hidden"), _("Include hidden objects in search")), - _check_include_locked(_("Include l_ocked"), _("Include locked objects in search")), - - _check_all(_("All types"), _("Search in all object types")), - _check_all_shapes(_("All shapes"), _("Search all shapes")), - _check_rects(_("Rectangles"), _("Search rectangles")), - _check_ellipses(_("Ellipses"), _("Search ellipses, arcs, circles")), - _check_stars(_("Stars"), _("Search stars and polygons")), - _check_spirals(_("Spirals"), _("Search spirals")), - _check_paths(_("Paths"), _("Search paths, lines, polylines")), - _check_texts(_("Texts"), _("Search text objects")), - _check_groups(_("Groups"), _("Search groups")), - _check_clones( + + entry_find(_("F_ind:"), _("Find objects by their content (exact or partial match)")), + entry_replace(_("Re_place:"), _("Replace found objects with this value ")), + + check_scope_all(_("_All"), _("Search in all layers")), + check_scope_layer(_("Current _layer"), _("Limit search to the current layer")), + check_scope_selection(_("S_election"), _("Limit search to the current selection")), + check_searchin_text(_("Te_xt"), _("Search in text objects")), + check_searchin_property(_("_Properties"), _("Search in object properties, styles, attributes and IDs")), + frame_searchin(_("Search in")), + frame_scope(_("Scope")), + + + check_case_sensitive(_("Case sensiti_ve"), _("Match upper/lower case"), false), + check_exact_match(_("E_xact match"), _("Match whole objects only"), false), + check_include_hidden(_("Include _hidden"), _("Include hidden objects in search"), false), + check_include_locked(_("Include loc_ked"), _("Include locked objects in search"), false), + expander_options(_("Options")), + frame_options(_("General")), + + check_ids(_("_ID"), _("Search id name"), true), + check_attributename(_("Attribute _Name"), _("Search attribute name"), false), + check_attributevalue(_("Attribute _Value"), _("Search attribute value"), true), + check_style(_("_Style"), _("Search style"), true), + check_font(_("_Font"), _("Search fonts"), false), + frame_properties(_("Properties")), + + check_alltypes(_("All types"), _("Search all object types"), true), + check_rects(_("Rectangles"), _("Search rectangles"), false), + check_ellipses(_("Ellipses"), _("Search ellipses, arcs, circles"), false), + check_stars(_("Stars"), _("Search stars and polygons"), false), + check_spirals(_("Spirals"), _("Search spirals"), false), + check_paths(_("Paths"), _("Search paths, lines, polylines"), false), + check_texts(_("Texts"), _("Search text objects"), false), + check_groups(_("Groups"), _("Search groups"), false), + check_clones( //TRANSLATORS: "Clones" is a noun indicating type of object to find - C_("Find dialog", "Clones"), _("Search clones")), - _check_images(_("Images"), _("Search images")), - _check_offsets(_("Offsets"), _("Search offset objects")), - - _button_clear(_("_Clear"), _("Clear values")), - _button_find(_("_Find"), _("Select objects matching all of the fields you filled in")) + C_("Find dialog", "Clones"), _("Search clones"), false), + + check_images(_("Images"), _("Search images"), false), + check_offsets(_("Offsets"), _("Search offset objects"), false), + frame_types(_("Object Types")), + + status(""), + button_find(_("_Find"), _("Select all objects matching the selected fields ")), + button_replace(_("_Replace All"), _("Replace all the matching objects")), + _action_replace(false), + blocked(false), + desktop(NULL), + deskTrack() + { + entry_find.getEntry()->set_width_chars(25); + entry_replace.getEntry()->set_width_chars(25); + + Gtk::RadioButtonGroup grp_searchin = check_searchin_text.get_group(); + check_searchin_property.set_group(grp_searchin); + vbox_searchin.pack_start(check_searchin_text, true, true); + vbox_searchin.pack_start(check_searchin_property, true, true); + frame_searchin.add(vbox_searchin); + + Gtk::RadioButtonGroup grp_scope = check_scope_all.get_group(); + check_scope_layer.set_group(grp_scope); + check_scope_selection.set_group(grp_scope); + vbox_scope.pack_start(check_scope_all, true, true); + vbox_scope.pack_start(check_scope_layer, true, true); + vbox_scope.pack_start(check_scope_selection, true, true); + frame_scope.add(vbox_scope); + + hbox_searchin.set_spacing(4); + hbox_searchin.pack_start(frame_searchin, true, true); + hbox_searchin.pack_start(frame_scope, true, true); + + vbox_options1.pack_start(check_case_sensitive, true, true); + vbox_options1.pack_start(check_include_hidden, true, true); + vbox_options2.pack_start(check_exact_match, true, true); + vbox_options2.pack_start(check_include_locked, true, true); + hbox_options.pack_start(vbox_options1, true, true, 4); + hbox_options.pack_start(vbox_options2, true, true, 4); + frame_options.add(hbox_options); + + hbox_properties1.set_homogeneous(false); + hbox_properties1.pack_start(check_ids, false, false, 4 ); + hbox_properties1.pack_start(check_style, false, false, 8); + hbox_properties1.pack_start(check_font, false, false, 8); + hbox_properties2.set_homogeneous(false); + hbox_properties2.pack_start(check_attributevalue, false, false, 4); + hbox_properties2.pack_start(check_attributename, false, false, 4); + vbox_properties.pack_start(hbox_properties1, true, true, 0); + vbox_properties.pack_start(hbox_properties2, true, true, 2); + frame_properties.add(vbox_properties); + + vbox_types1.pack_start(check_alltypes, true, true); + vbox_types1.pack_start(check_paths, true, true); + vbox_types1.pack_start(check_texts, true, true); + vbox_types1.pack_start(check_groups, true, true); + vbox_types1.pack_start(check_clones, true, true); + vbox_types1.pack_start(check_images, true, true); + vbox_types2.pack_start(check_offsets, true, true); + vbox_types2.pack_start(check_rects, true, true); + vbox_types2.pack_start(check_ellipses, true, true); + vbox_types2.pack_start(check_stars, true, true); + vbox_types2.pack_start(check_spirals, true, true); + hbox_types.pack_start(vbox_types1, true, true, 4); + hbox_types.pack_start(vbox_types2, true, true, 4); + frame_types.add(hbox_types); + + vbox_expander.pack_start(frame_options, true, true, 4); + vbox_expander.pack_start(frame_properties, true, true, 4); + vbox_expander.pack_start(frame_types, true, true, 4); + + expander_options.set_use_underline(); + expander_options.add(vbox_expander); + + box_buttons.set_layout(Gtk::BUTTONBOX_END); + box_buttons.set_spacing(4); + box_buttons.pack_start(button_find, true, true, 6); + box_buttons.pack_start(button_replace, true, true, 6); + hboxbutton_row.pack_start(status, true, true, 6); + hboxbutton_row.pack_end(box_buttons, true, true); + Gtk::Box *contents = _getContents(); - contents->set_spacing(4); - - contents->pack_start(_entry_text, true, true); - contents->pack_start(_entry_id, true, true); - contents->pack_start(_entry_style, true, true); - contents->pack_start(_entry_attribute, true, true); - - contents->pack_start(_check_all, true, true); - contents->pack_start(_check_all_shapes, true, true); - contents->pack_start(_check_rects, true, true); - contents->pack_start(_check_ellipses, true, true); - contents->pack_start(_check_stars, true, true); - contents->pack_start(_check_spirals, true, true); - contents->pack_start(_check_paths, true, true); - contents->pack_start(_check_texts, true, true); - contents->pack_start(_check_groups, true, true); - contents->pack_start(_check_clones, true, true); - contents->pack_start(_check_images, true, true); - contents->pack_start(_check_offsets, true, true); - - contents->pack_start(_check_search_selection, true, true); - contents->pack_start(_check_search_layer, true, true); - contents->pack_start(_check_include_hidden, true, true); - contents->pack_start(_check_include_locked, true, true); - - contents->pack_start(_button_clear, true, true); - contents->pack_start(_button_find, true, true); + contents->set_spacing(6); + contents->pack_start(entry_find, false, false); + contents->pack_start(entry_replace, false, false); + contents->pack_start(hbox_searchin, false, false); + contents->pack_start(expander_options, false, false); + contents->pack_end(hboxbutton_row, false, false); + + checkProperties.push_back(&check_ids); + checkProperties.push_back(&check_style); + checkProperties.push_back(&check_font); + checkProperties.push_back(&check_attributevalue); + checkProperties.push_back(&check_attributename); + + checkTypes.push_back(&check_paths); + checkTypes.push_back(&check_texts); + checkTypes.push_back(&check_groups); + checkTypes.push_back(&check_clones); + checkTypes.push_back(&check_images); + checkTypes.push_back(&check_offsets); + checkTypes.push_back(&check_rects); + checkTypes.push_back(&check_ellipses); + checkTypes.push_back(&check_stars); + checkTypes.push_back(&check_spirals); + checkTypes.push_back(&check_offsets); // set signals to handle clicks - _check_all.signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleAlltypes)); - _check_all_shapes.signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleShapes)); - _button_clear.signal_clicked().connect(sigc::mem_fun(*this, &Find::onClear)); - _button_find.signal_clicked().connect(sigc::mem_fun(*this, &Find::onFind)); + expander_options.property_expanded().signal_changed().connect(sigc::mem_fun(*this, &Find::onExpander)); + button_find.signal_clicked().connect(sigc::mem_fun(*this, &Find::onFind)); + button_replace.signal_clicked().connect(sigc::mem_fun(*this, &Find::onReplace)); + check_searchin_text.signal_clicked().connect(sigc::mem_fun(*this, &Find::onSearchinText)); + check_searchin_property.signal_clicked().connect(sigc::mem_fun(*this, &Find::onSearchinProperty)); + check_alltypes.signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleAlltypes)); + + for(size_t i = 0; i < checkProperties.size(); i++) { + checkProperties[i]->signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleCheck)); + } + + for(size_t i = 0; i < checkTypes.size(); i++) { + checkTypes[i]->signal_clicked().connect(sigc::mem_fun(*this, &Find::onToggleCheck)); + } + + onSearchinText(); + onToggleAlltypes(); - _button_find.set_can_default(); - // set_default (_button_find); // activatable by Enter - _entry_text.getEntry()->grab_focus(); + desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &Find::setTargetDesktop) ); + deskTrack.connect(GTK_WIDGET(gobj())); show_all_children(); - onClear(); + + Inkscape::Selection *selection = sp_desktop_selection (SP_ACTIVE_DESKTOP); + SPItem *item = selection->singleItem(); + if (item) { + if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { + gchar *str; + str = sp_te_get_string_multiline (item); + entry_find.getEntry()->set_text(str); + } + } + + button_find.set_can_default(); + //button_find.grab_default(); // activatable by Enter + entry_find.getEntry()->grab_focus(); } -Find::~Find() +Find::~Find() { + desktopChangeConn.disconnect(); + selectChangedConn.disconnect(); + deskTrack.disconnect(); } +void Find::setDesktop(SPDesktop *desktop) +{ + Panel::setDesktop(desktop); + deskTrack.setBase(desktop); +} + +void Find::setTargetDesktop(SPDesktop *desktop) +{ + if (this->desktop != desktop) { + if (this->desktop) { + selectChangedConn.disconnect(); + } + this->desktop = desktop; + if (desktop && desktop->selection) { + selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &Find::onSelectionChange))); + } + } +} + +void Find::onSelectionChange(void) +{ + if (!blocked) { + status.set_text(""); + } +} /*######################################################################## # FIND helper functions ########################################################################*/ - -bool -Find::item_id_match (SPItem *item, const gchar *id, bool exact) +Glib::ustring Find::find_replace(const gchar *str, const gchar *find, const gchar *replace, bool exact, bool casematch, bool replaceall) { - if (item->getRepr() == NULL) { - return false; + Glib::ustring ustr = str; + Glib::ustring ufind = find; + if (!casematch) { + ufind = ufind.lowercase(); } - - if (SP_IS_STRING(item)) { // SPStrings have "on demand" ids which are useless for searching - return false; + gsize n = find_strcmp_pos(ustr.c_str(), ufind.c_str(), exact, casematch); + while (n != std::string::npos) { + ustr.replace(n, ufind.length(), replace); + if (!replaceall) { + return ustr; + } + // Start the next search after the last replace character to avoid infinite loops (replace "a" with "aaa" etc) + n = find_strcmp_pos(ustr.c_str(), ufind.c_str(), exact, casematch, n + strlen(replace) + 1); } + return ustr; +} - const gchar *item_id = item->getRepr()->attribute("id"); - if (item_id == NULL) { - return false; +gsize Find::find_strcmp_pos(const gchar *str, const gchar *find, bool exact, bool casematch, gsize start/*=0*/) +{ + Glib::ustring ustr = str; + Glib::ustring ufind = find; + + if (!casematch) { + ustr = ustr.lowercase(); + ufind = ufind.lowercase(); } + gsize pos = std::string::npos; if (exact) { - return ((bool) !strcmp(item_id, id)); + if (ustr == ufind) { + pos = 0; + } } else { -// g_print ("strstr: %s %s: %s\n", item_id, id, strstr(item_id, id) != NULL? "yes":"no"); - return ((bool) (strstr(item_id, id) != NULL)); + pos = ustr.find(ufind, start); } + + return pos; +} + + +bool Find::find_strcmp(const gchar *str, const gchar *find, bool exact, bool casematch) +{ + return (std::string::npos != find_strcmp_pos(str, find, exact, casematch)); } bool -Find::item_text_match (SPItem *item, const gchar *text, bool exact) +Find::item_text_match (SPItem *item, const gchar *find, bool exact, bool casematch, bool replace/*=false*/) { if (item->getRepr() == NULL) { return false; @@ -172,111 +342,317 @@ Find::item_text_match (SPItem *item, const gchar *text, bool exact) if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) { const gchar *item_text = sp_te_get_string_multiline (item); - if (item_text == NULL) + if (item_text == NULL) { return false; - bool ret; - if (exact) { - ret = ((bool) !strcasecmp(item_text, text)); - } else { - //FIXME: strcasestr - ret = ((bool) (strstr(item_text, text) != NULL)); } - g_free ((void*) item_text); - return ret; + bool found = find_strcmp(item_text, find, exact, casematch); + + if (found && replace) { + Glib::ustring ufind = find; + if (!casematch) { + ufind = ufind.lowercase(); + } + + Inkscape::Text::Layout const *layout = te_get_layout (item); + if (!layout) { + return found; + } + + gchar* replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str()); + gsize n = find_strcmp_pos(item_text, ufind.c_str(), exact, casematch); + static Inkscape::Text::Layout::iterator _begin_w; + static Inkscape::Text::Layout::iterator _end_w; + while (n != std::string::npos) { + _begin_w = layout->charIndexToIterator(n); + _end_w = layout->charIndexToIterator(n + strlen(find)); + sp_te_replace(item, _begin_w, _end_w, replace_text); + item_text = sp_te_get_string_multiline (item); + n = find_strcmp_pos(item_text, ufind.c_str(), exact, casematch, n + strlen(replace_text) + 1); + } + + g_free(replace_text); + } + + return found; } return false; } + bool -Find::item_style_match (SPItem *item, const gchar *text, bool exact) +Find::item_id_match (SPItem *item, const gchar *id, bool exact, bool casematch, bool replace/*=false*/) { if (item->getRepr() == NULL) { return false; } - const gchar *item_text = item->getRepr()->attribute("style"); - if (item_text == NULL) { + if (SP_IS_STRING(item)) { // SPStrings have "on demand" ids which are useless for searching + return false; + } + + const gchar *item_id = item->getRepr()->attribute("id"); + if (item_id == NULL) { + return false; + } + + bool found = find_strcmp(item_id, id, exact, casematch); + + if (found && replace) { + gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str()); + Glib::ustring new_item_style = find_replace(item_id, id, replace_text , exact, casematch, true); + if (new_item_style != item_id) { + item->getRepr()->setAttribute("id", new_item_style.data()); + } + g_free(replace_text); + } + + return found; +} + +bool +Find::item_style_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/) +{ + if (item->getRepr() == NULL) { + return false; + } + + gchar *item_style = g_strdup(item->getRepr()->attribute("style")); + if (item_style == NULL) { + return false; + } + + bool found = find_strcmp(item_style, text, exact, casematch); + + if (found && replace) { + gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str()); + Glib::ustring new_item_style = find_replace(item_style, text, replace_text , exact, casematch, true); + if (new_item_style != item_style) { + item->getRepr()->setAttribute("style", new_item_style.data()); + } + g_free(replace_text); + } + + g_free(item_style); + return found; +} + +bool Find::item_attr_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/) +{ + bool found = false; + + if (item->getRepr() == NULL) { return false; } + gchar *attr_value = g_strdup(item->getRepr()->attribute(text)); if (exact) { - return ((bool) !strcmp(item_text, text)); + found = (attr_value != NULL); } else { - return ((bool) (strstr(item_text, text) != NULL)); + found = item->getRepr()->matchAttributeName(text); } + g_free(attr_value); + + // TODO - Rename attribute name ? + if (found && replace) { + found = false; + } + + return found; } -bool Find::item_attr_match(SPItem *item, const gchar *name, bool exact) +bool Find::item_attrvalue_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/) { - bool result = false; - if (item->getRepr()) { - if (exact) { - const gchar *attr_value = item->getRepr()->attribute(name); - result = (attr_value != NULL); - } else { - result = item->getRepr()->matchAttributeName(name); + bool ret = false; + + if (item->getRepr() == NULL) { + return false; + } + + Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = item->getRepr()->attributeList(); + for (; iter; ++iter) { + const gchar* key = g_quark_to_string(iter->key); + gchar *attr_value = g_strdup(item->getRepr()->attribute(key)); + bool found = find_strcmp(attr_value, text, exact, casematch); + if (found) { + ret = true; + } + + if (found && replace) { + gchar * replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str()); + Glib::ustring new_item_style = find_replace(attr_value, text, replace_text , exact, casematch, true); + if (new_item_style != attr_value) { + item->getRepr()->setAttribute(key, new_item_style.data()); + } + } + + g_free(attr_value); + } + + return ret; +} + + +bool Find::item_font_match(SPItem *item, const gchar *text, bool exact, bool casematch, bool replace/*=false*/) +{ + bool ret = false; + + if (item->getRepr() == NULL) { + return false; + } + + const gchar *item_style = item->getRepr()->attribute("style"); + if (item_style == NULL) { + return false; + } + + std::vector<Glib::ustring> vFontTokenNames; + vFontTokenNames.push_back("font-family:"); + vFontTokenNames.push_back("-inkscape-font-specification:"); + + std::vector<Glib::ustring> vStyleTokens = Glib::Regex::split_simple(";", item_style); + for(size_t i=0; i<vStyleTokens.size(); i++) { + Glib::ustring token = vStyleTokens[i]; + for(size_t j=0; j<vFontTokenNames.size(); j++) { + if ( token.find(vFontTokenNames[j]) != std::string::npos) { + Glib::ustring font1 = Glib::ustring(vFontTokenNames[j]).append(text); + bool found = find_strcmp(token.c_str(), font1.c_str(), exact, casematch); + if (found) { + ret = true; + if (_action_replace) { + gchar *replace_text = g_strdup(entry_replace.getEntry()->get_text().c_str()); + gchar *orig_str = g_strdup(token.c_str()); + // Exact match fails since the "font-family:" is in the token, since the find was exact it still works with false below + Glib::ustring new_item_style = find_replace(orig_str, text, replace_text , false /*exact*/, casematch, true); + if (new_item_style != orig_str) { + vStyleTokens.at(i) = new_item_style; + } + g_free(orig_str); + g_free(replace_text); + } + } + } } } - return result; + + if (ret && _action_replace) { + Glib::ustring new_item_style; + for(size_t i=0; i<vStyleTokens.size(); i++) { + new_item_style.append(vStyleTokens.at(i)).append(";"); + } + new_item_style.erase(new_item_style.size()-1); + item->getRepr()->setAttribute("style", new_item_style.data()); + } + + return ret; } GSList * -Find::filter_fields (GSList *l, bool exact) +Find::filter_fields (GSList *l, bool exact, bool casematch) { - const gchar* text = _entry_text.getEntry()->get_text().c_str(); - const gchar* id = _entry_id.getEntry()->get_text().c_str(); - const gchar* style = _entry_style.getEntry()->get_text().c_str(); - const gchar* attr = _entry_attribute.getEntry()->get_text().c_str(); - + Glib::ustring tmp = entry_find.getEntry()->get_text(); + if (tmp.empty()) { + return l; + } + gchar* text = g_strdup(tmp.c_str()); + GSList *in = l; GSList *out = NULL; - if (strlen (text) != 0) { + + if (check_searchin_text.get_active()) { for (GSList *i = in; i != NULL; i = i->next) { - if (item_text_match (SP_ITEM(i->data), text, exact)) { - out = g_slist_prepend (out, i->data); + if (item_text_match (SP_ITEM(i->data), text, exact, casematch)) { + if (!g_slist_find(out, i->data)) { + out = g_slist_prepend (out, i->data); + if (_action_replace) { + item_text_match (SP_ITEM(i->data), text, exact, casematch, _action_replace); + } + } } } - } else { - out = in; } - - in = out; - out = NULL; - if (strlen (id) != 0) { - for (GSList *i = in; i != NULL; i = i->next) { - if (item_id_match (SP_ITEM(i->data), id, exact)) { - out = g_slist_prepend (out, i->data); + else if (check_searchin_property.get_active()) { + + bool ids = check_ids.get_active(); + bool style = check_style.get_active(); + bool font = check_font.get_active(); + bool attrname = check_attributename.get_active(); + bool attrvalue = check_attributevalue.get_active(); + + if (ids) { + for (GSList *i = in; i != NULL; i = i->next) { + if (item_id_match (SP_ITEM(i->data), text, exact, casematch)) { + if (!g_slist_find(out, i->data)) { + out = g_slist_prepend (out, i->data); + if (_action_replace) { + item_id_match (SP_ITEM(i->data), text, exact, casematch, _action_replace); + } + } + } } } - } else { - out = in; - } - in = out; - out = NULL; - if (strlen (style) != 0) { - for (GSList *i = in; i != NULL; i = i->next) { - if (item_style_match (SP_ITEM(i->data), style, exact)) { - out = g_slist_prepend (out, i->data); + + if (style) { + for (GSList *i = in; i != NULL; i = i->next) { + if (item_style_match (SP_ITEM(i->data), text, exact, casematch)) { + if (!g_slist_find(out, i->data)) + if (!g_slist_find(out, i->data)) { + out = g_slist_prepend (out, i->data); + if (_action_replace) { + item_style_match (SP_ITEM(i->data), text, exact, casematch, _action_replace); + } + } + } } } - } else { - out = in; - } - in = out; - out = NULL; - if (strlen (attr) != 0) { - for (GSList *i = in; i != NULL; i = i->next) { - if (item_attr_match (SP_ITEM(i->data), attr, exact)) { - out = g_slist_prepend (out, i->data); + + if (attrname) { + for (GSList *i = in; i != NULL; i = i->next) { + if (item_attr_match (SP_ITEM(i->data), text, exact, casematch)) { + if (!g_slist_find(out, i->data)) { + out = g_slist_prepend (out, i->data); + if (_action_replace) { + item_attr_match (SP_ITEM(i->data), text, exact, casematch, _action_replace); + } + } + } } } - } else { - out = in; + + + if (attrvalue) { + for (GSList *i = in; i != NULL; i = i->next) { + if (item_attrvalue_match (SP_ITEM(i->data), text, exact, casematch)) { + if (!g_slist_find(out, i->data)) { + out = g_slist_prepend (out, i->data); + if (_action_replace) { + item_attrvalue_match (SP_ITEM(i->data), text, exact, casematch, _action_replace); + } + } + } + } + } + + + if (font) { + for (GSList *i = in; i != NULL; i = i->next) { + if (item_font_match (SP_ITEM(i->data), text, exact, casematch)) { + if (!g_slist_find(out, i->data)) { + out = g_slist_prepend (out, i->data); + if (_action_replace) { + item_font_match (SP_ITEM(i->data), text, exact, casematch, _action_replace); + } + } + } + } + } + } + g_free(text); + return out; } @@ -284,37 +660,37 @@ Find::filter_fields (GSList *l, bool exact) bool Find::item_type_match (SPItem *item) { - SPDesktop *desktop = getDesktop(); + bool all =check_alltypes.get_active(); - if (SP_IS_RECT(item)) { - return (_check_all_shapes.get_active() || _check_rects.get_active()); + if ( SP_IS_RECT(item)) { + return ( all ||check_rects.get_active()); } else if (SP_IS_GENERICELLIPSE(item) || SP_IS_ELLIPSE(item) || SP_IS_ARC(item) || SP_IS_CIRCLE(item)) { - return (_check_all_shapes.get_active() || _check_ellipses.get_active()); + return ( all || check_ellipses.get_active()); } else if (SP_IS_STAR(item) || SP_IS_POLYGON(item)) { - return (_check_all_shapes.get_active() || _check_stars.get_active()); + return ( all || check_stars.get_active()); } else if (SP_IS_SPIRAL(item)) { - return (_check_all_shapes.get_active() || _check_spirals.get_active()); + return ( all || check_spirals.get_active()); } else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) { - return (_check_paths.get_active()); + return (all || check_paths.get_active()); } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_TREF(item) || SP_IS_STRING(item)) { - return (_check_texts.get_active()); + return (all || check_texts.get_active()); } else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers! - return (_check_groups.get_active()); + return (all || check_groups.get_active()); } else if (SP_IS_USE(item)) { - return (_check_clones.get_active()); + return (all || check_clones.get_active()); } else if (SP_IS_IMAGE(item)) { - return (_check_images.get_active()); + return (all || check_images.get_active()); } else if (SP_IS_OFFSET(item)) { - return (_check_offsets.get_active()); + return (all || check_offsets.get_active()); } return false; @@ -323,8 +699,6 @@ Find::item_type_match (SPItem *item) GSList * Find::filter_types (GSList *l) { - if (_check_all.get_active()) return l; - GSList *n = NULL; for (GSList *i = l; i != NULL; i = i->next) { if (item_type_match (SP_ITEM(i->data))) { @@ -336,18 +710,16 @@ Find::filter_types (GSList *l) GSList * -Find::filter_list (GSList *l, bool exact) +Find::filter_list (GSList *l, bool exact, bool casematch) { - l = filter_fields (l, exact); l = filter_types (l); + l = filter_fields (l, exact, casematch); return l; } GSList * Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked) { - SPDesktop *desktop = getDesktop(); - if (SP_IS_DEFS(r)) { return l; // we're not interested in items in defs } @@ -371,8 +743,6 @@ Find::all_items (SPObject *r, GSList *l, bool hidden, bool locked) GSList * Find::all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked) { - SPDesktop *desktop = getDesktop(); - for (GSList *i = (GSList *) s->itemList(); i != NULL; i = i->next) { if (SP_IS_ITEM (i->data) && !reinterpret_cast<SPItem *>(i->data)->cloned && !desktop->isLayer(SP_ITEM(i->data))) { SPItem *item = reinterpret_cast<SPItem *>(i->data); @@ -388,43 +758,56 @@ Find::all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor } return l; } - - + + /*######################################################################## # BUTTON CLICK HANDLERS (callbacks) ########################################################################*/ void -Find::onClear() -{ - _entry_text.getEntry()->set_text(Glib::ustring("")); - _entry_id.getEntry()->set_text(Glib::ustring("")); - _entry_style.getEntry()->set_text(Glib::ustring("")); - _entry_attribute.getEntry()->set_text(Glib::ustring("")); - - _check_all.set_active(); +Find::onFind() +{ + _action_replace = false; + onAction(); + + // Return focus to the find entry + entry_find.getEntry()->grab_focus(); } - - void -Find::onFind() -{ - SPDesktop *desktop = getDesktop(); +Find::onReplace() +{ + if (entry_find.getEntry()->get_text().length() < 1) { + status.set_text(_("Nothing to replace")); + return; + } + _action_replace = true; + onAction(); - bool hidden = _check_include_hidden.get_active(); - bool locked = _check_include_locked.get_active(); + // Return focus to the find entry + entry_find.getEntry()->grab_focus(); +} + +void +Find::onAction() +{ + + bool hidden = check_include_hidden.get_active(); + bool locked = check_include_locked.get_active(); + bool exact = check_exact_match.get_active(); + bool casematch = check_case_sensitive.get_active(); + blocked = true; GSList *l = NULL; - if (_check_search_selection.get_active()) { - if (_check_search_layer.get_active()) { + if (check_scope_selection.get_active()) { + if (check_scope_layer.get_active()) { l = all_selection_items (desktop->selection, l, desktop->currentLayer(), hidden, locked); } else { l = all_selection_items (desktop->selection, l, NULL, hidden, locked); } } else { - if (_check_search_layer.get_active()) { + if (check_scope_layer.get_active()) { l = all_items (desktop->currentLayer(), l, hidden, locked); } else { l = all_items(sp_desktop_document(desktop)->getRoot(), l, hidden, locked); @@ -432,13 +815,7 @@ Find::onFind() } guint all = g_slist_length (l); - bool exact = true; - GSList *n = NULL; - n = filter_list (l, exact); - if (n == NULL) { - exact = false; - n = filter_list (l, exact); - } + GSList *n = filter_list (l, exact, casematch); if (n != NULL) { int count = g_slist_length (n); @@ -448,88 +825,124 @@ Find::onFind() "<b>%d</b> objects found (out of <b>%d</b>), %s match.", count), count, all, exact? _("exact") : _("partial")); + status.set_text(Glib::ustring::compose("%1 %2 %3", count, _("objects"), _action_replace? _("replaced") : _("found") )); Inkscape::Selection *selection = sp_desktop_selection (desktop); selection->clear(); selection->setList(n); scroll_to_show_item (desktop, SP_ITEM(n->data)); + + if (_action_replace) { + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, _("Text Replace")); + } + } else { + status.set_text(_("Not found")); + if (!check_scope_selection.get_active()) { + Inkscape::Selection *selection = sp_desktop_selection (desktop); + selection->clear(); + } desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No objects found")); } + blocked = false; + +} +void +Find::onToggleCheck () +{ + bool objectok = false; + status.set_text(""); + + if (check_alltypes.get_active()) { + objectok = true; + } + for(int i = 0; i < 11; i++) { + if (checkTypes[i]->get_active()) { + objectok = true; + } + } + + if (!objectok) { + status.set_text(_("Select an object")); + } + + + bool propertyok = false; + + if (!check_searchin_property.get_active()) { + propertyok = true; + } else { + + for(size_t i = 0; i < checkProperties.size(); i++) { + if (checkProperties[i]->get_active()) { + propertyok = true; + } + } + } + + if (!propertyok) { + status.set_text(_("Select a property")); + } + + // Can't replace attribute names + bool attributenameyok = !check_attributename.get_active(); + + button_find.set_sensitive(objectok && propertyok); + button_replace.set_sensitive(objectok && propertyok && attributenameyok); + } void Find::onToggleAlltypes () { - if (_check_all.get_active()) { - // explicit toggle to make sure its handler gets called, no matter what was the original state - _check_all_shapes.toggled(); - _check_all_shapes.set_active(); - _check_all_shapes.hide(); - _check_paths.hide(); - _check_texts.hide(); - _check_groups.hide(); - _check_clones.hide(); - _check_images.hide(); - _check_offsets.hide(); - } else { - // explicit toggle to make sure its handler gets called, no matter what was the original state - _check_all_shapes.toggled(); - _check_all_shapes.set_active(); - _check_all_shapes.show(); - - _check_paths.set_active(); - _check_paths.show(); - _check_texts.set_active(); - _check_texts.show(); - _check_groups.set_active(); - _check_groups.show(); - _check_clones.set_active(); - _check_clones.show(); - _check_images.set_active(); - _check_images.show(); - _check_offsets.set_active(); - _check_offsets.show(); - } - squeeze_window(); + bool all =check_alltypes.get_active(); + for(size_t i = 0; i < checkTypes.size(); i++) { + checkTypes[i]->set_sensitive(!all); + } + + onToggleCheck(); } void -Find::onToggleShapes () +Find::onSearchinText () { - if (_check_all_shapes.get_active()) { - _check_rects.hide(); - _check_ellipses.hide(); - _check_stars.hide(); - _check_spirals.hide(); - } else { - _check_rects.set_active(); - _check_rects.show(); - _check_ellipses.set_active(); - _check_ellipses.show(); - _check_stars.set_active(); - _check_stars.show(); - _check_spirals.set_active(); - _check_spirals.show(); - } - squeeze_window(); + searchinToggle(false); + onToggleCheck(); } +void +Find::onSearchinProperty () +{ + searchinToggle(true); + onToggleCheck(); +} + +void +Find::searchinToggle(bool on) +{ + for(size_t i = 0; i < checkProperties.size(); i++) { + checkProperties[i]->set_sensitive(on); + } +} + +void +Find::onExpander () +{ + if (!expander_options.get_expanded()) + squeeze_window(); +} /*######################################################################## # UTILITY ########################################################################*/ - - void Find::squeeze_window() { - // TO DO: make window as small as possible + // TODO: resize dialog window when the expander is closed + // set_size_request(-1, -1); } - - } // namespace Dialog } // namespace UI } // namespace Inkscape diff --git a/src/ui/dialog/find.h b/src/ui/dialog/find.h index 15263caac..62ecd1763 100644 --- a/src/ui/dialog/find.h +++ b/src/ui/dialog/find.h @@ -16,6 +16,10 @@ #include "ui/widget/button.h" #include "ui/widget/entry.h" #include <glib.h> +#include <gtkmm.h> + +#include "desktop.h" +#include "ui/dialog/desktop-tracker.h" class SPItem; class SPObject; @@ -26,66 +30,268 @@ class Selection; namespace UI { namespace Dialog { +/** + * The Find class defines the Find and replace dialog. + * + * The Find and replace dialog allows you to search within the + * current document for specific text or properties of items. + * Matches items are highlighted and can be replaced as well. + * Scope can be limited to the entire document, current layer or selected items. + * Other options allow searching on specific object types and properties. + */ + class Find : public UI::Widget::Panel { public: Find(); virtual ~Find(); + /** + * Helper function which returns a new instance of the dialog. + * getInstance is needed by the dialog manager (Inkscape::UI::Dialog::DialogManager). + */ static Find &getInstance() { return *new Find(); } protected: - // Widgets: - Inkscape::UI::Widget::Entry _entry_text; - Inkscape::UI::Widget::Entry _entry_id; - Inkscape::UI::Widget::Entry _entry_style; - Inkscape::UI::Widget::Entry _entry_attribute; - - Inkscape::UI::Widget::CheckButton _check_search_selection; - Inkscape::UI::Widget::CheckButton _check_search_layer; - Inkscape::UI::Widget::CheckButton _check_include_hidden; - Inkscape::UI::Widget::CheckButton _check_include_locked; - - // Type checkbutton widgets... - Inkscape::UI::Widget::CheckButton _check_all; - Inkscape::UI::Widget::CheckButton _check_all_shapes; - Inkscape::UI::Widget::CheckButton _check_rects; - Inkscape::UI::Widget::CheckButton _check_ellipses; - Inkscape::UI::Widget::CheckButton _check_stars; - Inkscape::UI::Widget::CheckButton _check_spirals; - Inkscape::UI::Widget::CheckButton _check_paths; - Inkscape::UI::Widget::CheckButton _check_texts; - Inkscape::UI::Widget::CheckButton _check_groups; - Inkscape::UI::Widget::CheckButton _check_clones; - Inkscape::UI::Widget::CheckButton _check_images; - Inkscape::UI::Widget::CheckButton _check_offsets; - - // Button-click handlers - void onClear(); + + + /** + * Callbacks for pressing the dialog buttons. + */ void onFind(); + void onReplace(); + void onExpander(); + void onAction(); void onToggleAlltypes(); - void onToggleShapes(); + void onToggleCheck(); + void onSearchinText(); + void onSearchinProperty(); + /** + * Toggle all the properties checkboxes + */ + void searchinToggle(bool on); - // onFind helper functions - bool item_id_match (SPItem *item, const gchar *id, bool exact); - bool item_text_match (SPItem *item, const gchar *text, bool exact); - bool item_style_match (SPItem *item, const gchar *text, bool exact); - bool item_attr_match (SPItem *item, const gchar *name, bool exact); - GSList * filter_fields (GSList *l, bool exact); + /** + * Returns true if the SPItem 'item' has the same id + * + * @param item the SPItem to check + * @param id the value to compare with + * @param exact do an exacty match + * @param casematch match the text case exactly + * @param replace replace the value if found + * + */ + bool item_id_match (SPItem *item, const gchar *id, bool exact, bool casematch, bool replace=false); + /** + * Returns true if the SPItem 'item' has the same text content + * + * @param item the SPItem to check + * @param name the value to compare with + * @param exact do an exacty match + * @param casematch match the text case exactly + * @param replace replace the value if found + * + * + */ + bool item_text_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false); + /** + * Returns true if the SPItem 'item' has the same text in the style attribute + * + * @param item the SPItem to check + * @param name the value to compare with + * @param exact do an exacty match + * @param casematch match the text case exactly + * @param replace replace the value if found + * + */ + bool item_style_match (SPItem *item, const gchar *text, bool exact, bool casematch, bool replace=false); + /** + * Returns true if found the SPItem 'item' has the same attribute name + * + * @param item the SPItem to check + * @param name the value to compare with + * @param exact do an exacty match + * @param casematch match the text case exactly + * @param replace replace the value if found + * + */ + bool item_attr_match (SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false); + /** + * Returns true if the SPItem 'item' has the same attribute value + * + * @param item the SPItem to check + * @param name the value to compare with + * @param exact do an exacty match + * @param casematch match the text case exactly + * @param replace replace the value if found + * + */ + bool item_attrvalue_match (SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false); + /** + * Returns true if the SPItem 'item' has the same font values + * + * @param item the SPItem to check + * @param name the value to compare with + * @param exact do an exacty match + * @param casematch match the text case exactly + * @param replace replace the value if found + * + */ + bool item_font_match (SPItem *item, const gchar *name, bool exact, bool casematch, bool replace=false); + /** + * Function to filter a list of items based on the item type by calling each item_XXX_match function + */ + GSList * filter_fields (GSList *l, bool exact, bool casematch); bool item_type_match (SPItem *item); GSList * filter_types (GSList *l); - GSList * filter_list (GSList *l, bool exact); + GSList * filter_list (GSList *l, bool exact, bool casematch); + + /** + * Find a string within a string and returns true if found with options for exact and casematching + */ + bool find_strcmp(const gchar *str, const gchar *find, bool exact, bool casematch); + + /** + * Find a string within a string and return the position with options for exact, casematching and search start location + */ + gsize find_strcmp_pos(const gchar *str, const gchar *find, bool exact, bool casematch, gsize start=0); + + /** + * Replace a string with another string with options for exact and casematching and replace once/all + */ + Glib::ustring find_replace(const gchar *str, const gchar *find, const gchar *replace, bool exact, bool casematch, bool replaceall); + + /** + * recursive function to return a list of all the items in the SPObject tree + * + */ GSList * all_items (SPObject *r, GSList *l, bool hidden, bool locked); + /** + * to return a list of all the selected items + * + */ GSList * all_selection_items (Inkscape::Selection *s, GSList *l, SPObject *ancestor, bool hidden, bool locked); + /** + * Shrink the dialog size when the expander widget is closed + * Currently not working, no known way to do this + */ void squeeze_window(); + /** + * Can be invoked for setting the desktop. Currently not used. + */ + void setDesktop(SPDesktop *desktop); + /** + * Is invoked by the desktop tracker when the desktop changes. + */ + void setTargetDesktop(SPDesktop *desktop); + /** + * Called when desktop selection changes + */ + void onSelectionChange(void); + private: Find(Find const &d); Find& operator=(Find const &d); - - Inkscape::UI::Widget::Button _button_clear; - Inkscape::UI::Widget::Button _button_find; + + /* + * Find and replace combo box widgets + */ + Inkscape::UI::Widget::Entry entry_find; + Inkscape::UI::Widget::Entry entry_replace; + + /** + * Scope and search in widgets + */ + Inkscape::UI::Widget::RadioButton check_scope_all; + Inkscape::UI::Widget::RadioButton check_scope_layer; + Inkscape::UI::Widget::RadioButton check_scope_selection; + Inkscape::UI::Widget::RadioButton check_searchin_text; + Inkscape::UI::Widget::RadioButton check_searchin_property; + Gtk::HBox hbox_searchin; + Gtk::VBox vbox_scope; + Gtk::VBox vbox_searchin; + Gtk::Frame frame_searchin; + Gtk::Frame frame_scope; + + /** + * General option widgets + */ + Inkscape::UI::Widget::CheckButton check_case_sensitive; + Inkscape::UI::Widget::CheckButton check_exact_match; + Inkscape::UI::Widget::CheckButton check_include_hidden; + Inkscape::UI::Widget::CheckButton check_include_locked; + Gtk::VBox vbox_options1; + Gtk::VBox vbox_options2; + Gtk::HBox hbox_options; + Gtk::VBox vbox_expander; + Gtk::Expander expander_options; + Gtk::Frame frame_options; + + /** + * Property type widgets + */ + Inkscape::UI::Widget::CheckButton check_ids; + Inkscape::UI::Widget::CheckButton check_attributename; + Inkscape::UI::Widget::CheckButton check_attributevalue; + Inkscape::UI::Widget::CheckButton check_style; + Inkscape::UI::Widget::CheckButton check_font; + Gtk::VBox vbox_properties; + Gtk::HBox hbox_properties1; + Gtk::HBox hbox_properties2; + Gtk::Frame frame_properties; + + /** + * A vector of all the properties widgets for easy processing + */ + std::vector<Inkscape::UI::Widget::CheckButton *> checkProperties; + + /** + * Object type widgets + */ + Inkscape::UI::Widget::CheckButton check_alltypes; + Inkscape::UI::Widget::CheckButton check_rects; + Inkscape::UI::Widget::CheckButton check_ellipses; + Inkscape::UI::Widget::CheckButton check_stars; + Inkscape::UI::Widget::CheckButton check_spirals; + Inkscape::UI::Widget::CheckButton check_paths; + Inkscape::UI::Widget::CheckButton check_texts; + Inkscape::UI::Widget::CheckButton check_groups; + Inkscape::UI::Widget::CheckButton check_clones; + Inkscape::UI::Widget::CheckButton check_images; + Inkscape::UI::Widget::CheckButton check_offsets; + Gtk::VBox vbox_types1; + Gtk::VBox vbox_types2; + Gtk::HBox hbox_types; + Gtk::Frame frame_types; + + /** + * A vector of all the check option widgets for easy processing + */ + std::vector<Inkscape::UI::Widget::CheckButton *> checkTypes; + + //Gtk::HBox hbox_text; + + /** + * Action Buttons and status + */ + Gtk::Label status; + Inkscape::UI::Widget::Button button_find; + Inkscape::UI::Widget::Button button_replace; + Gtk::HButtonBox box_buttons; + Gtk::HBox hboxbutton_row; + + /** + * Finding or replacing + */ + bool _action_replace; + bool blocked; + + SPDesktop *desktop; + DesktopTracker deskTrack; + sigc::connection desktopChangeConn; + sigc::connection selectChangedConn; }; } // namespace Dialog diff --git a/src/ui/dialog/guides.cpp b/src/ui/dialog/guides.cpp index 61b4fe389..088290b6f 100644 --- a/src/ui/dialog/guides.cpp +++ b/src/ui/dialog/guides.cpp @@ -54,7 +54,7 @@ GuidelinePropertiesDialog::GuidelinePropertiesDialog(SPGuide *guide, SPDesktop * } bool GuidelinePropertiesDialog::_relative_toggle_status = false; // initialize relative checkbox status for when this dialog is opened for first time -Glib::ustring GuidelinePropertiesDialog::_angle_unit_status = "deg"; // initialize angle unit status +Glib::ustring GuidelinePropertiesDialog::_angle_unit_status = DEG; // initialize angle unit status GuidelinePropertiesDialog::~GuidelinePropertiesDialog() { // save current status @@ -87,7 +87,7 @@ void GuidelinePropertiesDialog::_modeChanged() _spin_button_x.setValue(0); } else { // absolute - _spin_angle.setValueKeepUnit(_oldangle, "deg"); + _spin_angle.setValueKeepUnit(_oldangle, DEG); _spin_button_x.setValueKeepUnit(_oldpos[Geom::X], "px"); _spin_button_y.setValueKeepUnit(_oldpos[Geom::Y], "px"); @@ -96,7 +96,7 @@ void GuidelinePropertiesDialog::_modeChanged() void GuidelinePropertiesDialog::_onApply() { - double deg_angle = _spin_angle.getValue("deg"); + double deg_angle = _spin_angle.getValue(DEG); if (!_mode) deg_angle += _oldangle; Geom::Point normal; diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp index 9b65c1f68..1951dc92a 100644 --- a/src/ui/dialog/transformation.cpp +++ b/src/ui/dialog/transformation.cpp @@ -724,7 +724,7 @@ void Transformation::applyPageScale(Inkscape::Selection *selection) void Transformation::applyPageRotate(Inkscape::Selection *selection) { - double angle = _scalar_rotate.getValue("deg"); + double angle = _scalar_rotate.getValue(DEG); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (prefs->getBool("/dialogs/transformation/applyseparately")) { diff --git a/src/ui/dialog/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp index cdbb2257c..ae1ebf5ca 100644 --- a/src/ui/dialog/xml-tree.cpp +++ b/src/ui/dialog/xml-tree.cpp @@ -233,7 +233,7 @@ XmlTree::XmlTree (void) : GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(attributes)); g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK (on_attr_select_row), this); - + g_signal_connect( G_OBJECT(attributes), "row-value-changed", G_CALLBACK(on_attr_row_changed), this); xml_element_new_button.signal_clicked().connect(sigc::mem_fun(*this, &XmlTree::cmd_new_element_node)); xml_text_new_button.signal_clicked().connect(sigc::mem_fun(*this, &XmlTree::cmd_new_text_node)); @@ -738,11 +738,12 @@ void XmlTree::on_attr_select_row(GtkTreeSelection *selection, gpointer data) GtkTreeModel *model; if (!gtk_tree_selection_get_selected (selection, &model, &iter)) { -/* self->selected_attr = 0; + // Nothing selected + self->selected_attr = 0; self->attr_reset_context(self->selected_attr); self->xml_attribute_delete_button.set_sensitive(false); - self->on_attr_unselect_row_clear_text();*/ + self->on_attr_unselect_row_clear_text(); return; } @@ -763,6 +764,22 @@ void XmlTree::on_attr_select_row(GtkTreeSelection *selection, gpointer data) } +void XmlTree::on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer data) +{ + // Reselect the selected row if the data changes to refresh the attribute and value edit boxes. + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(attributes)); + GtkTreeIter iter; + GtkTreeModel *model; + const gchar *attr_name; + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, 0, &attr_name, -1); + if (!strcmp(name, attr_name)) { + gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_select_iter(selection, &iter); + } + } +} + void XmlTree::on_attr_unselect_row_clear_text() { attr_name.set_text(""); diff --git a/src/ui/dialog/xml-tree.h b/src/ui/dialog/xml-tree.h index eeb828771..bf43f5ae8 100644 --- a/src/ui/dialog/xml-tree.h +++ b/src/ui/dialog/xml-tree.h @@ -126,11 +126,16 @@ private: static void after_tree_move(GtkCTree *tree, GtkCTreeNode *node, GtkCTreeNode *new_parent, GtkCTreeNode *new_sibling, gpointer data); /** - * Callback functions for when attribute selection changes + * Callback for when attribute selection changes */ static void on_attr_select_row(GtkTreeSelection *selection, gpointer data); /** + * Callback for when attribute list values change + */ + static void on_attr_row_changed(SPXMLViewAttrList *attributes, const gchar * name, gpointer data); + + /** * Enable widgets based on current selections */ void on_tree_select_row_enable(GtkCTreeNode *node); diff --git a/src/ui/widget/button.cpp b/src/ui/widget/button.cpp index 1ba531ddf..bac866920 100644 --- a/src/ui/widget/button.cpp +++ b/src/ui/widget/button.cpp @@ -31,6 +31,22 @@ CheckButton::CheckButton(Glib::ustring const &label, Glib::ustring const &toolti set_tooltip_text(tooltip); } +CheckButton::CheckButton(Glib::ustring const &label, Glib::ustring const &tooltip, bool active) +{ + set_use_underline (true); + set_label (label); + set_tooltip_text(tooltip); + set_active(active); +} + +RadioButton::RadioButton(Glib::ustring const &label, Glib::ustring const &tooltip) +{ + set_use_underline (true); + set_label (label); + set_tooltip_text(tooltip); +} + + } // namespace Widget } // namespace UI } // namespace Inkscape diff --git a/src/ui/widget/button.h b/src/ui/widget/button.h index 362cdf8ff..b219fc3db 100644 --- a/src/ui/widget/button.h +++ b/src/ui/widget/button.h @@ -11,6 +11,7 @@ #define INKSCAPE_UI_WIDGET_BUTTON_H #include <gtkmm/checkbutton.h> +#include <gtkmm/radiobutton.h> namespace Inkscape { namespace UI { @@ -34,6 +35,19 @@ class CheckButton : public Gtk::CheckButton public: CheckButton(); CheckButton(Glib::ustring const &label, Glib::ustring const &tooltip); + CheckButton(Glib::ustring const &label, Glib::ustring const &tooltip, bool active); + +}; + +/** + * RadioButton widget. + */ +class RadioButton : public Gtk::RadioButton +{ +public: + RadioButton(); + RadioButton(Glib::ustring const &label, Glib::ustring const &tooltip); + }; } // namespace Widget diff --git a/src/ui/widget/entry.cpp b/src/ui/widget/entry.cpp index 7ac8532fb..173e014d9 100644 --- a/src/ui/widget/entry.cpp +++ b/src/ui/widget/entry.cpp @@ -24,7 +24,6 @@ Entry::Entry( Glib::ustring const &label, Glib::ustring const &tooltip, : Labelled(label, tooltip, new Gtk::Entry(), suffix, icon, mnemonic) { } - } // namespace Widget } // namespace UI diff --git a/src/ui/widget/entry.h b/src/ui/widget/entry.h index d332dde1b..53b848fc9 100644 --- a/src/ui/widget/entry.h +++ b/src/ui/widget/entry.h @@ -11,8 +11,10 @@ #define INKSCAPE_UI_WIDGET_ENTRY__H #include "labelled.h" +#include <gtkmm.h> #include <gtkmm/entry.h> +#include <gtkmm/comboboxtext.h> namespace Inkscape { namespace UI { diff --git a/src/util/units.h b/src/util/units.h index d45109a8f..b22bdb1f2 100644 --- a/src/util/units.h +++ b/src/util/units.h @@ -32,6 +32,8 @@ enum UnitType { UNIT_TYPE_NONE = -1 }; +const char DEG[] = "°"; + class Unit { public: Glib::ustring name; diff --git a/src/verbs.cpp b/src/verbs.cpp index b00315248..9b750e123 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -39,7 +39,6 @@ #include "bind/javabind.h" #include "desktop.h" #include "desktop-handles.h" -#include "dialogs/clonetiler.h" #include "dialogs/find.h" #include "display/curve.h" #include "document.h" @@ -67,6 +66,7 @@ #include "sp-namedview.h" #include "text-chemistry.h" #include "tools-switch.h" +#include "ui/dialog/clonetiler.h" #include "ui/dialog/dialog-manager.h" #include "ui/dialog/document-properties.h" #include "ui/dialog/extensions.h" @@ -1822,9 +1822,7 @@ void DialogVerb::perform(SPAction *action, void *data) dt->_dlg_mgr->showDialog("XmlTree"); break; case SP_VERB_DIALOG_FIND: - sp_find_dialog(); -// Please test the new find dialog if you have time: -// dt->_dlg_mgr->showDialog("Find"); + dt->_dlg_mgr->showDialog("Find"); break; case SP_VERB_DIALOG_FINDREPLACE: // not implemented yet @@ -1846,7 +1844,8 @@ void DialogVerb::perform(SPAction *action, void *data) inkscape_dialogs_toggle(); break; case SP_VERB_DIALOG_CLONETILER: - clonetiler_dialog(); + //clonetiler_dialog(); + dt->_dlg_mgr->showDialog("CloneTiler"); break; case SP_VERB_DIALOG_ATTR: //sp_item_dialog(); diff --git a/src/widgets/sp-xmlview-attr-list.cpp b/src/widgets/sp-xmlview-attr-list.cpp index 1dd1f79bc..1c92476fa 100644 --- a/src/widgets/sp-xmlview-attr-list.cpp +++ b/src/widgets/sp-xmlview-attr-list.cpp @@ -54,11 +54,13 @@ sp_xmlview_attr_list_new (Inkscape::XML::Node * repr) gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_column_set_sort_column_id (column, COL_NAME); gtk_tree_sortable_set_sort_column_id ( GTK_TREE_SORTABLE(attr_list->store), COL_NAME, GTK_SORT_ASCENDING); + gtk_cell_renderer_set_padding (cell, 2, 0); cell = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(attr_list), COL_VALUE, _("Value"), cell, "text", COL_VALUE, NULL); column = gtk_tree_view_get_column (GTK_TREE_VIEW(attr_list), COL_VALUE); gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_cell_renderer_set_padding (cell, 2, 0); sp_xmlview_attr_list_set_repr (attr_list, repr); @@ -205,6 +207,6 @@ event_attr_changed (Inkscape::XML::Node * /*repr*/, } // send a "changed" signal so widget owners will know I've updated - g_signal_emit_by_name(G_OBJECT (list), "row-value-changed", row ); + g_signal_emit_by_name(G_OBJECT (list), "row-value-changed", name ); } |
