summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDenis Declara <declara91@gmail.com>2012-03-26 15:59:00 +0000
committerDenis Declara <declara91@gmail.com>2012-03-26 15:59:00 +0000
commit8874f80dd60361ed721554497862ce3a6fb5ea8e (patch)
treefe114b3559c430d1a90f073a3458f4bd1c6d2285 /src
parentAdded anchor-selection widget, it doesn't do much at the moment, next step wi... (diff)
parentpowerstroke: spiro join: more robust detection of tangent direction. fixes in... (diff)
downloadinkscape-8874f80dd60361ed721554497862ce3a6fb5ea8e.tar.gz
inkscape-8874f80dd60361ed721554497862ce3a6fb5ea8e.zip
Trunk merge
(bzr r11073.1.4)
Diffstat (limited to 'src')
-rw-r--r--src/2geom/svg-path.h8
-rw-r--r--src/dialogs/CMakeLists.txt2
-rw-r--r--src/dialogs/Makefile_insert2
-rw-r--r--src/dialogs/clonetiler.h30
-rw-r--r--src/extension/internal/pdfinput/pdf-parser.cpp22
-rw-r--r--src/live_effects/CMakeLists.txt8
-rw-r--r--src/live_effects/Makefile_insert5
-rw-r--r--src/live_effects/bezctx.cpp48
-rw-r--r--src/live_effects/bezctx.h10
-rw-r--r--src/live_effects/bezctx_intf.h20
-rw-r--r--src/live_effects/lpe-powerstroke-interpolators.h83
-rw-r--r--src/live_effects/lpe-powerstroke.cpp193
-rw-r--r--src/live_effects/lpe-powerstroke.h2
-rw-r--r--src/live_effects/lpe-spiro.cpp85
-rw-r--r--src/live_effects/spiro-converters.cpp123
-rw-r--r--src/live_effects/spiro-converters.h67
-rw-r--r--src/live_effects/spiro.cpp69
-rw-r--r--src/live_effects/spiro.h54
-rw-r--r--src/ui/CMakeLists.txt2
-rw-r--r--src/ui/context-menu.cpp1372
-rw-r--r--src/ui/dialog/Makefile_insert2
-rw-r--r--src/ui/dialog/clonetiler.cpp (renamed from src/dialogs/clonetiler.cpp)2455
-rw-r--r--src/ui/dialog/clonetiler.h183
-rw-r--r--src/ui/dialog/dialog-manager.cpp3
-rw-r--r--src/ui/dialog/find.cpp887
-rw-r--r--src/ui/dialog/find.h282
-rw-r--r--src/ui/dialog/guides.cpp6
-rw-r--r--src/ui/dialog/transformation.cpp2
-rw-r--r--src/ui/dialog/xml-tree.cpp23
-rw-r--r--src/ui/dialog/xml-tree.h7
-rw-r--r--src/ui/widget/button.cpp16
-rw-r--r--src/ui/widget/button.h14
-rw-r--r--src/ui/widget/entry.cpp1
-rw-r--r--src/ui/widget/entry.h2
-rw-r--r--src/util/units.h2
-rw-r--r--src/verbs.cpp9
-rw-r--r--src/widgets/sp-xmlview-attr-list.cpp4
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&#176; 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&#176; rotation")},
+ {TILE_PGG, _("<b>PGG</b>: glide reflection + 180&#176; rotation")},
+ {TILE_CMM, _("<b>CMM</b>: reflection + reflection + 180&#176; rotation")},
+ {TILE_P4, _("<b>P4</b>: 90&#176; rotation")},
+ {TILE_P4M, _("<b>P4M</b>: 90&#176; rotation + 45&#176; reflection")},
+ {TILE_P4G, _("<b>P4G</b>: 90&#176; rotation + 90&#176; reflection")},
+ {TILE_P3, _("<b>P3</b>: 120&#176; rotation")},
+ {TILE_P31M, _("<b>P31M</b>: reflection + 120&#176; rotation, dense")},
+ {TILE_P3M1, _("<b>P3M1</b>: reflection + 120&#176; rotation, sparse")},
+ {TILE_P6, _("<b>P6</b>: 60&#176; rotation")},
+ {TILE_P6M, _("<b>P6M</b>: reflection + 60&#176; 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, "&#176;");
+ 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, "&#176;");
+ 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), "&#215;");
+ 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), "&#215;");
+ 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&#176; 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&#176; rotation")},
- {TILE_PGG, _("<b>PGG</b>: glide reflection + 180&#176; rotation")},
- {TILE_CMM, _("<b>CMM</b>: reflection + reflection + 180&#176; rotation")},
- {TILE_P4, _("<b>P4</b>: 90&#176; rotation")},
- {TILE_P4M, _("<b>P4M</b>: 90&#176; rotation + 45&#176; reflection")},
- {TILE_P4G, _("<b>P4G</b>: 90&#176; rotation + 90&#176; reflection")},
- {TILE_P3, _("<b>P3</b>: 120&#176; rotation")},
- {TILE_P31M, _("<b>P31M</b>: reflection + 120&#176; rotation, dense")},
- {TILE_P3M1, _("<b>P3M1</b>: reflection + 120&#176; rotation, sparse")},
- {TILE_P6, _("<b>P6</b>: 60&#176; rotation")},
- {TILE_P6M, _("<b>P6M</b>: reflection + 60&#176; 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, "&#176;");
- 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, "&#176;");
- 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), "&#215;");
- 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), "&#215;");
- 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 );
}