summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShlomi Fish <shlomif@shlomifish.org>2017-02-06 16:50:07 +0000
committerShlomi Fish <shlomif@shlomifish.org>2017-02-06 16:50:07 +0000
commit1232596134bcba8d19f2809ffdc84e3b5c33d3b3 (patch)
tree2fcb91d6fe9ef47a85ba3f73be10dc5dc7ee10a4 /src
parentMerged. (diff)
parentRemove some unneeded < C++11 fallback code (diff)
downloadinkscape-1232596134bcba8d19f2809ffdc84e3b5c33d3b3.tar.gz
inkscape-1232596134bcba8d19f2809ffdc84e3b5c33d3b3.zip
Merged.
(bzr r15369.1.18)
Diffstat (limited to 'src')
-rw-r--r--src/2geom/intersection-graph.cpp6
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/attributes.cpp1
-rw-r--r--src/attributes.h1
-rw-r--r--src/box3d.cpp4
-rw-r--r--src/decimal-round.h7
-rw-r--r--src/desktop-events.cpp4
-rw-r--r--src/display/canvas-axonomgrid.cpp1
-rw-r--r--src/display/canvas-temporary-item-list.h1
-rw-r--r--src/display/nr-filter-turbulence.cpp12
-rw-r--r--src/display/nr-filter-utils.h2
-rw-r--r--src/display/nr-filter.cpp5
-rw-r--r--src/display/sp-canvas.cpp295
-rw-r--r--src/display/sp-canvas.h5
-rw-r--r--src/document-undo.cpp53
-rw-r--r--src/document.cpp74
-rw-r--r--src/document.h21
-rw-r--r--src/extension/internal/cairo-png-out.cpp4
-rw-r--r--src/extension/internal/cairo-ps-out.cpp7
-rw-r--r--src/extension/internal/cairo-render-context.cpp2
-rw-r--r--src/extension/internal/cairo-renderer-pdf-out.cpp4
-rw-r--r--src/extension/internal/emf-inout.cpp5
-rw-r--r--src/extension/internal/javafx-out.cpp9
-rw-r--r--src/extension/internal/latex-pstricks-out.cpp2
-rw-r--r--src/extension/internal/odf.cpp9
-rw-r--r--src/extension/internal/pov-out.cpp13
-rw-r--r--src/extension/internal/wmf-inout.cpp7
-rw-r--r--src/file-update.cpp173
-rw-r--r--src/file.cpp12
-rw-r--r--src/file.h3
-rw-r--r--src/filters/image.cpp5
-rw-r--r--src/helper/geom.cpp7
-rw-r--r--src/helper/geom.h2
-rw-r--r--src/inkgc/gc-core.h6
-rw-r--r--src/inkgc/gc-managed.h2
-rw-r--r--src/inkgc/gc.cpp2
-rw-r--r--src/isinf.h20
-rw-r--r--src/libcola/gradient_projection.cpp1
-rw-r--r--src/libnrtype/Layout-TNG-Compute.cpp36
-rw-r--r--src/libnrtype/Layout-TNG-Scanline-Makers.cpp2
-rw-r--r--src/live_effects/CMakeLists.txt6
-rw-r--r--src/live_effects/effect.cpp90
-rw-r--r--src/live_effects/effect.h20
-rw-r--r--src/live_effects/lpe-clone-original.cpp340
-rw-r--r--src/live_effects/lpe-clone-original.h35
-rw-r--r--src/live_effects/lpe-copy_rotate.cpp305
-rw-r--r--src/live_effects/lpe-copy_rotate.h14
-rw-r--r--src/live_effects/lpe-curvestitch.cpp12
-rw-r--r--src/live_effects/lpe-curvestitch.h1
-rw-r--r--src/live_effects/lpe-fill-between-many.cpp10
-rw-r--r--src/live_effects/lpe-fill-between-many.h1
-rw-r--r--src/live_effects/lpe-fill-between-strokes.cpp28
-rw-r--r--src/live_effects/lpe-fill-between-strokes.h2
-rw-r--r--src/live_effects/lpe-measure-line.cpp66
-rw-r--r--src/live_effects/lpe-measure-line.h8
-rw-r--r--src/live_effects/lpe-mirror_symmetry.cpp342
-rw-r--r--src/live_effects/lpe-mirror_symmetry.h13
-rw-r--r--src/live_effects/lpe-patternalongpath.cpp14
-rw-r--r--src/live_effects/lpe-perspective-envelope.cpp2
-rw-r--r--src/live_effects/lpeobject-reference.cpp4
-rw-r--r--src/live_effects/parameter/bool.cpp3
-rw-r--r--src/live_effects/parameter/item-reference.cpp44
-rw-r--r--src/live_effects/parameter/item-reference.h56
-rw-r--r--src/live_effects/parameter/item.cpp249
-rw-r--r--src/live_effects/parameter/item.h79
-rw-r--r--src/live_effects/parameter/originalitem.cpp129
-rw-r--r--src/live_effects/parameter/originalitem.h49
-rw-r--r--src/live_effects/parameter/path.cpp13
-rw-r--r--src/live_effects/parameter/text.cpp3
-rw-r--r--src/live_effects/parameter/vector.cpp2
-rw-r--r--src/main.cpp9
-rw-r--r--src/menus-skeleton.h1
-rw-r--r--src/object-set.h2
-rw-r--r--src/path-chemistry.cpp6
-rw-r--r--src/print.cpp2
-rw-r--r--src/resource-manager.cpp92
-rw-r--r--src/round.h32
-rw-r--r--src/selection-chemistry.cpp72
-rw-r--r--src/sp-conn-end.cpp3
-rw-r--r--src/sp-filter.cpp10
-rw-r--r--src/sp-flowtext.cpp2
-rw-r--r--src/sp-gradient.cpp4
-rw-r--r--src/sp-hatch.cpp5
-rw-r--r--src/sp-item-group.cpp43
-rw-r--r--src/sp-item.cpp7
-rw-r--r--src/sp-lpe-item.cpp40
-rw-r--r--src/sp-lpe-item.h5
-rw-r--r--src/sp-namedview.cpp98
-rw-r--r--src/sp-namedview.h11
-rw-r--r--src/sp-object.cpp19
-rw-r--r--src/sp-object.h3
-rw-r--r--src/sp-offset.cpp7
-rw-r--r--src/sp-pattern.cpp4
-rw-r--r--src/sp-style-elem.cpp25
-rw-r--r--src/sp-tag-use-reference.cpp5
-rw-r--r--src/sp-tag-use.cpp5
-rw-r--r--src/sp-tag-use.h1
-rw-r--r--src/sp-tref.cpp4
-rw-r--r--src/sp-use-reference.cpp4
-rw-r--r--src/sp-use.cpp2
-rw-r--r--src/style-internal.cpp206
-rw-r--r--src/style-internal.h36
-rw-r--r--src/style.cpp170
-rw-r--r--src/style.h7
-rw-r--r--src/svg/svg-length.h2
-rw-r--r--src/trace/trace.h4
-rw-r--r--src/ui/CMakeLists.txt4
-rw-r--r--src/ui/clipboard.cpp40
-rw-r--r--src/ui/dialog/aboutbox.cpp8
-rw-r--r--src/ui/dialog/cssdialog.cpp125
-rw-r--r--src/ui/dialog/cssdialog.h75
-rw-r--r--src/ui/dialog/dialog-manager.cpp6
-rw-r--r--src/ui/dialog/export.cpp9
-rw-r--r--src/ui/dialog/polar-arrange-tab.cpp4
-rw-r--r--src/ui/dialog/styledialog.cpp1116
-rw-r--r--src/ui/dialog/styledialog.h117
-rw-r--r--src/ui/tool/multi-path-manipulator.h15
-rw-r--r--src/ui/tool/node.h5
-rw-r--r--src/ui/tool/transform-handle-set.cpp5
-rw-r--r--src/ui/tools/flood-tool.cpp59
-rw-r--r--src/ui/tools/mesh-tool.cpp2
-rw-r--r--src/ui/tools/tool-base.cpp862
-rw-r--r--src/ui/tools/tool-base.h1
-rw-r--r--src/ui/widget/selected-style.cpp57
-rw-r--r--src/uri-references.cpp9
-rw-r--r--src/uri-references.h3
-rw-r--r--src/uri.cpp14
-rw-r--r--src/uri.h11
-rw-r--r--src/verbs.cpp15
-rw-r--r--src/verbs.h3
-rw-r--r--src/viewbox.cpp62
-rw-r--r--src/viewbox.h7
-rw-r--r--src/widgets/desktop-widget.cpp218
-rw-r--r--src/widgets/desktop-widget.h4
-rw-r--r--src/widgets/gimp/ruler.cpp5
-rw-r--r--src/widgets/icon.cpp14
-rw-r--r--src/widgets/text-toolbar.cpp164
-rw-r--r--src/widgets/toolbox.cpp5
-rw-r--r--src/widgets/widget-sizes.h1
-rw-r--r--src/xml/rebase-hrefs.cpp2
140 files changed, 5552 insertions, 1211 deletions
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp
index d469d3ffc..cac010942 100644
--- a/src/2geom/intersection-graph.cpp
+++ b/src/2geom/intersection-graph.cpp
@@ -410,10 +410,10 @@ PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b)
assert(!result.back().empty());
}
- /*if (n_processed != size() * 2) {
+ if (n_processed != size() * 2) {
std::cerr << "Processed " << n_processed << " intersections, expected " << (size() * 2) << std::endl;
- }*/
- assert(n_processed == size() * 2);
+ }
+ //assert(n_processed == size() * 2);
return result;
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 47aa02ef1..6df71e17e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -200,6 +200,7 @@ set(inkscape_SRC
event-log.cpp
extract-uri.cpp
file.cpp
+ file-update.cpp
filter-chemistry.cpp
filter-enums.cpp
gc-anchored.cpp
@@ -325,7 +326,6 @@ set(inkscape_SRC
id-clash.h
inkscape-version.h
inkscape.h
- isinf.h
knot-enums.h
knot-holder-entity.h
knot-ptr.h
@@ -366,7 +366,6 @@ set(inkscape_SRC
removeoverlap.h
require-config.h
resource-manager.h
- round.h
rubberband.h
satisfied-guide-cns.h
selcue.h
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 02b9450b0..8154bfbbe 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -89,6 +89,7 @@ static SPStyleProp const props[] = {
{SP_ATTR_INKSCAPE_ZOOM, "inkscape:zoom"},
{SP_ATTR_INKSCAPE_CX, "inkscape:cx"},
{SP_ATTR_INKSCAPE_CY, "inkscape:cy"},
+ {SP_ATTR_INKSCAPE_DOCUMENT_ROTATION, "inkscape:document-rotation"},
{SP_ATTR_INKSCAPE_WINDOW_WIDTH, "inkscape:window-width"},
{SP_ATTR_INKSCAPE_WINDOW_HEIGHT, "inkscape:window-height"},
{SP_ATTR_INKSCAPE_WINDOW_X, "inkscape:window-x"},
diff --git a/src/attributes.h b/src/attributes.h
index 12adcf8e2..e21851bcf 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -97,6 +97,7 @@ enum SPAttributeEnum {
SP_ATTR_INKSCAPE_ZOOM,
SP_ATTR_INKSCAPE_CX,
SP_ATTR_INKSCAPE_CY,
+ SP_ATTR_INKSCAPE_DOCUMENT_ROTATION,
SP_ATTR_INKSCAPE_WINDOW_WIDTH,
SP_ATTR_INKSCAPE_WINDOW_HEIGHT,
SP_ATTR_INKSCAPE_WINDOW_X,
diff --git a/src/box3d.cpp b/src/box3d.cpp
index dfc4d06e7..23766895a 100644
--- a/src/box3d.cpp
+++ b/src/box3d.cpp
@@ -15,12 +15,14 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "box3d.h"
+
#include <glibmm/i18n.h>
#include "attributes.h"
#include "xml/document.h"
#include "xml/repr.h"
-#include "box3d.h"
+#include "bad-uri-exception.h"
#include "box3d-side.h"
#include "ui/tools/box3d-tool.h"
#include "perspective-line.h"
diff --git a/src/decimal-round.h b/src/decimal-round.h
index 6b412685e..b90958cae 100644
--- a/src/decimal-round.h
+++ b/src/decimal-round.h
@@ -3,14 +3,11 @@
#include <cmath>
-#include "round.h"
-
-
namespace Inkscape {
/** Returns x rounded to the nearest \a nplaces decimal places.
- Implemented in terms of Inkscape::round, i.e. we make no guarantees as to what happens if x is
+ Implemented in terms of std::round, i.e. we make no guarantees as to what happens if x is
half way between two rounded numbers. Add a note to the documentation if you care which
candidate is chosen for such case (round towards -infinity, +infinity, 0, or "even").
@@ -25,7 +22,7 @@ inline double
decimal_round(double const x, int const nplaces)
{
double const multiplier = std::pow(10.0, nplaces);
- return Inkscape::round( x * multiplier ) / multiplier;
+ return round( x * multiplier ) / multiplier;
}
}
diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp
index 5fef8cbfc..9e5f1c20b 100644
--- a/src/desktop-events.cpp
+++ b/src/desktop-events.cpp
@@ -155,6 +155,10 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge
}
}
+ SPNamedView *namedview = desktop->namedview;
+ //if (namedview && namedview->document_rotation) {
+ // normal *= Geom::Rotate(Geom::rad_from_deg(namedview->document_rotation * -1));
+ //}
guide = sp_guideline_new(desktop->guides, NULL, event_dt, normal);
sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor);
diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp
index 94ee4ccfd..589cc849d 100644
--- a/src/display/canvas-axonomgrid.cpp
+++ b/src/display/canvas-axonomgrid.cpp
@@ -42,7 +42,6 @@
#include "2geom/line.h"
#include "2geom/angle.h"
#include "helper/mathfns.h"
-#include "round.h"
#include "util/units.h"
using Inkscape::Util::unit_table;
diff --git a/src/display/canvas-temporary-item-list.h b/src/display/canvas-temporary-item-list.h
index 471bb99b9..6b0e5796b 100644
--- a/src/display/canvas-temporary-item-list.h
+++ b/src/display/canvas-temporary-item-list.h
@@ -14,6 +14,7 @@
struct SPCanvasItem;
class SPDesktop;
+class SPViewBox;
namespace Inkscape {
namespace Display {
diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp
index 19dedb67c..1397c0f34 100644
--- a/src/display/nr-filter-turbulence.cpp
+++ b/src/display/nr-filter-turbulence.cpp
@@ -286,15 +286,7 @@ private:
static int const BSize = 0x100;
static int const BMask = 0xff;
-#ifdef CPP11 // GCC 4.6.1 (currently used on Windows) does not correctly set __cplusplus in C++11 mode, so configure with -DCPP11 to make this work in C++11 mode
static double constexpr PerlinOffset = 4096.0;
-#else
-#if (__cplusplus < 201103L)
- static double const PerlinOffset;
-#else
- static double constexpr PerlinOffset = 4096.0;
-#endif
-#endif
Geom::Rect _tile;
Geom::Point _baseFreq;
@@ -311,10 +303,6 @@ private:
bool _fractalnoise;
};
-#if !defined(CPP11) && __cplusplus < 201103L
- double const TurbulenceGenerator::PerlinOffset = 4096.0;
-#endif
-
FilterTurbulence::FilterTurbulence()
: gen(new TurbulenceGenerator())
, XbaseFrequency(0)
diff --git a/src/display/nr-filter-utils.h b/src/display/nr-filter-utils.h
index 35a74d7c1..52b6bd11a 100644
--- a/src/display/nr-filter-utils.h
+++ b/src/display/nr-filter-utils.h
@@ -14,8 +14,6 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include "round.h"
-
namespace Inkscape {
namespace Filters {
diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp
index 789758d9c..d430553d4 100644
--- a/src/display/nr-filter.cpp
+++ b/src/display/nr-filter.cpp
@@ -48,11 +48,6 @@
#include "sp-filter-units.h"
#include "preferences.h"
-#if defined (SOLARIS) && (SOLARIS == 8)
-#include "round.h"
-using Inkscape::round;
-#endif
-
namespace Inkscape {
namespace Filters {
diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp
index 416c2c804..68eae0a65 100644
--- a/src/display/sp-canvas.cpp
+++ b/src/display/sp-canvas.cpp
@@ -29,18 +29,24 @@
#include "helper/sp-marshal.h"
#include <2geom/rect.h>
#include <2geom/affine.h>
-#include "display/cairo-utils.h"
#include "display/sp-canvas.h"
#include "display/sp-canvas-group.h"
+#include "display/rendermode.h"
+#include "display/cairo-utils.h"
+#include "display/cairo-templates.h"
+#include "display/drawing-context.h"
+#include "display/drawing-item.h"
+#include "display/nr-filter-colormatrix.h"
+#include "display/canvas-arena.h"
#include "preferences.h"
#include "inkscape.h"
#include "sodipodi-ctrlrect.h"
#include "cms-system.h"
-#include "display/rendermode.h"
-#include "display/cairo-utils.h"
#include "debug/gdk-event-latency-tracker.h"
#include "desktop.h"
#include "color.h"
+#include <iomanip>
+#include <glibmm/i18n.h>
using Inkscape::Debug::GdkEventLatencyTracker;
@@ -1888,6 +1894,19 @@ SPCanvasGroup *SPCanvas::getRoot()
return SP_CANVAS_GROUP(_root);
}
+gdouble grayscale_value_matrix[20] = {
+ 0.21, 0.72, 0.072, 0, 0,
+ 0.21, 0.72, 0.072, 0, 0,
+ 0.21, 0.72, 0.072, 0, 0,
+ 0 , 0 , 0 , 1, 0
+ };
+cairo_surface_t *surface_rotated;
+cairo_surface_t *surface_origin;
+cairo_surface_t *surface_measure;
+double start_angle = 0;
+bool started = false;
+bool rotated = false;
+
void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling)
{
GtkAllocation allocation;
@@ -1910,9 +1929,14 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
// Paint the background
cairo_translate(cr, -ix, -iy);
+ if (rotated) {
+ cairo_translate(cr, dx, dy);
+ rotated = false;
+ }
cairo_set_source(cr, _background);
cairo_paint(cr);
// Copy the old backing store contents
+
cairo_set_source_surface(cr, _backing_store, _x0, _y0);
cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height);
cairo_clip(cr);
@@ -1949,6 +1973,271 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll
addIdle();
}
+void SPCanvas::startRotateTo(double angle)
+{
+ if (!_backing_store || started) {
+ return;
+ }
+ start_angle = angle;
+ started = true;
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(&_widget, &allocation);
+ int half_w = allocation.width/2;
+ int half_h = allocation.height/2;
+ int half_min = std::min(half_w,half_h);
+
+ cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cairo_t *cr = cairo_create(new_backing_store);
+ cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI);
+ cairo_fill(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_IN);
+ cairo_set_source_surface(cr, _backing_store, 0, 0);
+ cairo_paint(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_arc(cr, half_w, half_h, half_min-16, 0, 2*M_PI);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ surface_rotated = new_backing_store;
+
+ cairo_surface_t *new_backing_store_measure = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cr = cairo_create(new_backing_store_measure);
+ cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
+ cairo_fill(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 10.0);
+ for (gint x = 0; x < 360 ; x++){
+ gint ang = 360 - x ;//+ 90;
+ if (ang > 180) {
+ ang -= 360;
+ }
+ double rot = (-180.0 + x)*(M_PI/180.);
+ double dist = half_min-9;
+ gint inverse = 1;
+ if((x) < 91 || (x) > 270) {
+ inverse = -1;
+ }
+ if(x%10 == 0) {
+ cairo_rotate(cr, -rot);
+ cairo_text_extents_t extents;
+ std::string s = std::to_string(ang) + "º";
+ cairo_text_extents(cr, s.c_str(), &extents);
+ //std::cout << extents.width/2 << "extents.x_bearing\n";
+ cairo_translate(cr, (extents.width/2) * inverse * -1, (dist + ((extents.height/2)* inverse)));
+ if((x) < 91 || (x) > 270) {
+ cairo_rotate(cr, 180*(M_PI/180.0));
+ }
+ cairo_text_path(cr, s.c_str());
+ if((x) < 91 || (x) > 270) {
+ cairo_rotate(cr, -180*(M_PI/180.0));
+ }
+ cairo_translate(cr, (extents.width/2) * inverse , (dist + ((extents.height/2)* inverse)) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_fill(cr);
+ cairo_rotate(cr, rot);
+ }
+ cairo_rotate(cr, x*(M_PI/180.));
+ if(x%5 == 0) {
+ cairo_move_to(cr, 0, half_min-30);
+ cairo_line_to(cr, 0, half_min-17);
+ } else {
+ cairo_move_to(cr, 0, half_min-20);
+ cairo_line_to(cr, 0, half_min-15);
+ }
+ cairo_line_to(cr, 0, half_min-15);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.4);
+ cairo_set_line_width (cr,1);
+ cairo_stroke(cr);
+ cairo_rotate(cr, -x*(M_PI/180.));
+ }
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ cairo_translate(cr, -half_w, -half_h);
+ cairo_arc(cr, half_w, half_h, half_min-30, 0, 2*M_PI);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_fill(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_rotate(cr, start_angle*(M_PI/180.));
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width (cr,5);
+ cairo_stroke(cr);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.9);
+ cairo_set_line_width (cr,1);
+ cairo_stroke(cr);
+ cairo_rotate(cr, -start_angle*(M_PI/180.));
+ cairo_destroy(cr);
+ surface_measure = new_backing_store_measure;
+
+ cairo_surface_t *new_backing_store_grey = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cr = cairo_create(new_backing_store_grey);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface(cr, _backing_store, 0, 0);
+ cairo_paint(cr);
+ Inkscape::Filters::FilterColorMatrix::ColorMatrixMatrix _grayscale_colormatrix = std::vector<gdouble> (grayscale_value_matrix, grayscale_value_matrix + 20);
+ cairo_surface_t *out = ink_cairo_surface_create_identical(new_backing_store_grey);
+ ink_cairo_surface_filter(new_backing_store_grey, out, _grayscale_colormatrix);
+ cairo_set_source_surface(cr, out, 0, 0);
+ cairo_surface_destroy(out);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ surface_origin = new_backing_store_grey;
+}
+
+bool SPCanvas::endRotateTo()
+{
+ if (!_backing_store || !started) {
+ return false;
+ }
+ started = false;
+ surface_rotated = NULL;
+ surface_origin = NULL;
+ gtk_widget_queue_draw(GTK_WIDGET(this));
+ dirtyAll();
+ addIdle();
+ rotated = true;
+ return true;
+}
+
+void SPCanvas::clearRotateTo()
+{
+ if (!started) {
+ return;
+ }
+ gtk_widget_queue_draw(GTK_WIDGET(this));
+ dirtyAll();
+ addIdle();
+}
+
+void SPCanvas::rotateTo(double angle)
+{
+ if (!_backing_store || !started) {
+ return;
+ }
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(&_widget, &allocation);
+ int half_w = allocation.width/2;
+ int half_h = allocation.height/2;
+ int half_min = std::min(half_w,half_h);
+ cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+ cairo_t *cr = cairo_create(new_backing_store);
+ cairo_set_source_surface(cr, surface_origin, 0, 0);
+ cairo_paint(cr);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
+ cairo_paint(cr);
+ cairo_pattern_t *source_pattern;
+ cairo_matrix_t matrix;
+ source_pattern = cairo_pattern_create_for_surface (surface_rotated);
+ cairo_matrix_init_identity (&matrix);
+ cairo_matrix_translate (&matrix, allocation.width/2.0, allocation.height/2.0);
+ cairo_matrix_rotate (&matrix, Geom::rad_from_deg(angle - start_angle) * -1);
+ cairo_matrix_translate (&matrix, -allocation.width/2.0, -allocation.height/2.0);
+ cairo_pattern_set_matrix (source_pattern, &matrix);
+ cairo_set_source(cr, source_pattern);
+ cairo_paint(cr);
+ cairo_set_source_surface(cr, surface_measure, 0, 0);
+ cairo_paint(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_rotate(cr, angle*(M_PI/180.));
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width (cr,5);
+ cairo_stroke(cr);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.9);
+ cairo_set_line_width (cr,1);
+ cairo_stroke(cr);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0, (half_min-17) * -1);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.9);
+ const double dashed[] = {6.0, 3.0};
+ int len = sizeof(dashed) / sizeof(dashed[0]);
+ cairo_set_dash(cr, dashed, len, 1);
+ cairo_stroke(cr);
+ cairo_translate(cr, -half_w, -half_h);
+ cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+ cairo_arc(cr, half_w, half_h, 7, 0, 2*M_PI);
+ cairo_fill(cr);
+ cairo_set_source_rgba (cr, 1, 0, 0, 0.7);
+ cairo_arc(cr, half_w, half_h, 5, 0, 2*M_PI);
+ cairo_fill(cr);
+ cairo_translate(cr, half_w, half_h);
+ cairo_rotate(cr, -angle*(M_PI/180.));
+ cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ cairo_set_font_size(cr, 15.0);
+ cairo_text_extents_t extents;
+ std::ostringstream s;
+ s << _("Original angle ") << std::fixed << std::setprecision(2) << start_angle << "º";
+ cairo_text_extents(cr, s.str().c_str(), &extents);
+ cairo_translate(cr, half_w - extents.width -15 ,-half_h + 25);
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 25) *-1);
+ s.str("");
+ s << _("New angle ") << std::fixed << std::setprecision(2) << angle << "º";
+ cairo_text_extents(cr, s.str().c_str(), &extents);
+ cairo_translate(cr, half_w - extents.width -15 ,-half_h + 45);
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 45) *-1);
+ s.str("");
+ s << _("Gap ") << std::fixed << std::setprecision(2) << std::abs(start_angle-angle) << "º";
+ cairo_text_extents(cr, s.str().c_str(), &extents);
+ cairo_translate(cr, half_w - extents.width -15 ,-half_h + 65);
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 65) *-1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 25);
+ s.str("");
+ cairo_set_font_size(cr, 12.0);
+ s << _("Normal mode, 1º round step");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w +10) * -1 ,(-half_h + 25) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 40);
+ s.str("");
+ s << _("+ALT, Fractional degrees");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 40) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 55);
+ s.str("");
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ s << _("+CTRL, ") << 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12) << _("º round step");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 55) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 70);
+ s.str("");
+ s << _("+SHIFT, Reset");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 70) * -1);
+ cairo_translate(cr, -half_w + 10 ,-half_h + 85);
+ s.str("");
+ s << _("+CTRL+SHIFT, 0º");
+ cairo_text_path(cr, s.str().c_str());
+ cairo_fill(cr);
+ //cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 60) * -1);
+ cairo_destroy(cr);
+ cairo_surface_destroy(_backing_store);
+ _backing_store = new_backing_store;
+ cairo_pattern_destroy (source_pattern);
+ gtk_widget_queue_draw(GTK_WIDGET(this));
+ addIdle();
+}
+
void SPCanvas::updateNow()
{
if (_need_update) {
diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h
index 78d96d728..21b6760f2 100644
--- a/src/display/sp-canvas.h
+++ b/src/display/sp-canvas.h
@@ -72,7 +72,10 @@ GType sp_canvas_get_type() G_GNUC_CONST;
struct SPCanvas {
/// Scrolls canvas to specific position (cx and cy are measured in screen pixels).
void scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling = false);
-
+ void startRotateTo(double angle);
+ void rotateTo(double angle);
+ bool endRotateTo();
+ void clearRotateTo();
/// Synchronously updates the canvas if necessary.
void updateNow();
diff --git a/src/document-undo.cpp b/src/document-undo.cpp
index 113d09d66..ae90c916e 100644
--- a/src/document-undo.cpp
+++ b/src/document-undo.cpp
@@ -240,49 +240,52 @@ static void perform_document_update(SPDocument &doc) {
gboolean Inkscape::DocumentUndo::undo(SPDocument *doc)
{
- using Inkscape::Debug::EventTracker;
- using Inkscape::Debug::SimpleEvent;
+ using Inkscape::Debug::EventTracker;
+ using Inkscape::Debug::SimpleEvent;
- gboolean ret;
+ gboolean ret;
- EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo");
+ EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo");
- g_assert (doc != NULL);
- g_assert (doc->priv != NULL);
- g_assert (doc->priv->sensitive);
+ g_assert (doc != NULL);
+ g_assert (doc->priv != NULL);
+ g_assert (doc->priv->sensitive);
- doc->priv->sensitive = FALSE;
+ doc->priv->sensitive = FALSE;
doc->priv->seeking = true;
- doc->actionkey.clear();
+ doc->actionkey.clear();
- finish_incomplete_transaction(*doc);
+ finish_incomplete_transaction(*doc);
- if (! doc->priv->undo.empty()) {
- Inkscape::Event *log = doc->priv->undo.back();
- doc->priv->undo.pop_back();
- sp_repr_undo_log (log->event);
- perform_document_update(*doc);
+ if (! doc->priv->undo.empty()) {
+ Inkscape::Event *log = doc->priv->undo.back();
+ doc->priv->undo.pop_back();
+ sp_repr_undo_log (log->event);
+ perform_document_update(*doc);
- doc->priv->redo.push_back(log);
+ doc->priv->redo.push_back(log);
doc->setModifiedSinceSave();
doc->priv->undoStackObservers.notifyUndoEvent(log);
- ret = TRUE;
- } else {
- ret = FALSE;
- }
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
- sp_repr_begin_transaction (doc->rdoc);
+ sp_repr_begin_transaction (doc->rdoc);
- doc->priv->sensitive = TRUE;
+ doc->priv->sensitive = TRUE;
doc->priv->seeking = false;
- if (ret)
- INKSCAPE.external_change();
+ if (ret) INKSCAPE.external_change();
- return ret;
+ SPObject *updated = doc->getRoot();
+ if (updated) {
+ updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ return ret;
}
gboolean Inkscape::DocumentUndo::redo(SPDocument *doc)
diff --git a/src/document.cpp b/src/document.cpp
index 0c77a8f48..b69508751 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -49,6 +49,7 @@
#include "display/drawing.h"
#include "document-private.h"
#include "document-undo.h"
+#include "file.h"
#include "id-clash.h"
#include "inkscape.h"
#include "inkscape-version.h"
@@ -71,7 +72,7 @@ using Inkscape::Util::unit_table;
// since we want it to happen when there are no more updates.
#define SP_DOCUMENT_REROUTING_PRIORITY (G_PRIORITY_HIGH_IDLE - 1)
-
+bool sp_no_convert_text_baseline_spacing = false;
static gint sp_document_idle_handler(gpointer data);
static gint sp_document_rerouting_handler(gpointer data);
@@ -386,6 +387,7 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc,
if (!bordercolor.empty()) {
rnew->setAttribute("bordercolor", bordercolor.data());
}
+ sp_repr_set_svg_double(rnew, "inkscape:document-rotation", 0.);
sp_repr_set_svg_double(rnew, "borderopacity",
prefs->getDouble("/template/base/borderopacity", 1.0));
sp_repr_set_svg_double(rnew, "objecttolerance",
@@ -407,6 +409,11 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc,
rroot->addChild(rnew, NULL);
// clean up
Inkscape::GC::release(rnew);
+ } else {
+ Inkscape::XML::Node *nv_repr = sp_item_group_get_child_by_name(document->root, NULL, "sodipodi:namedview")->getRepr();
+ if (!nv_repr->attribute("inkscape:document-rotation")) {
+ sp_repr_set_svg_double(nv_repr, "inkscape:document-rotation", 0.);
+ }
}
// Defs
@@ -446,6 +453,17 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc,
));
document->oldSignalsConnected = true;
+ /** Fix baseline spacing (pre-92 files) **/
+ if ( (!sp_no_convert_text_baseline_spacing)
+ && sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) {
+ sp_file_convert_text_baseline_spacing(document);
+ }
+
+ /** Fix font names in legacy documents (pre-92 files) **/
+ if ( sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) {
+ sp_file_convert_font_name(document);
+ }
+
return document;
}
@@ -1047,6 +1065,60 @@ sigc::connection SPDocument::connectIdChanged(gchar const *id,
return priv->id_changed_signals[g_quark_from_string(id)].connect(slot);
}
+void _getObjectsByClassRecursive(Glib::ustring const &klass, SPObject *parent, std::vector<SPObject *> &objects)
+{
+ if (parent) {
+ Glib::ustring class_attribute;
+ char const *temp = parent->getAttribute("class");
+ if (temp) {
+ class_attribute = temp;
+ }
+
+ if (class_attribute.find( klass ) != std::string::npos) {
+ objects.push_back( parent );
+ }
+
+ // Check children
+ for (auto& child : parent->children) {
+ _getObjectsByClassRecursive( klass, &child, objects );
+ }
+ }
+}
+
+std::vector<SPObject *> SPDocument::getObjectsByClass(Glib::ustring const &klass) const
+{
+ std::vector<SPObject *> objects;
+ g_return_val_if_fail(!klass.empty(), objects);
+
+ _getObjectsByClassRecursive(klass, root, objects);
+ return objects;
+}
+
+void _getObjectsByElementRecursive(Glib::ustring const &element, SPObject *parent,
+ std::vector<SPObject *> &objects)
+{
+ if (parent) {
+ Glib::ustring prefixed = "svg:" + element;
+ if (parent->getRepr()->name() == prefixed) {
+ objects.push_back(parent);
+ }
+
+ // Check children
+ for (auto& child : parent->children) {
+ _getObjectsByElementRecursive(element, &child, objects);
+ }
+ }
+}
+
+std::vector<SPObject *> SPDocument::getObjectsByElement(Glib::ustring const &element) const
+{
+ std::vector<SPObject *> objects;
+ g_return_val_if_fail(!element.empty(), objects);
+
+ _getObjectsByElementRecursive(element, root, objects);
+ return objects;
+}
+
void SPDocument::bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object)
{
if (object) {
diff --git a/src/document.h b/src/document.h
index 813d4ae49..142eb5000 100644
--- a/src/document.h
+++ b/src/document.h
@@ -30,6 +30,24 @@
#include <set>
#include <deque>
+// This variable is introduced with 0.92.1
+// with the introduction of automatic fix
+// for files detected to have been created
+// with previous versions to have a similar
+// look in 0.92+.
+extern bool sp_no_convert_text_baseline_spacing;
+
+
+
+// This variable is introduced with 0.92.1
+// with the introduction of automatic fix
+// for files detected to have been created
+// with previous versions to have a similar
+// look in 0.92+.
+extern bool sp_do_not_fix_pre_92;
+
+
+
namespace Avoid {
class Router;
}
@@ -179,6 +197,9 @@ public:
SPObject *getObjectById(char const *id) const;
sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot);
+ std::vector<SPObject *> getObjectsByClass(Glib::ustring const &klass) const;
+ std::vector<SPObject *> getObjectsByElement(Glib::ustring const &element) const;
+
void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object);
SPObject *getObjectByRepr(Inkscape::XML::Node *repr) const;
diff --git a/src/extension/internal/cairo-png-out.cpp b/src/extension/internal/cairo-png-out.cpp
index 956fcce9a..5859a82da 100644
--- a/src/extension/internal/cairo-png-out.cpp
+++ b/src/extension/internal/cairo-png-out.cpp
@@ -53,11 +53,10 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename)
{
CairoRenderer *renderer;
CairoRenderContext *ctx;
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
/* Start */
-
SPItem *base = doc->getRoot();
Inkscape::Drawing drawing;
unsigned dkey = SPItem::display_key_new(1);
@@ -77,6 +76,7 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename)
renderer->destroyContext(ctx);
base->invoke_hide(dkey);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
/* end */
delete renderer;
diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp
index e8f47e79e..809125266 100644
--- a/src/extension/internal/cairo-ps-out.cpp
+++ b/src/extension/internal/cairo-ps-out.cpp
@@ -68,6 +68,7 @@ static bool
ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool omittext,
bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px, bool eps = false)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
SPItem *base = NULL;
@@ -84,9 +85,10 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l
pageBoundingBox = !exportDrawing;
}
- if (!base)
+ if (!base) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
-
+ }
Inkscape::Drawing drawing;
unsigned dkey = SPItem::display_key_new(1);
base->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY);
@@ -115,6 +117,7 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l
renderer->destroyContext(ctx);
delete renderer;
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return ret;
}
diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp
index e65752c84..06711bca4 100644
--- a/src/extension/internal/cairo-render-context.cpp
+++ b/src/extension/internal/cairo-render-context.cpp
@@ -865,7 +865,7 @@ CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t
cairo_scale(_cr, Inkscape::Util::Quantity::convert(1, "px", "pt"), Inkscape::Util::Quantity::convert(1, "px", "pt"));
} else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) {
// set background color on non-alpha surfaces
- // TODO: bgcolor should be derived from SPDocument
+ // TODO: bgcolor should be derived from SPDocument (see IconImpl)
cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0);
cairo_rectangle(_cr, 0, 0, _width, _height);
cairo_fill(_cr);
diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp
index 5576676b2..5558fe1ad 100644
--- a/src/extension/internal/cairo-renderer-pdf-out.cpp
+++ b/src/extension/internal/cairo-renderer-pdf-out.cpp
@@ -61,6 +61,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
bool texttopath, bool omittext, bool filtertobitmap, int resolution,
const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
/* Start */
@@ -80,6 +81,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
}
if (!base) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
@@ -112,7 +114,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int
renderer->destroyContext(ctx);
delete renderer;
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return ret;
}
diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp
index 12751c5ec..198c18ff2 100644
--- a/src/extension/internal/emf-inout.cpp
+++ b/src/extension/internal/emf-inout.cpp
@@ -94,7 +94,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
const gchar *oldconst;
gchar *oldoutput;
unsigned int ret;
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
mod = Inkscape::Extension::get_print(PRINT_EMF);
@@ -114,6 +114,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
/* Print document */
ret = mod->begin(doc);
if (ret) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
g_free(oldoutput);
throw Inkscape::Extension::Output::save_failed();
}
@@ -127,7 +128,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename)
mod->set_param_string("destination", oldoutput);
g_free(oldoutput);
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp
index d7ad7e6f7..c95434939 100644
--- a/src/extension/internal/javafx-out.cpp
+++ b/src/extension/internal/javafx-out.cpp
@@ -843,7 +843,8 @@ void JavaFXOutput::reset()
bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
{
reset();
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
name = Glib::path_get_basename(filename_utf8);
int pos = name.find('.');
@@ -856,12 +857,14 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
//# Lets do the curves first, to get the stats
if (!doTree(doc)) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
String curveBuf = outbuf;
outbuf.clear();
if (!doHeader()) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
@@ -875,6 +878,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
doBody(doc, doc->getRoot());
if (!doTail()) {
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return false;
}
@@ -884,6 +888,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
if (!f)
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could open JavaFX file '%s' for writing", filename_utf8);
return false;
}
@@ -894,7 +899,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
}
fclose(f);
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return true;
}
diff --git a/src/extension/internal/latex-pstricks-out.cpp b/src/extension/internal/latex-pstricks-out.cpp
index 3ce2c5531..aa6ea6963 100644
--- a/src/extension/internal/latex-pstricks-out.cpp
+++ b/src/extension/internal/latex-pstricks-out.cpp
@@ -49,6 +49,7 @@ bool LatexOutput::check(Inkscape::Extension::Extension * /*module*/)
void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc, gchar const *filename)
{
SPPrintContext context;
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
Inkscape::Extension::Print *mod = Inkscape::Extension::get_print(SP_MODULE_KEY_PRINT_LATEX);
@@ -76,6 +77,7 @@ void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc,
mod->set_param_string("destination", oldoutput);
g_free(oldoutput);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
#include "clear-n_.h"
diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp
index f885ef5e5..66d370357 100644
--- a/src/extension/internal/odf.cpp
+++ b/src/extension/internal/odf.cpp
@@ -72,6 +72,7 @@
#include "sp-path.h"
#include "sp-text.h"
#include "sp-flowtext.h"
+#include "sp-root.h"
#include "svg/svg.h"
#include "text-editing.h"
#include "util/units.h"
@@ -2095,7 +2096,8 @@ void OdfOutput::reset()
void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename)
{
reset();
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
documentUri = Inkscape::URI(filename);
ZipFile zf;
@@ -2104,25 +2106,30 @@ void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gcha
if (!writeManifest(zf))
{
g_warning("Failed to write manifest");
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
if (!writeContent(zf, doc->rroot))
{
g_warning("Failed to write content");
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
if (!writeMeta(zf))
{
g_warning("Failed to write metafile");
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
if (!zf.writeFile(filename))
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp
index 8df883069..03a2ecd62 100644
--- a/src/extension/internal/pov-out.cpp
+++ b/src/extension/internal/pov-out.cpp
@@ -616,11 +616,13 @@ void PovOutput::reset()
void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
{
reset();
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
//###### SAVE IN POV FORMAT TO BUFFER
//# Lets do the curves first, to get the stats
if (!doTree(doc))
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could not output curves for %s", filename_utf8);
return;
}
@@ -630,6 +632,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
if (!doHeader())
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could not write header for %s", filename_utf8);
return;
}
@@ -638,6 +641,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
if (!doTail())
{
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
err("Could not write footer for %s", filename_utf8);
return;
}
@@ -648,9 +652,11 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
//###### WRITE TO FILE
Inkscape::IO::dump_fopen_call(filename_utf8, "L");
FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
- if (!f)
+ if (!f){
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
-
+ }
+
for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); ++iter)
{
int ch = *iter;
@@ -658,6 +664,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
}
fclose(f);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp
index c7226a58a..a79af5ec1 100644
--- a/src/extension/internal/wmf-inout.cpp
+++ b/src/extension/internal/wmf-inout.cpp
@@ -95,7 +95,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
SPPrintContext context;
const gchar *oldconst;
gchar *oldoutput;
-
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
mod = Inkscape::Extension::get_print(PRINT_WMF);
@@ -115,6 +115,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
/* Print document */
if (mod->begin(doc)) {
g_free(oldoutput);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
throw Inkscape::Extension::Output::save_failed();
}
mod->base->invoke_print(&context);
@@ -127,7 +128,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
mod->set_param_string("destination", oldoutput);
g_free(oldoutput);
-
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -135,6 +136,8 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename)
void
Wmf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
Inkscape::Extension::Extension * ext;
ext = Inkscape::Extension::db.get(PRINT_WMF);
diff --git a/src/file-update.cpp b/src/file-update.cpp
new file mode 100644
index 000000000..40a8bc834
--- /dev/null
+++ b/src/file-update.cpp
@@ -0,0 +1,173 @@
+/**
+ * @file-update
+ * Operations to bump files from the pre-0.92 era into the 0.92+ era
+ * (90dpi vs 96dpi, line height problems, and related bugs)
+ */
+/* Authors:
+ * Tavmjong Bah
+ * Marc Jeanmougin
+ * su_v
+ */
+#include "file.h"
+#include "sp-root.h"
+#include "sp-text.h"
+#include "sp-tspan.h"
+#include "sp-flowdiv.h"
+#include "sp-flowtext.h"
+#include "sp-object.h"
+#include "sp-item.h"
+#include "style.h"
+#include "document.h"
+#include <string>
+#include "text-editing.h"
+
+using namespace std;
+
+bool is_line(SPObject *i)
+{
+ if (!(i->getAttribute("sodipodi:role")))
+ return false;
+ return !strcmp(i->getAttribute("sodipodi:role"), "line");
+}
+
+
+void fix_blank_line(SPObject *o)
+{
+ if (SP_IS_TEXT(o))
+ ((SPText *)o)->rebuildLayout();
+ else if (SP_IS_FLOWTEXT(o))
+ ((SPFlowtext *)o)->rebuildLayout();
+
+ SPIFontSize fontsize = o->style->font_size;
+ SPILengthOrNormal lineheight = o->style->line_height;
+ vector<SPObject *> cl = o->childList(false);
+ bool beginning = true;
+ for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) {
+ SPObject *i = *ci;
+ if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) {
+ if (sp_text_get_length((SPItem *)i) <= 1) { // empty line
+ Inkscape::Text::Layout::iterator pos = te_get_layout((SPItem*)(o))->charIndexToIterator(
+ ((SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i))?0:((ci==cl.begin())?0:1)) + sp_text_get_length_upto(o,i) );
+ sp_te_insert((SPItem *)o, pos, "\u00a0"); //"\u00a0"
+ gchar *l = g_strdup_printf("%f", lineheight.value);
+ gchar *f = g_strdup_printf("%f", fontsize.value);
+ i->style->line_height.readIfUnset(l);
+ if (!beginning)
+ i->style->font_size.read(f);
+ else
+ i->style->font_size.readIfUnset(f);
+ g_free(l);
+ g_free(f);
+ } else {
+ beginning = false;
+ fontsize = i->style->font_size;
+ lineheight = o->style->line_height;
+ }
+ }
+ }
+}
+
+void fix_line_spacing(SPObject *o)
+{
+ SPILengthOrNormal lineheight = o->style->line_height;
+ bool inner = false;
+ vector<SPObject *> cl = o->childList(false);
+ for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) {
+ SPObject *i = *ci;
+ if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) {
+ // if no line-height attribute, set it
+ gchar *l = g_strdup_printf("%f", lineheight.value);
+ i->style->line_height.readIfUnset(l);
+ g_free(l);
+ }
+ inner = true;
+ }
+ if (inner) {
+ if (SP_IS_TEXT(o)) {
+ o->style->line_height.read("0.00%");
+ } else {
+ o->style->line_height.read("0.01%");
+ }
+ }
+}
+
+void fix_font_name(SPObject *o)
+{
+ vector<SPObject *> cl = o->childList(false);
+ for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci)
+ fix_font_name(*ci);
+ string prev = o->style->font_family.value ? o->style->font_family.value : o->style->font_family.value_default;
+ if (prev == "Sans")
+ o->style->font_family.read("sans-serif");
+ else if (prev == "Serif")
+ o->style->font_family.read("serif");
+ else if (prev == "Monospace")
+ o->style->font_family.read("monospace");
+}
+
+
+void fix_font_size(SPObject *o)
+{
+ SPIFontSize fontsize = o->style->font_size;
+ if (!fontsize.set)
+ return;
+ bool inner = false;
+ vector<SPObject *> cl = o->childList(false);
+ for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) {
+ SPObject *i = *ci;
+ fix_font_size(i);
+ if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) {
+ inner = true;
+ gchar *s = g_strdup_printf("%f", fontsize.value);
+ if (fontsize.set)
+ i->style->font_size.readIfUnset(s);
+ g_free(s);
+ }
+ }
+ if (inner && (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o)))
+ o->style->font_size.clear();
+}
+
+
+
+// helper function
+void sp_file_text_run_recursive(void (*f)(SPObject *), SPObject *o)
+{
+ if (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o))
+ f(o);
+ else {
+ vector<SPObject *> cl = o->childList(false);
+ for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci)
+ sp_file_text_run_recursive(f, *ci);
+ }
+}
+
+void fix_update(SPObject *o) {
+ o->style->write();
+ o->updateRepr();
+}
+
+void sp_file_convert_text_baseline_spacing(SPDocument *doc)
+{
+ sp_file_text_run_recursive(fix_blank_line, doc->getRoot());
+ sp_file_text_run_recursive(fix_line_spacing, doc->getRoot());
+ sp_file_text_run_recursive(fix_font_size, doc->getRoot());
+ sp_file_text_run_recursive(fix_update, doc->getRoot());
+}
+
+void sp_file_convert_font_name(SPDocument *doc)
+{
+ sp_file_text_run_recursive(fix_font_name, doc->getRoot());
+ sp_file_text_run_recursive(fix_update, doc->getRoot());
+}
+
+/*
+ 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/file.cpp b/src/file.cpp
index 9e96361c3..6613b1e65 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -293,10 +293,12 @@ bool sp_file_open(const Glib::ustring &uri,
bool replace_empty)
{
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ Inkscape::Display::TemporaryItem *page_border_rotated = NULL;
if (desktop) {
desktop->setWaitingCursor();
+ page_border_rotated = sp_document_namedview(desktop->getDocument(), NULL)->page_border_rotated;
}
-
+
SPDocument *doc = NULL;
bool cancelled = false;
try {
@@ -315,7 +317,6 @@ bool sp_file_open(const Glib::ustring &uri,
}
if (doc) {
-
SPDocument *existing = desktop ? desktop->getDocument() : NULL;
if (existing && existing->virgin && replace_empty) {
@@ -323,6 +324,7 @@ bool sp_file_open(const Glib::ustring &uri,
doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc.
desktop->change_document(doc);
doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px"));
+ desktop->remove_temporary_canvasitem(page_border_rotated);
} else {
// create a whole new desktop and window
SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc.
@@ -1631,8 +1633,12 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place)
for (Inkscape::XML::Node *obj = clipboard->firstChild() ; obj ; obj = obj->next()) {
if(target_document->getObjectById(obj->attribute("id"))) continue;
Inkscape::XML::Node *obj_copy = obj->duplicate(target_document->getReprDoc());
- target_parent->appendChild(obj_copy);
+ SPObject * pasted = desktop->currentLayer()->appendChildRepr(obj_copy);
Inkscape::GC::release(obj_copy);
+ SPLPEItem * pasted_lpe_item = dynamic_cast<SPLPEItem *>(pasted);
+ if (pasted_lpe_item){
+ pasted_lpe_item->forkPathEffectsIfNecessary(1);
+ }
pasted_objects_not.push_back(obj_copy);
}
Inkscape::Selection *selection = desktop->getSelection();
diff --git a/src/file.h b/src/file.h
index 4ffbc8ec0..2fc4ea910 100644
--- a/src/file.h
+++ b/src/file.h
@@ -203,7 +203,8 @@ void sp_file_print (Gtk::Window& parentWindow);
* clean unused defs out of file
*/
void sp_file_vacuum (SPDocument *doc);
-
+void sp_file_convert_text_baseline_spacing(SPDocument *doc);
+void sp_file_convert_font_name(SPDocument *doc);
#endif // SEEN_SP_FILE_H
diff --git a/src/filters/image.cpp b/src/filters/image.cpp
index 887201eb3..3dedb28ad 100644
--- a/src/filters/image.cpp
+++ b/src/filters/image.cpp
@@ -14,12 +14,15 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "image.h"
+
#include <sigc++/bind.h>
+
+#include "bad-uri-exception.h"
#include "display/nr-filter-image.h"
#include "uri.h"
#include "uri-references.h"
#include "attributes.h"
-#include "image.h"
#include "xml/repr.h"
#include "display/nr-filter.h"
diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp
index 42c494c00..e1f05c3ce 100644
--- a/src/helper/geom.cpp
+++ b/src/helper/geom.cpp
@@ -843,6 +843,13 @@ recursive_bezier4(const double x1, const double y1,
recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1);
}
+void
+swap(Geom::Point &A, Geom::Point &B){
+ Geom::Point tmp = A;
+ A = B;
+ B = tmp;
+}
+
/*
Local Variables:
mode:c++
diff --git a/src/helper/geom.h b/src/helper/geom.h
index d49e2070c..b3d907e51 100644
--- a/src/helper/geom.h
+++ b/src/helper/geom.h
@@ -32,7 +32,7 @@ void recursive_bezier4(const double x1, const double y1, const double x2, const
const double x3, const double y3, const double x4, const double y4,
std::vector<Geom::Point> &pointlist,
int level);
-
+void swap(Geom::Point &A, Geom::Point &B);
#endif // INKSCAPE_HELPER_GEOM_H
/*
diff --git a/src/inkgc/gc-core.h b/src/inkgc/gc-core.h
index d9d0bf4ff..a27510f50 100644
--- a/src/inkgc/gc-core.h
+++ b/src/inkgc/gc-core.h
@@ -18,7 +18,7 @@
#include <new>
#include <cstdlib>
-#include <cstddef>
+
#ifdef HAVE_GC_GC_H
# include <gc/gc.h>
#else
@@ -137,7 +137,6 @@ inline void *operator new(std::size_t size,
Inkscape::GC::CollectionPolicy collect,
Inkscape::GC::CleanupFunc cleanup=NULL,
void *data=NULL)
-throw(std::bad_alloc)
{
using namespace Inkscape::GC;
@@ -168,7 +167,6 @@ inline void *operator new(std::size_t size,
Inkscape::GC::ScanPolicy scan,
Inkscape::GC::CleanupFunc cleanup=NULL,
void *data=NULL)
-throw(std::bad_alloc)
{
return operator new(size, scan, Inkscape::GC::AUTO, cleanup, data);
}
@@ -178,7 +176,6 @@ inline void *operator new[](std::size_t size,
Inkscape::GC::CollectionPolicy collect,
Inkscape::GC::CleanupFunc cleanup=NULL,
void *data=NULL)
-throw(std::bad_alloc)
{
return operator new(size, scan, collect, cleanup, data);
}
@@ -187,7 +184,6 @@ inline void *operator new[](std::size_t size,
Inkscape::GC::ScanPolicy scan,
Inkscape::GC::CleanupFunc cleanup=NULL,
void *data=NULL)
-throw(std::bad_alloc)
{
return operator new[](size, scan, Inkscape::GC::AUTO, cleanup, data);
}
diff --git a/src/inkgc/gc-managed.h b/src/inkgc/gc-managed.h
index f435d7802..97b2b816e 100644
--- a/src/inkgc/gc-managed.h
+++ b/src/inkgc/gc-managed.h
@@ -30,7 +30,6 @@ public:
void *operator new(std::size_t size,
ScanPolicy scan=default_scan,
CollectionPolicy collect=default_collect)
- throw (std::bad_alloc)
{
return ::operator new(size, scan, collect);
}
@@ -38,7 +37,6 @@ public:
void *operator new[](std::size_t size,
ScanPolicy scan=default_scan,
CollectionPolicy collect=default_collect)
- throw (std::bad_alloc)
{
return ::operator new[](size, scan, collect);
}
diff --git a/src/inkgc/gc.cpp b/src/inkgc/gc.cpp
index ffa94ea2a..b1bd07af6 100644
--- a/src/inkgc/gc.cpp
+++ b/src/inkgc/gc.cpp
@@ -173,7 +173,7 @@ public:
{}
};
-Ops const &get_ops() throw (InvalidGCModeError) {
+Ops const &get_ops() {
char *mode_string=std::getenv("_INKSCAPE_GC");
if (mode_string) {
if (!std::strcmp(mode_string, "enable")) {
diff --git a/src/isinf.h b/src/isinf.h
deleted file mode 100644
index 8d590b972..000000000
--- a/src/isinf.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __ISINF_H__
-#define __ISINF_H__
-
-/*
- * Fix for missing std::isinf with SOLARIS8/GCC3.2
- */
-
-#if defined (SOLARIS)
-
- #include <ieeefp.h>
- #define isinf(x) ((fpclass(x) == FP_NINF) || (fpclass(x) == FP_PINF))
-
-#elif defined(__APPLE__) && __GNUC__ == 3
-#define isinf(x) __isinf(x)
-#elif __cplusplus >= 201103L
-# include <cmath>
-# define isinf std::isinf
-#endif
-
-#endif /* __ISINF_H__ */
diff --git a/src/libcola/gradient_projection.cpp b/src/libcola/gradient_projection.cpp
index 3e41aceac..c8488a545 100644
--- a/src/libcola/gradient_projection.cpp
+++ b/src/libcola/gradient_projection.cpp
@@ -18,7 +18,6 @@
#include "gradient_projection.h"
#include <iostream>
#include <2geom/math-utils.h>
-#include "isinf.h"
#include <math.h>
using namespace std;
diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp
index e6da9ba63..8e173a7c7 100644
--- a/src/libnrtype/Layout-TNG-Compute.cpp
+++ b/src/libnrtype/Layout-TNG-Compute.cpp
@@ -210,6 +210,7 @@ class Layout::Calculator
*/
static void dumpPangoItemsOut(ParagraphInfo *para){
std::cout << "Pango items: " << para->pango_items.size() << std::endl;
+ font_factory * factory = font_factory::Default();
for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){
std::cout
<< "idx: " << pidx
@@ -217,6 +218,8 @@ static void dumpPangoItemsOut(ParagraphInfo *para){
<< para->pango_items[pidx].item->offset
<< " length: "
<< para->pango_items[pidx].item->length
+ << " font: "
+ << factory->ConstructFontSpecification( para->pango_items[pidx].font )
<< std::endl;
}
}
@@ -1483,11 +1486,18 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const &para,
UnbrokenSpanPosition span_pos;
for( ; ; ) {
+
+ // Get regions where one can place one line of text (can be more than one, if filling a
+ // donut for example).
std::vector<ScanlineMaker::ScanRun> scan_runs;
scan_runs = _scanline_maker->makeScanline(*line_box_height); // Only one line with "InfiniteScanlineMaker
+
+ // If scan_runs is empty, we must have reached the bottom of a shape. Go to next shape.
while (scan_runs.empty()) {
// Only used by ShapeScanlineMaker
if (!_goToNextWrapShape()) return false; // no more shapes to wrap in to
+
+ *line_box_height = *strut_height;
scan_runs = _scanline_maker->makeScanline(*line_box_height);
}
@@ -1498,13 +1508,21 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const &para,
unsigned scan_run_index;
span_pos = *start_span_pos;
for (scan_run_index = 0 ; scan_run_index < scan_runs.size() ; scan_run_index++) {
- if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height))
+
+ // Returns false if some text in line requires a taller line_box_height.
+ // (We try again with a larger line_box_height.)
+ if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height)) {
break;
+ }
+
if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty())
span_pos = chunk_info->back().broken_spans.back().end;
}
+
if (scan_run_index == scan_runs.size()) break; // ie when buildChunksInScanRun() succeeded
- }
+
+ } // End for loop
+
*start_span_pos = span_pos;
TRACE((" final line_box_height: %f\n", line_box_height->emSize() ));
TRACE((" end _findChunksForLine: chunks: %lu\n", chunk_info->size()));
@@ -1578,6 +1596,8 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const &para,
new_span_height.descent > line_height->descent + std::numeric_limits<float>::epsilon() ) {
// Take larger of each of the two ascents and two descents per CSS
line_height->max(new_span_height);
+
+ // Currently always true for flowed text and false for Inkscape multiline text.
if (!_scanline_maker->canExtendCurrentScanline(*line_height)) {
return false;
}
@@ -1752,12 +1772,20 @@ bool Layout::Calculator::calculate()
span_pos.char_byte = 0;
span_pos.char_index = 0;
+ bool keep_going = true;
do { // for each line in the paragraph
TRACE(("begin line\n"));
+
std::vector<ChunkInfo> line_chunk_info;
- if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height, &strut_height ))
+ if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height, &strut_height )) {
+ keep_going = false;
break; // out of shapes to wrap in to
+ }
+ if (line_box_height.emSize() < 0.001 && line_chunk_info.empty()) {
+ keep_going = false;
+ break; // No room for text and not useful to try again at same place.
+ }
_outputLine(para, line_box_height, line_chunk_info);
_scanline_maker->setLineHeight( line_box_height );
_scanline_maker->completeLine(); // Increments y by line height
@@ -1765,7 +1793,7 @@ bool Layout::Calculator::calculate()
} while (span_pos.iter_span != para.unbroken_spans.end());
TRACE(("para %lu end\n\n", _flow._paragraphs.size() - 1));
- if (_scanline_maker != NULL) {
+ if (keep_going) {
bool is_empty_para = _flow._characters.empty() || _flow._characters.back().line(&_flow).in_paragraph != _flow._paragraphs.size() - 1;
if ((is_empty_para && para_end_input_index + 1 >= _flow._input_stream.size())
|| para_end_input_index + 1 < _flow._input_stream.size()) {
diff --git a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp
index 0d6112d19..a57aaa3e4 100644
--- a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp
+++ b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp
@@ -124,7 +124,7 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScan
FloatLigne line_rasterization;
FloatLigne line_decent_length_runs;
float line_text_height = (float)(line_height.emSize());
- if (line_text_height == 0.0)
+ if (line_text_height < 0.001)
line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do
_current_line_height = (float)line_height.emSize();
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 784317090..5ffccc7c0 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -60,6 +60,9 @@ set(live_effects_SRC
parameter/array.cpp
parameter/bool.cpp
parameter/filletchamferpointarray.cpp
+ parameter/item-reference.cpp
+ parameter/item.cpp
+ parameter/originalitem.cpp
parameter/originalpath.cpp
parameter/originalpatharray.cpp
parameter/parameter.cpp
@@ -142,6 +145,9 @@ set(live_effects_SRC
parameter/bool.h
parameter/enum.h
parameter/filletchamferpointarray.h
+ parameter/item.h
+ parameter/item-reference.h
+ parameter/originalitem.h
parameter/originalpath.h
parameter/originalpatharray.h
parameter/parameter.h
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 5cc0d6f20..50e2aa353 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -70,6 +70,8 @@
#include "ui/tools/node-tool.h"
#include "ui/tools-switch.h"
#include "knotholder.h"
+#include "path-chemistry.h"
+#include "xml/sp-css-attr.h"
#include "live_effects/lpeobject.h"
#include "display/curve.h"
@@ -116,7 +118,7 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{RULER, N_("Ruler"), "ruler"},
/* 0.91 */
{POWERSTROKE, N_("Power stroke"), "powerstroke"},
- {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"},
+ {CLONE_ORIGINAL, N_("Clone original"), "clone_original"},
/* 0.92 */
{SIMPLIFY, N_("Simplify"), "simplify"},
{LATTICE2, N_("Lattice Deformation 2"), "lattice2"},
@@ -356,6 +358,7 @@ Effect::Effect(LivePathEffectObject *lpeobject)
sp_lpe_item(NULL),
current_zoom(1),
upd_params(true),
+ sp_shape(NULL),
sp_curve(NULL),
provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
@@ -392,15 +395,15 @@ Effect::doOnApply (SPLPEItem const*/*lpeitem*/)
}
void
-Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP)
+Effect::setCurrentZoom(double cZ)
{
- selectedNodesPoints = sNP;
+ current_zoom = cZ;
}
void
-Effect::setCurrentZoom(double cZ)
+Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP)
{
- current_zoom = cZ;
+ selectedNodesPoints = sNP;
}
bool
@@ -423,6 +426,74 @@ Effect::isNodePointSelected(Geom::Point const &nodePoint) const
return false;
}
+void
+Effect::processObjects(LpeAction lpe_action)
+{
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ for (std::vector<const char *>::iterator el_it = items.begin();
+ el_it != items.end(); ++el_it) {
+ const char * id = *el_it;
+ if (!id || strlen(id) == 0) {
+ return;
+ }
+ SPObject *elemref = NULL;
+ if (elemref = document->getObjectById(id)) {
+ Inkscape::XML::Node * elemnode = elemref->getRepr();
+ std::vector<SPItem*> item_list;
+ item_list.push_back(SP_ITEM(elemref));
+ std::vector<Inkscape::XML::Node*> item_to_select;
+ std::vector<SPItem*> item_selected;
+ SPCSSAttr *css;
+ Glib::ustring css_str;
+ switch (lpe_action){
+ case LPE_TO_OBJECTS:
+ if (SP_ITEM(elemref)->isHidden()) {
+ elemref->deleteObject();
+ } else {
+ if (elemnode->attribute("inkscape:path-effect")) {
+ sp_item_list_to_curves(item_list, item_selected, item_to_select);
+ }
+ elemnode->setAttribute("sodipodi:insensitive", NULL);
+ }
+ break;
+
+ case LPE_ERASE:
+ elemref->deleteObject();
+ break;
+
+ case LPE_VISIBILITY:
+ css = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style"));
+ if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) {
+ css->setAttribute("display", "none");
+ } else {
+ css->setAttribute("display", NULL);
+ }
+ sp_repr_css_write_string(css,css_str);
+ elemnode->setAttribute("style", css_str.c_str());
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) {
+ items.clear();
+ }
+}
+
+void Effect::setCurrentShape(SPShape * shape){
+ if(shape){
+ sp_shape = shape;
+ if (!(sp_curve = sp_shape->getCurve())) {
+ // oops
+ return;
+ }
+ pathvector_before_effect = sp_curve->get_pathvector();
+ }
+}
+
/**
* Is performed each time before the effect is updated.
*/
@@ -446,8 +517,12 @@ void Effect::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
void Effect::doOnApply_impl(SPLPEItem const* lpeitem)
{
sp_lpe_item = const_cast<SPLPEItem *>(lpeitem);
- /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
- pathvector_before_effect = sp_curve->get_pathvector();*/
+ sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
+ pathvector_before_effect = sp_curve->get_pathvector();
+ SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item);
+ if(shape){
+ setCurrentShape(shape);
+ }
doOnApply(lpeitem);
}
@@ -457,6 +532,7 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem)
//printf("(SPLPEITEM*) %p\n", sp_lpe_item);
SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item);
if(shape){
+ setCurrentShape(shape);
sp_curve = shape->getCurve();
pathvector_before_effect = sp_curve->get_pathvector();
}
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
index 1997ff0ca..f5e41d50e 100644
--- a/src/live_effects/effect.h
+++ b/src/live_effects/effect.h
@@ -19,7 +19,7 @@
class SPDocument;
class SPDesktop;
class SPItem;
-class LivePathEffectObject;
+class LivePathEffectObject;
class SPLPEItem;
class KnotHolder;
class KnotHolderEntity;
@@ -44,6 +44,12 @@ enum LPEPathFlashType {
DEFAULT
};
+enum LpeAction {
+ LPE_ERASE = 0,
+ LPE_TO_OBJECTS,
+ LPE_VISIBILITY
+};
+
class Effect {
public:
static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj);
@@ -73,6 +79,9 @@ public:
static int acceptsNumClicks(EffectType type);
int acceptsNumClicks() const { return acceptsNumClicks(effectType()); }
void doAcceptPathPreparations(SPLPEItem *lpeitem);
+ SPShape * getCurrentShape(){ return sp_shape; };
+ void setCurrentShape(SPShape * shape);
+ void processObjects(LpeAction lpe_action);
/*
* isReady() indicates whether all preparations which are necessary to apply the LPE are done,
@@ -125,7 +134,9 @@ public:
bool apply_to_clippath_and_mask;
bool erase_extra_objects; // set this to false allow retain extra generated objects, see measure line LPE
bool upd_params;
-
+ BoolParam is_visible;
+ SPCurve * sp_curve;
+ Geom::PathVector pathvector_before_effect;
protected:
Effect(LivePathEffectObject *lpeobject);
@@ -150,7 +161,6 @@ protected:
bool _provides_knotholder_entities;
int oncanvasedit_it;
- BoolParam is_visible;
bool show_orig_path; // set this to true in derived effects to automatically have the original
// path displayed as helperpath
@@ -164,10 +174,10 @@ protected:
bool concatenate_before_pwd2;
SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them.
+ SPShape * sp_shape; // these get stored in doBeforeEffect_impl before doEffect chain, or in performPathEffects on groups, and derived classes may do as they please with them.
+ std::vector<const char *> items;
double current_zoom;
std::vector<Geom::Point> selectedNodesPoints;
- SPCurve * sp_curve;
- Geom::PathVector pathvector_before_effect;
private:
bool provides_own_flash_paths; // if true, the standard flash path is suppressed
diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp
index 10418a02d..04db6171e 100644
--- a/src/live_effects/lpe-clone-original.cpp
+++ b/src/live_effects/lpe-clone-original.cpp
@@ -6,6 +6,12 @@
#include "live_effects/lpe-clone-original.h"
#include "display/curve.h"
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include "sp-clippath.h"
+#include "sp-mask.h"
+#include "xml/sp-css-attr.h"
+
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -14,9 +20,319 @@ namespace LivePathEffect {
LPECloneOriginal::LPECloneOriginal(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
- linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this)
+ linked_path("LEGACY FALLBACK", "LEGACY FALLBACK", "linkedpath", &wr, this),
+ linked_item(_("Linked Item:"), _("Item from which to take the original data"), "linked_item", &wr, this),
+ scale(_("Scale %"), _("Scale item %"), "scale", &wr, this, 100.0),
+ preserve_position(_("Preserve position"), _("Preserve position"), "preserve_position", &wr, this, false),
+ inverse(_("Inverse clone"), _("Use LPE item as origin"), "inverse", &wr, this, false),
+ d(_("Clone shape -d-"), _("Clone shape -d-"), "d", &wr, this, true),
+ transform(_("Clone transforms"), _("Clone transforms"), "transform", &wr, this, true),
+ fill(_("Clone fill"), _("Clone fill"), "fill", &wr, this, false),
+ stroke(_("Clone stroke"), _("Clone stroke"), "stroke", &wr, this, false),
+ paintorder(_("Clone paint order"), _("Clone paint order"), "paintorder", &wr, this, false),
+ opacity(_("Clone opacity"), _("Clone opacity"), "opacity", &wr, this, false),
+ filter(_("Clone filter"), _("Clone filter"), "filter", &wr, this, false),
+ attributes("Attributes linked", "Attributes linked, comma separated atributes", "attributes", &wr, this,""),
+ style_attributes("Style attributes linked", "Style attributes linked, comma separated atributes", "style_attributes", &wr, this,""),
+ expanded(false),
+ origin(Geom::Point(0,0))
+{
+ registerParameter(&linked_path);
+ registerParameter(&linked_item);
+ registerParameter(&scale);
+ registerParameter(&attributes);
+ registerParameter(&style_attributes);
+ registerParameter(&preserve_position);
+ registerParameter(&inverse);
+ registerParameter(&d);
+ registerParameter(&transform);
+ registerParameter(&fill);
+ registerParameter(&stroke);
+ registerParameter(&paintorder);
+ registerParameter(&opacity);
+ registerParameter(&filter);
+ scale.param_set_range(0.01, 999999.0);
+ scale.param_set_increments(1, 1);
+ scale.param_set_digits(2);
+ attributes.param_hide_canvas_text();
+ style_attributes.param_hide_canvas_text();
+ preserve_position_changed = preserve_position;
+ preserve_affine = Geom::identity();
+}
+
+void
+LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root)
{
- registerParameter( dynamic_cast<Parameter *>(&linked_path) );
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ std::vector< SPObject * > childs = origin->childList(true);
+ size_t index = 0;
+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
+ obj_it != childs.end(); ++obj_it) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneAttrbutes((*obj_it), dest_child, live, attributes, style_attributes, false);
+ index++;
+ }
+ }
+ //Attributes
+ SPShape * shape_origin = SP_SHAPE(origin);
+ SPPath * path_origin = SP_PATH(origin);
+ SPShape * shape_dest = SP_SHAPE(dest);
+ SPMask *mask_origin = SP_ITEM(origin)->mask_ref->getObject();
+ SPMask *mask_dest = SP_ITEM(dest)->mask_ref->getObject();
+ if(mask_origin && mask_dest) {
+ std::vector<SPObject*> mask_list = mask_origin->childList(true);
+ std::vector<SPObject*> mask_list_dest = mask_dest->childList(true);
+ if (mask_list.size() == mask_list_dest.size()) {
+ size_t i = 0;
+ for ( std::vector<SPObject*>::const_iterator iter=mask_list.begin();iter!=mask_list.end();++iter) {
+ SPObject * mask_data = *iter;
+ SPObject * mask_dest_data = mask_list_dest[i];
+ cloneAttrbutes(mask_data, mask_dest_data, live, attributes, style_attributes, false);
+ i++;
+ }
+ }
+ }
+ SPClipPath *clippath_origin = SP_ITEM(origin)->clip_ref->getObject();
+ SPClipPath *clippath_dest = SP_ITEM(dest)->clip_ref->getObject();
+ if(clippath_origin && clippath_dest) {
+ std::vector<SPObject*> clippath_list = clippath_origin->childList(true);
+ std::vector<SPObject*> clippath_list_dest = clippath_dest->childList(true);
+ if (clippath_list.size() == clippath_list_dest.size()) {
+ size_t i = 0;
+ for ( std::vector<SPObject*>::const_iterator iter=clippath_list.begin();iter!=clippath_list.end();++iter) {
+ SPObject * clippath_data = *iter;
+ SPObject * clippath_dest_data = clippath_list_dest[i];
+ cloneAttrbutes(clippath_data, clippath_dest_data, live, attributes, style_attributes, false);
+ i++;
+ }
+ }
+ }
+ gchar ** attarray = g_strsplit(attributes, ",", 0);
+ gchar ** iter = attarray;
+ Geom::Affine affine_dest = Geom::identity();
+ Geom::Affine affine_origin = Geom::identity();
+ Geom::Affine affine_previous = Geom::identity();
+ sp_svg_transform_read(SP_ITEM(dest)->getAttribute("transform"), &affine_dest);
+ sp_svg_transform_read(SP_ITEM(origin)->getAttribute("transform"), &affine_origin);
+ while (*iter != NULL) {
+ const char* attribute = (*iter);
+ if ( std::strcmp(attribute, "transform") == 0 ) {
+ if (preserve_position) {
+ Geom::Affine dest_affine = Geom::identity();
+ if (root) {
+ dest_affine *= affine_origin;
+ if (preserve_affine == Geom::identity()) {
+ dest_affine *= Geom::Translate(affine_dest.translation());
+ }
+ dest_affine *= Geom::Translate(affine_origin.translation()).inverse();
+ dest_affine *= Geom::Translate(preserve_affine.translation());
+ affine_previous = preserve_affine;
+ preserve_affine = Geom::identity();
+ SP_ITEM(dest)->getRepr()->setAttribute("transform",sp_svg_transform_write(dest_affine));
+ }
+ } else {
+ SP_ITEM(dest)->getRepr()->setAttribute("transform",sp_svg_transform_write(affine_origin));
+ }
+ } else if ( shape_dest && shape_origin && live && (std::strcmp(attribute, "d") == 0)) {
+ SPCurve *c = NULL;
+ if (inverse) {
+ c = shape_origin->getCurveBeforeLPE();
+ } else {
+ c = shape_origin->getCurve();
+ }
+ if (c) {
+ Geom::PathVector c_pv = c->get_pathvector();
+ Geom::OptRect orig_bbox = SP_ITEM(origin)->geometricBounds();
+ Geom::OptRect dest_bbox = SP_ITEM(dest)->geometricBounds();
+ if (dest_bbox && orig_bbox && root) {
+ Geom::Point orig_point = (*orig_bbox).corner(0);
+ Geom::Point dest_point = (*dest_bbox).corner(0);
+ if (scale != 100.0) {
+ double scale_affine = scale/100.0;
+ Geom::Scale scale = Geom::Scale(scale_affine);
+ c_pv *= Geom::Translate(orig_point).inverse();
+ c_pv *= scale;
+ c_pv *= Geom::Translate(orig_point);
+ }
+ if (preserve_position) {
+ c_pv *= Geom::Translate(dest_point - orig_point);
+ }
+ }
+ if (inverse) {
+ c_pv *= i2anc_affine(origin, sp_lpe_item);
+ } else {
+ c_pv *= i2anc_affine(dest, sp_lpe_item);
+ }
+ c->set_pathvector(c_pv);
+ if (!path_origin) {
+ shape_dest->setCurveInsync(c, TRUE);
+ dest->getRepr()->setAttribute(attribute, sp_svg_write_path(c_pv));
+ } else {
+ shape_dest->setCurve(c, TRUE);
+ }
+ c->unref();
+ } else {
+ dest->getRepr()->setAttribute(attribute, NULL);
+ }
+ } else {
+ dest->getRepr()->setAttribute(attribute, origin->getRepr()->attribute(attribute));
+ }
+ iter++;
+ }
+ g_strfreev (attarray);
+ SPCSSAttr *css_origin = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css_origin, origin->getRepr()->attribute("style"));
+ SPCSSAttr *css_dest = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string(css_dest, dest->getRepr()->attribute("style"));
+ gchar ** styleattarray = g_strsplit(style_attributes, ",", 0);
+ gchar ** styleiter = styleattarray;
+ while (*styleiter != NULL) {
+ const char* attribute = (*styleiter);
+ const char* origin_attribute = sp_repr_css_property(css_origin, attribute, "");
+ if (origin_attribute == "") {
+ sp_repr_css_set_property (css_dest, attribute, NULL);
+ } else {
+ sp_repr_css_set_property (css_dest, attribute, origin_attribute);
+ }
+ styleiter++;
+ }
+ g_strfreev (styleattarray);
+ Glib::ustring css_str;
+ sp_repr_css_write_string(css_dest,css_str);
+ dest->getRepr()->setAttribute("style", css_str.c_str());
+}
+
+void
+LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){
+ if (linked_path.linksToPath()) { //Legacy staff
+ Glib::ustring attributes_value("d");
+ attributes.param_setValue(attributes_value);
+ attributes.write_to_SVG();
+ Glib::ustring style_attributes_value("");
+ style_attributes.param_setValue(style_attributes_value);
+ style_attributes.write_to_SVG();
+ linked_item.param_readSVGValue(linked_path.param_getSVGValue());
+ linked_path.param_readSVGValue("");
+ }
+
+ if (linked_item.linksToItem()) {
+ linked_item.setInverse(inverse);
+ if ( preserve_position_changed != preserve_position ) {
+ if (!preserve_position) {
+ sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &preserve_affine);
+ }
+ preserve_position_changed = preserve_position;
+ }
+ Glib::ustring attr = "";
+ if (d) {
+ attr.append("d,");
+ }
+ if (transform) {
+ attr.append("transform,");
+ }
+ attr.append(Glib::ustring(attributes.param_getSVGValue()).append(","));
+ if (attr.size() && !Glib::ustring(attributes.param_getSVGValue()).size()) {
+ attr.erase (attr.size()-1, 1);
+ }
+ Glib::ustring style_attr = "";
+ if (fill) {
+ style_attr.append("fill,").append("fill-rule,");
+ }
+ if (stroke) {
+ style_attr.append("stroke,").append("stroke-width,").append("stroke-linecap,").append("stroke-linejoin,");
+ style_attr.append("stroke-opacity,").append("stroke-miterlimit,").append("stroke-dasharray,");
+ style_attr.append("stroke-opacity,").append("stroke-dashoffset,").append("marker-start,");
+ style_attr.append("marker-mid,").append("marker-end,");
+ }
+ if (paintorder) {
+ style_attr.append("paint-order,");
+ }
+ if (filter) {
+ style_attr.append("filter,");
+ }
+ if (opacity) {
+ style_attr.append("opacity,");
+ }
+ if (style_attr.size() && !Glib::ustring(style_attributes.param_getSVGValue()).size()) {
+ style_attr.erase (style_attr.size()-1, 1);
+ }
+ style_attr.append(Glib::ustring(style_attributes.param_getSVGValue()).append(","));
+
+ SPItem * from = inverse ? SP_ITEM(sp_lpe_item) : SP_ITEM(linked_item.getObject());
+ SPItem * to = !inverse ? SP_ITEM(sp_lpe_item) : SP_ITEM(linked_item.getObject());
+ cloneAttrbutes(from, to, true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true);
+ Geom::OptRect bbox = from->geometricBounds();
+ if (bbox && preserve_position && origin != Geom::Point(0,0)) {
+ origin = (*bbox).corner(0) - origin;
+ to->transform *= Geom::Translate(origin);
+ }
+ bbox = from->geometricBounds();
+ if (bbox && preserve_position) {
+ origin = (*bbox).corner(0);
+ }
+ }
+}
+
+
+Gtk::Widget *
+LPECloneOriginal::newWidget()
+{
+ // use manage here, because after deletion of Effect object, others might
+ // still be pointing to this widget.
+ Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
+ vbox->set_border_width(5);
+ vbox->set_homogeneous(false);
+ vbox->set_spacing(2);
+ Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) );
+ vbox_expander->set_border_width(0);
+ vbox_expander->set_spacing(2);
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter * param = *it;
+ if (param->param_key == "linkedpath") {
+ ++it;
+ continue;
+ }
+ Gtk::Widget * widg = param->param_newWidget();
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ if (param->param_key != "attributes" &&
+ param->param_key != "style_attributes") {
+ vbox->pack_start(*widg, true, true, 2);
+ } else {
+ vbox_expander->pack_start(*widg, true, true, 2);
+ }
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+
+ ++it;
+ }
+ expander = Gtk::manage(new Gtk::Expander(Glib::ustring(_("Show attributes override"))));
+ expander->add(*vbox_expander);
+ expander->set_expanded(expanded);
+ expander->property_expanded().signal_changed().connect(sigc::mem_fun(*this, &LPECloneOriginal::onExpanderChanged) );
+ vbox->pack_start(*expander, true, true, 2);
+ this->upd_params = false;
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+void
+LPECloneOriginal::onExpanderChanged()
+{
+ expanded = expander->get_expanded();
+ if(expanded) {
+ expander->set_label (Glib::ustring(_("Hide attributes override")));
+ } else {
+ expander->set_label (Glib::ustring(_("Show attributes override")));
+ }
}
LPECloneOriginal::~LPECloneOriginal()
@@ -24,12 +340,22 @@ LPECloneOriginal::~LPECloneOriginal()
}
-void LPECloneOriginal::doEffect (SPCurve * curve)
+void
+LPECloneOriginal::transform_multiply(Geom::Affine const& postmul, bool set)
+{
+ if (linked_item.linksToItem()) {
+ bool changed = false;
+ linked_item.getObject()->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+}
+
+void
+LPECloneOriginal::doEffect (SPCurve * curve)
{
- if ( linked_path.linksToPath() ) {
- Geom::PathVector linked_pathv = linked_path.get_pathvector();
- if ( !linked_pathv.empty() ) {
- curve->set_pathvector(linked_pathv);
+ if (linked_item.linksToItem() && !inverse) {
+ SPShape * shape = getCurrentShape();
+ if(shape){
+ curve->set_pathvector(shape->getCurve()->get_pathvector());
}
}
}
diff --git a/src/live_effects/lpe-clone-original.h b/src/live_effects/lpe-clone-original.h
index abf65ded8..e4328c169 100644
--- a/src/live_effects/lpe-clone-original.h
+++ b/src/live_effects/lpe-clone-original.h
@@ -8,24 +8,49 @@
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-
+#include <gtkmm/expander.h>
#include "live_effects/effect.h"
+#include "live_effects/parameter/originalitem.h"
#include "live_effects/parameter/originalpath.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/point.h"
+#include "live_effects/parameter/text.h"
+#include "live_effects/lpegroupbbox.h"
namespace Inkscape {
namespace LivePathEffect {
-class LPECloneOriginal : public Effect {
+class LPECloneOriginal : public Effect, GroupBBoxEffect {
public:
LPECloneOriginal(LivePathEffectObject *lpeobject);
virtual ~LPECloneOriginal();
-
virtual void doEffect (SPCurve * curve);
+ virtual void doBeforeEffect (SPLPEItem const* lpeitem);
+ virtual void transform_multiply(Geom::Affine const& postmul, bool set);
+ virtual Gtk::Widget * newWidget();
+ void onExpanderChanged();
+ void cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root);
private:
OriginalPathParam linked_path;
-
-private:
+ OriginalItemParam linked_item;
+ ScalarParam scale;
+ BoolParam preserve_position;
+ BoolParam inverse;
+ BoolParam d;
+ BoolParam transform;
+ BoolParam fill;
+ BoolParam stroke;
+ BoolParam paintorder;
+ BoolParam opacity;
+ BoolParam filter;
+ TextParam attributes;
+ TextParam style_attributes;
+ Geom::Point origin;
+ bool preserve_position_changed;
+ bool expanded;
+ Gtk::Expander * expander;
+ Geom::Affine preserve_affine;
LPECloneOriginal(const LPECloneOriginal&);
LPECloneOriginal& operator=(const LPECloneOriginal&);
};
diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp
index 813f25d3d..42e055062 100644
--- a/src/live_effects/lpe-copy_rotate.cpp
+++ b/src/live_effects/lpe-copy_rotate.cpp
@@ -17,6 +17,13 @@
#include <2geom/sbasis-to-bezier.h>
#include <2geom/intersection-graph.h>
#include "live_effects/lpe-copy_rotate.h"
+#include "display/curve.h"
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include "style.h"
+#include "helper/geom.h"
+#include "xml/sp-css-attr.h"
+#include "path-chemistry.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -45,9 +52,11 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) :
starting_angle(_("Starting angle"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0),
rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0),
num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 6),
+ split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001),
copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true),
fuse_paths(_("Kaleidoskope"), _("Kaleidoskope by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false),
join_paths(_("Join paths"), _("Join paths, use fill-rule: evenodd for best result"), "join_paths", &wr, this, false),
+ split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false),
dist_angle_handle(100.0)
{
show_orig_path = true;
@@ -56,15 +65,21 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) :
registerParameter(&copies_to_360);
registerParameter(&fuse_paths);
registerParameter(&join_paths);
+ registerParameter(&split_items);
registerParameter(&starting_angle);
registerParameter(&starting_point);
registerParameter(&rotation_angle);
registerParameter(&num_copies);
+ registerParameter(&split_gap);
registerParameter(&origin);
-
+ split_gap.param_set_range(-999999.0, 999999.0);
+ split_gap.param_set_increments(0.1, 0.1);
+ split_gap.param_set_digits(5);
+ num_copies.param_set_range(0, 999999);
num_copies.param_make_integer(true);
- num_copies.param_set_range(0, 1000);
apply_to_clippath_and_mask = true;
+ previous_num_copies = num_copies;
+ reset = false;
}
LPECopyRotate::~LPECopyRotate()
@@ -72,6 +87,207 @@ LPECopyRotate::~LPECopyRotate()
}
+void
+LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem)
+{
+ if (split_items) {
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ items.clear();
+ container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
+ SPDocument * doc = SP_ACTIVE_DOCUMENT;
+ Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot();
+ Inkscape::XML::Node *root_origin = doc->getReprRoot();
+ if (root_origin != root) {
+ return;
+ }
+ if (previous_num_copies != num_copies) {
+ gint numcopies_gap = previous_num_copies - num_copies;
+ if (numcopies_gap > 0 && num_copies != 0) {
+ guint counter = num_copies - 1;
+ while (numcopies_gap > 0) {
+ const char * id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
+ if (!id || strlen(id) == 0) {
+ return;
+ }
+ SPObject *elemref = NULL;
+ if (elemref = document->getObjectById(id)) {
+ SP_ITEM(elemref)->setHidden(true);
+ }
+ counter++;
+ numcopies_gap--;
+ }
+ }
+ previous_num_copies = num_copies;
+ }
+ SPObject *elemref = NULL;
+ char * id = g_strdup(Glib::ustring("rotated-").append("1").append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
+ guint counter = 0;
+ while(elemref = document->getObjectById(id)) {
+ if (SP_ITEM(elemref)->isHidden()) {
+ items.push_back(id);
+ }
+ id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
+ counter++;
+ }
+ g_free(id);
+ double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
+ Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
+ double size_divider = Geom::distance(origin,bbox) + (diagonal * 2);
+ Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider;
+ Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider;
+ Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle)));
+ if (fuse_paths) {
+ size_t rest = 0;
+ for (size_t i = 1; i < num_copies; ++i) {
+ Geom::Affine r = Geom::identity();
+ Geom::Point dir = unit_vector((Geom::Point)origin - Geom::middle_point(line_start,line_end));
+ if( rest%2 == 0) {
+ r *= Geom::Rotate(Geom::Angle(dir)).inverse();
+ r *= Geom::Scale(1, -1);
+ r *= Geom::Rotate(Geom::Angle(dir));
+ }
+ Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i)));
+ Geom::Affine t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin);
+ if( rest%2 == 0) {
+ t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)).inverse() * Geom::Translate(origin);
+ }
+ t *= sp_lpe_item->transform;
+ toItem(t, i-1, reset);
+ rest ++;
+ }
+ } else {
+ for (size_t i = 1; i < num_copies; ++i) {
+ Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i)));
+ Geom::Affine t = m * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin);
+ t *= sp_lpe_item->transform;
+ toItem(t, i - 1, reset);
+ }
+ }
+ reset = false;
+ } else {
+ processObjects(LPE_ERASE);
+ items.clear();
+ }
+
+ std::cout << previous_num_copies << "previous_num_copies\n";
+ std::cout << num_copies << "num_copies\n";
+}
+
+void
+LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset)
+{
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ std::vector< SPObject * > childs = origin->childList(true);
+ size_t index = 0;
+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
+ obj_it != childs.end(); ++obj_it) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneD(*obj_it, dest_child, false, reset);
+ index++;
+ }
+ }
+ SPShape * shape = SP_SHAPE(origin);
+ SPPath * path = SP_PATH(dest);
+ if (!path && !SP_IS_GROUP(dest)) {
+ Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0);
+ dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
+ path = SP_PATH(dest);
+ }
+ if (path && shape) {
+ SPCurve *c = NULL;
+ if (root) {
+ c = new SPCurve();
+ c->set_pathvector(pathvector_before_effect);
+ } else {
+ c = shape->getCurve();
+ }
+ if (c) {
+ path->setCurve(c, TRUE);
+ c->unref();
+ } else {
+ dest->getRepr()->setAttribute("d", NULL);
+ }
+ if (reset) {
+ dest->getRepr()->setAttribute("style", shape->getRepr()->attribute("style"));
+ }
+ }
+}
+
+void
+LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset)
+{
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str());
+ items.push_back(elemref_id);
+ SPObject *elemref= NULL;
+ Inkscape::XML::Node *phantom = NULL;
+ if (elemref = document->getObjectById(elemref_id)) {
+ phantom = elemref->getRepr();
+ } else {
+ phantom = sp_lpe_item->getRepr()->duplicate(xml_doc);
+ std::vector<const char *> attrs;
+ attrs.push_back("inkscape:path-effect");
+ attrs.push_back("inkscape:original-d");
+ attrs.push_back("sodipodi:type");
+ attrs.push_back("sodipodi:rx");
+ attrs.push_back("sodipodi:ry");
+ attrs.push_back("sodipodi:cx");
+ attrs.push_back("sodipodi:cy");
+ attrs.push_back("sodipodi:end");
+ attrs.push_back("sodipodi:start");
+ attrs.push_back("inkscape:flatsided");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:rounded");
+ attrs.push_back("sodipodi:arg1");
+ attrs.push_back("sodipodi:arg2");
+ attrs.push_back("sodipodi:r1");
+ attrs.push_back("sodipodi:r2");
+ attrs.push_back("sodipodi:sides");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("sodipodi:argument");
+ attrs.push_back("sodipodi:expansion");
+ attrs.push_back("sodipodi:radius");
+ attrs.push_back("sodipodi:revolution");
+ attrs.push_back("sodipodi:t0");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("x");
+ attrs.push_back("y");
+ attrs.push_back("rx");
+ attrs.push_back("ry");
+ attrs.push_back("width");
+ attrs.push_back("height");
+ phantom->setAttribute("id", elemref_id);
+ for(const char * attr : attrs) {
+ phantom->setAttribute(attr, NULL);
+ }
+ }
+ if (!elemref) {
+ elemref = container->appendChildRepr(phantom);
+ Inkscape::GC::release(phantom);
+ }
+ cloneD(SP_OBJECT(sp_lpe_item), elemref, true, reset);
+ elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform));
+ SP_ITEM(elemref)->setHidden(false);
+ if (elemref->parent != container) {
+ Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
+ copy->setAttribute("id", elemref_id);
+ container->appendChildRepr(copy);
+ Inkscape::GC::release(copy);
+ elemref->deleteObject();
+ }
+}
+
+void
+LPECopyRotate::resetStyles(){
+ reset = true;
+ doAfterEffect(sp_lpe_item);
+}
+
Gtk::Widget * LPECopyRotate::newWidget()
{
// use manage here, because after deletion of Effect object, others might
@@ -81,7 +297,15 @@ Gtk::Widget * LPECopyRotate::newWidget()
vbox->set_border_width(5);
vbox->set_homogeneous(false);
vbox->set_spacing(2);
-
+ Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0));
+ Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) );
+ vbox_expander->set_border_width(0);
+ vbox_expander->set_spacing(2);
+ Gtk::Button * reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles"))));
+ reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPECopyRotate::resetStyles));
+ reset_button->set_size_request(140,30);
+ vbox->pack_start(*hbox, true,true,2);
+ hbox->pack_start(*reset_button, false, false,2);
std::vector<Parameter *>::iterator it = param_vector.begin();
while (it != param_vector.end()) {
if ((*it)->widget_is_visible) {
@@ -125,11 +349,11 @@ void
LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set)
{
// cycle through all parameters. Most parameters will not need transformation, but path and point params do.
-
for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) {
Parameter * param = *it;
param->param_transform_multiply(postmul, set);
}
+ sp_lpe_item_update_patheffect(sp_lpe_item, false, false);
}
void
@@ -140,17 +364,17 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem)
if (copies_to_360) {
rotation_angle.param_set_value(360.0/(double)num_copies);
}
- if (fuse_paths && rotation_angle * num_copies > 360 && rotation_angle > 0) {
+ if (fuse_paths && rotation_angle * num_copies > 360.1 && rotation_angle > 0) {
num_copies.param_set_value(floor(360/rotation_angle));
}
if (fuse_paths && copies_to_360) {
- num_copies.param_set_increments(2,2);
+ num_copies.param_set_increments(2.0,10.0);
if ((int)num_copies%2 !=0) {
num_copies.param_set_value(num_copies+1);
rotation_angle.param_set_value(360.0/(double)num_copies);
}
} else {
- num_copies.param_set_increments(1,1);
+ num_copies.param_set_increments(1.0, 10.0);
}
if (dist_angle_handle < 1.0) {
@@ -352,12 +576,56 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s
tmp_path.clear();
}
+Geom::PathVector
+LPECopyRotate::doEffect_path (Geom::PathVector const & path_in)
+{
+ Geom::PathVector path_out;
+ if (split_items && (fuse_paths || join_paths)) {
+ if (num_copies == 0) {
+ return path_out;
+ }
+ path_out = pathv_to_linear_and_cubic_beziers(path_in);
+ double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max()));
+ Geom::OptRect bbox = sp_lpe_item->geometricBounds();
+ double size_divider = Geom::distance(origin,bbox) + (diagonal * 2);
+ Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider;
+ Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider;
+ Geom::Path divider = Geom::Path(line_start);
+ divider.appendNew<Geom::LineSegment>((Geom::Point)origin);
+ divider.appendNew<Geom::LineSegment>(line_end);
+ divider.close();
+ Geom::PathVector triangle;
+ triangle.push_back(divider);
+ Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(triangle, path_out);
+ if (pig && ! path_out.empty() && !triangle.empty()) {
+ //TODO: Here can produce a crash because some knows problems in new boolops code
+ path_out = pig->getIntersection();
+ }
+ Geom::Affine r = Geom::identity();
+ Geom::Point dir = unit_vector(Geom::middle_point(line_start,line_end) - (Geom::Point)origin);
+ Geom::Point gap = dir * split_gap;
+ path_out *= Geom::Translate(gap);
+ } else {
+ // default behavior
+ for (unsigned int i=0; i < path_in.size(); i++) {
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
+ Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
+ // add the output path vector to the already accumulated vector:
+ for (unsigned int j=0; j < path.size(); j++) {
+ path_out.push_back(path[j]);
+ }
+ }
+ }
+ return pathv_to_linear_and_cubic_beziers(path_out);
+}
+
Geom::Piecewise<Geom::D2<Geom::SBasis> >
LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
{
using namespace Geom;
- if (num_copies == 1 && !fuse_paths) {
+ if ((num_copies == 1 && !fuse_paths) || split_items) {
return pwd2_in;
}
@@ -373,10 +641,10 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p
divider.appendNew<Geom::LineSegment>(line_end);
Piecewise<D2<SBasis> > output;
Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle));
+ PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001));
if (fuse_paths) {
Geom::PathVector path_out;
Geom::PathVector tmp_path;
- PathVector const original_pathv = path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001);
for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
if (path_it->empty()) {
continue;
@@ -403,7 +671,7 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p
output = paths_to_pw(path_out);
}
} else {
- Geom::PathVector output_pv = path_from_piecewise(output , 0.01);
+ Geom::PathVector output_pv;
for (int i = 0; i < num_copies; ++i) {
Rotate rot(-rad_from_deg(rotation_angle * i));
Affine t = pre * rot * Translate(origin);
@@ -449,6 +717,23 @@ LPECopyRotate::resetDefaults(SPItem const* item)
original_bbox(SP_LPE_ITEM(item));
}
+void
+LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
+{
+ processObjects(LPE_VISIBILITY);
+}
+
+void
+LPECopyRotate::doOnRemove (SPLPEItem const* /*lpeitem*/)
+{
+ //unset "erase_extra_objects" hook on sp-lpe-item.cpp
+ if (!erase_extra_objects) {
+ processObjects(LPE_TO_OBJECTS);
+ return;
+ }
+ processObjects(LPE_ERASE);
+}
+
} //namespace LivePathEffect
} /* namespace Inkscape */
diff --git a/src/live_effects/lpe-copy_rotate.h b/src/live_effects/lpe-copy_rotate.h
index c2ae2daf1..dbec2e1c3 100644
--- a/src/live_effects/lpe-copy_rotate.h
+++ b/src/live_effects/lpe-copy_rotate.h
@@ -15,6 +15,8 @@
*/
#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/text.h"
#include "live_effects/parameter/point.h"
#include "live_effects/lpegroupbbox.h"
@@ -26,13 +28,20 @@ public:
LPECopyRotate(LivePathEffectObject *lpeobject);
virtual ~LPECopyRotate();
virtual void doOnApply (SPLPEItem const* lpeitem);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
virtual void doBeforeEffect (SPLPEItem const* lpeitem);
+ virtual void doAfterEffect (SPLPEItem const* lpeitem);
virtual void setFusion(Geom::PathVector &path_in, Geom::Path divider, double sizeDivider);
virtual void split(Geom::PathVector &path_in, Geom::Path const &divider);
virtual void resetDefaults(SPItem const* item);
virtual void transform_multiply(Geom::Affine const& postmul, bool set);
+ virtual void doOnRemove (SPLPEItem const* /*lpeitem*/);
+ virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/);
virtual Gtk::Widget * newWidget();
+ void toItem(Geom::Affine transform, size_t i, bool reset);
+ void cloneD(SPObject *origin, SPObject *dest, bool root, bool reset);
+ void resetStyles();
protected:
virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
@@ -42,9 +51,11 @@ private:
ScalarParam starting_angle;
ScalarParam rotation_angle;
ScalarParam num_copies;
+ ScalarParam split_gap;
BoolParam copies_to_360;
BoolParam fuse_paths;
BoolParam join_paths;
+ BoolParam split_items;
Geom::Point A;
Geom::Point B;
Geom::Point dir;
@@ -52,6 +63,9 @@ private:
Geom::Point rot_pos;
Geom::Point previous_start_point;
double dist_angle_handle;
+ double previous_num_copies;
+ bool reset;
+ SPObject * container;
LPECopyRotate(const LPECopyRotate&);
LPECopyRotate& operator=(const LPECopyRotate&);
};
diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp
index 38cbeaac0..f2fc00aeb 100644
--- a/src/live_effects/lpe-curvestitch.cpp
+++ b/src/live_effects/lpe-curvestitch.cpp
@@ -54,6 +54,7 @@ LPECurveStitch::LPECurveStitch(LivePathEffectObject *lpeobject) :
prop_scale.param_set_digits(3);
prop_scale.param_set_increments(0.01, 0.10);
+ transformed = false;
}
LPECurveStitch::~LPECurveStitch()
@@ -106,8 +107,9 @@ LPECurveStitch::doEffect_path (Geom::PathVector const & path_in)
if (!Geom::are_near(start,end)) {
gdouble scaling_y = 1.0;
- if (scale_y_rel.get_value()) {
+ if (scale_y_rel.get_value() || transformed) {
scaling_y = (L2(end-start)/scaling)*prop_scale;
+ transformed = false;
} else {
scaling_y = prop_scale;
}
@@ -193,12 +195,8 @@ LPECurveStitch::transform_multiply(Geom::Affine const& postmul, bool set)
if (postmul.isTranslation()) {
strokepath.param_transform_multiply(postmul, set);
} else if (!scale_y_rel.get_value()) {
- // this basically means that for this transformation, the result should be the same as normal scaling the result path
- // don't know how to do this yet.
-// Geom::Affine new_postmul;
- //new_postmul.setIdentity();
-// new_postmul.setTranslation(postmul.translation());
-// Effect::transform_multiply(new_postmul, set);
+ transformed = true;
+ strokepath.param_transform_multiply(postmul, set);
}
}
diff --git a/src/live_effects/lpe-curvestitch.h b/src/live_effects/lpe-curvestitch.h
index c6ea66f6c..0a48046e0 100644
--- a/src/live_effects/lpe-curvestitch.h
+++ b/src/live_effects/lpe-curvestitch.h
@@ -43,6 +43,7 @@ private:
RandomParam endpoint_spacing_variation;
ScalarParam prop_scale;
BoolParam scale_y_rel;
+ bool transformed;
LPECurveStitch(const LPECurveStitch&);
LPECurveStitch& operator=(const LPECurveStitch&);
diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp
index 1e2eadfdb..2690ce160 100644
--- a/src/live_effects/lpe-fill-between-many.cpp
+++ b/src/live_effects/lpe-fill-between-many.cpp
@@ -19,9 +19,11 @@ namespace LivePathEffect {
LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
- linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this)
+ linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this),
+ fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this)
{
registerParameter( dynamic_cast<Parameter *>(&linked_paths) );
+ registerParameter( dynamic_cast<Parameter *>(&fuse) );
//perceived_path = true;
}
@@ -46,7 +48,11 @@ void LPEFillBetweenMany::doEffect (SPCurve * curve)
if (!res_pathv.empty()) {
linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj);
- res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint());
+ if (!are_near(res_pathv.front().finalPoint(), linked_path.initialPoint(), 0.01) || !fuse) {
+ res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint());
+ } else {
+ linked_path.setInitial(res_pathv.front().finalPoint());
+ }
res_pathv.front().append(linked_path);
} else {
firstObj = SP_ITEM(obj);
diff --git a/src/live_effects/lpe-fill-between-many.h b/src/live_effects/lpe-fill-between-many.h
index 99ee8b15f..552c8037d 100644
--- a/src/live_effects/lpe-fill-between-many.h
+++ b/src/live_effects/lpe-fill-between-many.h
@@ -24,6 +24,7 @@ public:
private:
OriginalPathArrayParam linked_paths;
+ BoolParam fuse;
private:
LPEFillBetweenMany(const LPEFillBetweenMany&);
diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp
index 0dbebdf26..8dc55357f 100644
--- a/src/live_effects/lpe-fill-between-strokes.cpp
+++ b/src/live_effects/lpe-fill-between-strokes.cpp
@@ -18,11 +18,15 @@ LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this),
second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this),
- reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this)
+ reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this),
+ close(_("Close path"), _("Close path"), "close", &wr, this),
+ fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this)
{
registerParameter( dynamic_cast<Parameter *>(&linked_path) );
registerParameter( dynamic_cast<Parameter *>(&second_path) );
registerParameter( dynamic_cast<Parameter *>(&reverse_second) );
+ registerParameter( dynamic_cast<Parameter *>(&close) );
+ registerParameter( dynamic_cast<Parameter *>(&fuse) );
//perceived_path = true;
}
@@ -51,22 +55,22 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
}
if ( !result_linked_pathv.empty() && !result_second_pathv.empty() && !result_linked_pathv.front().closed() ) {
- if (reverse_second.get_value())
- {
- result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint());
- result_linked_pathv.front().append(result_second_pathv.front().reversed());
+ if (reverse_second.get_value()) {
+ result_second_pathv.front() = result_second_pathv.front().reversed();
}
- else
- {
+ if (!are_near(result_linked_pathv.front().finalPoint(), result_second_pathv.front().initialPoint(),0.01) || !fuse) {
result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint());
- result_linked_pathv.front().append(result_second_pathv.front());
+ } else {
+ result_second_pathv.front().setInitial(result_linked_pathv.front().finalPoint());
+ }
+ result_linked_pathv.front().append(result_second_pathv.front());
+ if (close) {
+ result_linked_pathv.front().close();
}
curve->set_pathvector(result_linked_pathv);
- }
- else if ( !result_linked_pathv.empty() ) {
+ } else if ( !result_linked_pathv.empty() ) {
curve->set_pathvector(result_linked_pathv);
- }
- else if ( !result_second_pathv.empty() ) {
+ } else if ( !result_second_pathv.empty() ) {
curve->set_pathvector(result_second_pathv);
}
}
diff --git a/src/live_effects/lpe-fill-between-strokes.h b/src/live_effects/lpe-fill-between-strokes.h
index ec57b1852..14eb9167c 100644
--- a/src/live_effects/lpe-fill-between-strokes.h
+++ b/src/live_effects/lpe-fill-between-strokes.h
@@ -26,6 +26,8 @@ private:
OriginalPathParam linked_path;
OriginalPathParam second_path;
BoolParam reverse_second;
+ BoolParam close;
+ BoolParam fuse;
private:
LPEFillBetweenStrokes(const LPEFillBetweenStrokes&);
diff --git a/src/live_effects/lpe-measure-line.cpp b/src/live_effects/lpe-measure-line.cpp
index af2a8e919..ef87be81c 100644
--- a/src/live_effects/lpe-measure-line.cpp
+++ b/src/live_effects/lpe-measure-line.cpp
@@ -21,7 +21,9 @@
#include "svg/svg-color.h"
#include "svg/svg.h"
#include "display/curve.h"
+#include "helper/geom.h"
#include "2geom/affine.h"
+#include "path-chemistry.h"
#include "style.h"
#include "sp-root.h"
#include "sp-defs.h"
@@ -165,12 +167,6 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) :
LPEMeasureLine::~LPEMeasureLine() {}
-void swap(Geom::Point &A, Geom::Point &B){
- Geom::Point tmp = A;
- A = B;
- B = tmp;
-}
-
void
LPEMeasureLine::createArrowMarker(const char * mode)
{
@@ -229,7 +225,7 @@ LPEMeasureLine::createArrowMarker(const char * mode)
elemref = SP_OBJECT(document->getDefs()->appendChildRepr(arrow));
Inkscape::GC::release(arrow);
}
- elements.push_back(mode);
+ items.push_back(mode);
}
void
@@ -369,7 +365,7 @@ LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angl
copy->setAttribute("id", id);
elemref = elemref_copy;
}
- elements.push_back(id);
+ items.push_back(id);
Geom::OptRect bounds = SP_ITEM(elemref)->bounds(SPItem::GEOMETRIC_BBOX);
if (bounds) {
anotation_width = bounds->width() * 1.4;
@@ -483,7 +479,7 @@ LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, const char * id, b
elemref->deleteObject();
copy->setAttribute("id", id);
}
- elements.push_back(id);
+ items.push_back(id);
}
void
@@ -535,7 +531,7 @@ LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem)
sp_lpe_item->getCurrentLPE() != this){
return;
}
- elements.clear();
+ items.clear();
start_stored = start;
end_stored = end;
Geom::Point hstart = start;
@@ -680,60 +676,12 @@ LPEMeasureLine::doOnRemove (SPLPEItem const* /*lpeitem*/)
//unset "erase_extra_objects" hook on sp-lpe-item.cpp
if (!erase_extra_objects) {
processObjects(LPE_TO_OBJECTS);
- elements.clear();
+ items.clear();
return;
}
processObjects(LPE_ERASE);
}
-void
-LPEMeasureLine::processObjects(LpeAction lpe_action)
-{
- SPDocument * document = SP_ACTIVE_DOCUMENT;
- for (std::vector<const char *>::iterator el_it = elements.begin();
- el_it != elements.end(); ++el_it) {
- const char * id = *el_it;
- if (!id || strlen(id) == 0) {
- return;
- }
- SPObject *elemref = NULL;
- if (elemref = document->getObjectById(id)) {
- SPCSSAttr *css;
- Glib::ustring css_str;
- switch (lpe_action){
- case LPE_TO_OBJECTS:
- elemref->getRepr()->setAttribute("inkscape:path-effect", NULL);
- elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL);
- break;
-
- case LPE_ERASE:
- if (std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) {
- elemref->deleteObject();
- }
- break;
-
- case LPE_VISIBILITY:
- css = sp_repr_css_attr_new();
- sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style"));
- if (!this->isVisible() && std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) {
- css->setAttribute("display", "none");
- } else {
- css->setAttribute("display", NULL);
- }
- sp_repr_css_write_string(css,css_str);
- elemref->getRepr()->setAttribute("style", css_str.c_str());
- break;
-
- default:
- break;
- }
- }
- }
- if (lpe_action == LPE_ERASE) {
- elements.clear();
- }
-}
-
Gtk::Widget *LPEMeasureLine::newWidget()
{
// use manage here, because after deletion of Effect object, others might
diff --git a/src/live_effects/lpe-measure-line.h b/src/live_effects/lpe-measure-line.h
index c69921a4d..724c0d924 100644
--- a/src/live_effects/lpe-measure-line.h
+++ b/src/live_effects/lpe-measure-line.h
@@ -35,12 +35,6 @@ enum OrientationMethod {
OM_END
};
-enum LpeAction {
- LPE_ERASE = 0,
- LPE_TO_OBJECTS,
- LPE_VISIBILITY
-};
-
class LPEMeasureLine : public Effect {
public:
LPEMeasureLine(LivePathEffectObject *lpeobject);
@@ -51,7 +45,6 @@ public:
virtual void doEffect (SPCurve * curve){}; //stop the chain
virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/);
virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in);
- void processObjects(LpeAction lpe_action);
void createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows = false);
void createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid);
void onExpanderChanged();
@@ -92,7 +85,6 @@ private:
double arrow_gap;
Geom::Point start_stored;
Geom::Point end_stored;
- std::vector<const char *> elements;
/* Geom::Affine affine_over;*/
LPEMeasureLine(const LPEMeasureLine &);
LPEMeasureLine &operator=(const LPEMeasureLine &);
diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp
index 61b2b8b5c..7f0a93c52 100644
--- a/src/live_effects/lpe-mirror_symmetry.cpp
+++ b/src/live_effects/lpe-mirror_symmetry.cpp
@@ -15,11 +15,22 @@
*/
#include <gtkmm.h>
+#include "live_effects/lpeobject.h"
+#include "live_effects/lpeobject-reference.h"
#include "live_effects/lpe-mirror_symmetry.h"
-#include <display/curve.h>
-#include <svg/path-string.h>
+#include "display/curve.h"
+#include "svg/path-string.h"
+#include "svg/svg.h"
+#include "sp-defs.h"
#include "helper/geom.h"
-#include <2geom/path-intersection.h>
+#include "2geom/intersection-graph.h"
+#include "2geom/path-intersection.h"
+#include "2geom/affine.h"
+#include "helper/geom.h"
+#include "sp-lpe-item.h"
+#include "path-chemistry.h"
+#include "style.h"
+#include "xml/sp-css-attr.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -40,73 +51,69 @@ MTConverter(ModeTypeData, MT_END);
LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
mode(_("Mode"), _("Symmetry move mode"), "mode", MTConverter, &wr, this, MT_FREE),
+ split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001),
discard_orig_path(_("Discard original path"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false),
fuse_paths(_("Fuse paths"), _("Fuse original and the reflection into a single path"), "fuse_paths", &wr, this, false),
oposite_fuse(_("Opposite fuse"), _("Picks the other side of the mirror as the original"), "oposite_fuse", &wr, this, false),
+ split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false),
start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, _("Adjust start of mirroring")),
end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, _("Adjust end of mirroring")),
- center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring"))
+ center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")),
+ id_origin("id origin", "store the id of the first LPEItem", "id_origin", &wr, this,"")
{
show_orig_path = true;
registerParameter(&mode);
- registerParameter( &discard_orig_path);
- registerParameter( &fuse_paths);
- registerParameter( &oposite_fuse);
- registerParameter( &start_point);
- registerParameter( &end_point);
- registerParameter( &center_point);
- previous_center = Geom::Point(0,0);
+ registerParameter(&split_gap);
+ registerParameter(&discard_orig_path);
+ registerParameter(&fuse_paths);
+ registerParameter(&oposite_fuse);
+ registerParameter(&split_items);
+ registerParameter(&start_point);
+ registerParameter(&end_point);
+ registerParameter(&center_point);
+ registerParameter(&id_origin);
+ id_origin.param_hide_canvas_text();
+ split_gap.param_set_range(-999999.0, 999999.0);
+ split_gap.param_set_increments(0.1, 0.1);
+ split_gap.param_set_digits(5);
apply_to_clippath_and_mask = true;
+ previous_center = Geom::Point(0,0);
}
LPEMirrorSymmetry::~LPEMirrorSymmetry()
{
}
-Gtk::Widget * LPEMirrorSymmetry::newWidget()
+void
+LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem)
{
- // use manage here, because after deletion of Effect object, others might
- // still be pointing to this widget.
- Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
-
- vbox->set_border_width(5);
- vbox->set_homogeneous(false);
- vbox->set_spacing(2);
-
- std::vector<Parameter *>::iterator it = param_vector.begin();
- while (it != param_vector.end()) {
- if ((*it)->widget_is_visible) {
- Parameter *param = *it;
- Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
- Glib::ustring *tip = param->param_getTooltip();
- if (widg) {
- if (param->param_key != "center_point") {
- vbox->pack_start(*widg, true, true, 2);
- if (tip) {
- widg->set_tooltip_text(*tip);
- } else {
- widg->set_tooltip_text("");
- widg->set_has_tooltip(false);
- }
- }
- }
+ if (split_items && !discard_orig_path) {
+ container = dynamic_cast<SPObject *>(sp_lpe_item->parent);
+ SPDocument * doc = SP_ACTIVE_DOCUMENT;
+ Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot();
+ Inkscape::XML::Node *root_origin = doc->getReprRoot();
+ if (root_origin != root) {
+ return;
}
-
- ++it;
+ Geom::Line ls((Geom::Point)start_point, (Geom::Point)end_point);
+ Geom::Affine m = Geom::reflection (ls.vector(), (Geom::Point)start_point);
+ m = m * sp_lpe_item->transform;
+ toMirror(m);
+ } else {
+ processObjects(LPE_ERASE);
+ items.clear();
}
- return dynamic_cast<Gtk::Widget *>(vbox);
}
-
void
LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem)
{
+
using namespace Geom;
original_bbox(lpeitem);
//center_point->param_set_liveupdate(false);
Point point_a(boundingbox_X.max(), boundingbox_Y.min());
Point point_b(boundingbox_X.max(), boundingbox_Y.max());
- Point point_c(boundingbox_X.max(), boundingbox_Y.middle());
if (mode == MT_Y) {
point_a = Geom::Point(boundingbox_X.min(),center_point[Y]);
point_b = Geom::Point(boundingbox_X.max(),center_point[Y]);
@@ -173,6 +180,172 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem)
}
void
+LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root)
+{
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) {
+ std::vector< SPObject * > childs = origin->childList(true);
+ size_t index = 0;
+ for (std::vector<SPObject * >::iterator obj_it = childs.begin();
+ obj_it != childs.end(); ++obj_it) {
+ SPObject *dest_child = dest->nthChild(index);
+ cloneD(*obj_it, dest_child, live, false);
+ index++;
+ }
+ }
+ SPShape * shape = SP_SHAPE(origin);
+ SPPath * path = SP_PATH(dest);
+ if (!path && !SP_IS_GROUP(dest)) {
+ Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0);
+ dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL);
+ path = SP_PATH(dest);
+ }
+ if (path && shape) {
+ if ( live) {
+ SPCurve *c = NULL;
+ if (root) {
+ c = new SPCurve();
+ c->set_pathvector(pathvector_before_effect);
+ } else {
+ c = shape->getCurve();
+ }
+ if (c) {
+ path->setCurve(c, TRUE);
+ c->unref();
+ } else {
+ dest->getRepr()->setAttribute("d", NULL);
+ }
+ } else {
+ dest->getRepr()->setAttribute("d", origin->getRepr()->attribute("d"));
+ }
+ }
+}
+
+void
+LPEMirrorSymmetry::toMirror(Geom::Affine transform)
+{
+ SPDocument * document = SP_ACTIVE_DOCUMENT;
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ const char * id_origin_char = id_origin.param_getSVGValue();
+ const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str());
+ items.clear();
+ items.push_back(elemref_id);
+ SPObject *elemref= NULL;
+ Inkscape::XML::Node *phantom = NULL;
+ if (elemref = document->getObjectById(elemref_id)) {
+ phantom = elemref->getRepr();
+ } else {
+ phantom = sp_lpe_item->getRepr()->duplicate(xml_doc);
+ std::vector<const char *> attrs;
+ attrs.push_back("inkscape:path-effect");
+ attrs.push_back("inkscape:original-d");
+ attrs.push_back("sodipodi:type");
+ attrs.push_back("sodipodi:rx");
+ attrs.push_back("sodipodi:ry");
+ attrs.push_back("sodipodi:cx");
+ attrs.push_back("sodipodi:cy");
+ attrs.push_back("sodipodi:end");
+ attrs.push_back("sodipodi:start");
+ attrs.push_back("inkscape:flatsided");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:rounded");
+ attrs.push_back("sodipodi:arg1");
+ attrs.push_back("sodipodi:arg2");
+ attrs.push_back("sodipodi:r1");
+ attrs.push_back("sodipodi:r2");
+ attrs.push_back("sodipodi:sides");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("sodipodi:argument");
+ attrs.push_back("sodipodi:expansion");
+ attrs.push_back("sodipodi:radius");
+ attrs.push_back("sodipodi:revolution");
+ attrs.push_back("sodipodi:t0");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("inkscape:randomized");
+ attrs.push_back("x");
+ attrs.push_back("y");
+ attrs.push_back("rx");
+ attrs.push_back("ry");
+ attrs.push_back("width");
+ attrs.push_back("height");
+ for(const char * attr : attrs) {
+ phantom->setAttribute(attr, NULL);
+ }
+ }
+ phantom->setAttribute("id", elemref_id);
+ if (!elemref) {
+ elemref = container->appendChildRepr(phantom);
+ Inkscape::GC::release(phantom);
+ }
+ cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true);
+ elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform));
+ if (elemref->parent != container) {
+ Inkscape::XML::Node *copy = phantom->duplicate(xml_doc);
+ copy->setAttribute("id", elemref_id);
+ container->appendChildRepr(copy);
+ Inkscape::GC::release(copy);
+ elemref->deleteObject();
+ }
+}
+
+Gtk::Widget *
+LPEMirrorSymmetry::newWidget()
+{
+ // use manage here, because after deletion of Effect object, others might
+ // still be pointing to this widget.
+ Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
+ vbox->set_border_width(5);
+ vbox->set_homogeneous(false);
+ vbox->set_spacing(2);
+
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter * param = *it;
+ if (param->param_key == "id_origin" || param->param_key == "center_point") {
+ ++it;
+ continue;
+ }
+ Gtk::Widget * widg = param->param_newWidget();
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ }
+
+ ++it;
+ }
+ this->upd_params = false;
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+//TODO: Migrate the tree next function to effect.cpp/h to avoid duplication
+void
+LPEMirrorSymmetry::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/)
+{
+ processObjects(LPE_VISIBILITY);
+}
+
+void
+LPEMirrorSymmetry::doOnRemove (SPLPEItem const* /*lpeitem*/)
+{
+ //unset "erase_extra_objects" hook on sp-lpe-item.cpp
+ if (!erase_extra_objects) {
+ processObjects(LPE_TO_OBJECTS);
+ return;
+ }
+ processObjects(LPE_ERASE);
+}
+
+void
LPEMirrorSymmetry::transform_multiply(Geom::Affine const& postmul, bool set)
{
// cycle through all parameters. Most parameters will not need transformation, but path and point params do.
@@ -193,18 +366,26 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem)
Point point_a(boundingbox_X.max(), boundingbox_Y.min());
Point point_b(boundingbox_X.max(), boundingbox_Y.max());
Point point_c(boundingbox_X.max(), boundingbox_Y.middle());
- start_point.param_setValue(point_a, true);
+ start_point.param_setValue(point_a);
start_point.param_update_default(point_a);
- end_point.param_setValue(point_b, true);
+ end_point.param_setValue(point_b);
end_point.param_update_default(point_b);
center_point.param_setValue(point_c, true);
previous_center = center_point;
+ SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
+ if (!lpeitem->hasPathEffectOfType(this->effectType(), false) ){ //first applied not ready yet
+ id_origin.param_setValue(lpeitem->getRepr()->attribute("id"));
+ id_origin.write_to_SVG();
+ }
}
Geom::PathVector
LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
{
+ if (split_items && !fuse_paths) {
+ return path_in;
+ }
Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in);
Geom::PathVector path_out;
@@ -214,15 +395,51 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point);
Geom::Affine m = Geom::reflection (line_separation.vector(), (Geom::Point)start_point);
-
- if (fuse_paths && !discard_orig_path) {
+ if (split_items && fuse_paths) {
+ Geom::OptRect bbox = sp_lpe_item->geometricBounds();
+ Geom::Path p(Geom::Point(bbox->left(), bbox->top()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
+ p.close();
+ p *= Geom::Translate(bbox->midpoint()).inverse();
+ p *= Geom::Scale(1.2);
+ p *= Geom::Rotate(line_separation.angle());
+ p *= Geom::Translate(bbox->midpoint());
+ bbox = p.boundsFast();
+ p.clear();
+ p.start(Geom::Point(bbox->left(), bbox->top()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
+ p.close();
+ p *= Geom::Translate(bbox->midpoint()).inverse();
+ p *= Geom::Rotate(line_separation.angle());
+ p *= Geom::Translate(bbox->midpoint());
+ Geom::Point base(p.pointAt(3));
+ if (oposite_fuse) {
+ base = p.pointAt(0);
+ }
+ p *= Geom::Translate(line_separation.pointAt(line_separation.nearestTime(base)) - base);
+ Geom::PathVector pv_bbox;
+ pv_bbox.push_back(p);
+ Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(pv_bbox, original_pathv);
+ if (pig && !original_pathv.empty() && !pv_bbox.empty()) {
+ path_out = pig->getBminusA();
+ }
+ Geom::Point dir = rot90(unit_vector((Geom::Point)end_point - (Geom::Point)start_point));
+ Geom::Point gap = dir * split_gap;
+ path_out *= Geom::Translate(gap);
+ } else if (fuse_paths && !discard_orig_path) {
for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
path_it != original_pathv.end(); ++path_it)
{
if (path_it->empty()) {
continue;
}
- Geom::PathVector tmp_path;
+ Geom::PathVector tmp_pathvector;
double time_start = 0.0;
int position = 0;
bool end_open = false;
@@ -268,11 +485,11 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
Geom::Path mirror = portion.reversed() * m;
mirror.setInitial(portion.finalPoint());
portion.append(mirror);
- if(i!=0) {
+ if(i != 0) {
portion.setFinal(portion.initialPoint());
portion.close();
}
- tmp_path.push_back(portion);
+ tmp_pathvector.push_back(portion);
}
portion.clear();
}
@@ -293,36 +510,33 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
portion.append(mirror);
portion = portion.reversed();
if (!original.closed()) {
- tmp_path.push_back(portion);
+ tmp_pathvector.push_back(portion);
} else {
- if (cs.size() > 1 && tmp_path.size() > 0 && tmp_path[0].size() > 0 ) {
- portion.setFinal(tmp_path[0].initialPoint());
- portion.setInitial(tmp_path[0].finalPoint());
- tmp_path[0].append(portion);
+ if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) {
+ portion.setFinal(tmp_pathvector[0].initialPoint());
+ portion.setInitial(tmp_pathvector[0].finalPoint());
+ tmp_pathvector[0].append(portion);
} else {
- tmp_path.push_back(portion);
+ tmp_pathvector.push_back(portion);
}
- tmp_path[0].close();
+ tmp_pathvector[0].close();
}
portion.clear();
}
}
}
if (cs.size() == 0 && position == 1) {
- tmp_path.push_back(original);
- tmp_path.push_back(original * m);
+ tmp_pathvector.push_back(original);
+ tmp_pathvector.push_back(original * m);
}
- path_out.insert(path_out.end(), tmp_path.begin(), tmp_path.end());
- tmp_path.clear();
+ path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end());
+ tmp_pathvector.clear();
}
- }
-
- if (!fuse_paths || discard_orig_path) {
+ } else if (!fuse_paths || discard_orig_path) {
for (size_t i = 0; i < original_pathv.size(); ++i) {
path_out.push_back(original_pathv[i] * m);
}
}
-
return path_out;
}
diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h
index b4967173a..592a11894 100644
--- a/src/live_effects/lpe-mirror_symmetry.h
+++ b/src/live_effects/lpe-mirror_symmetry.h
@@ -18,8 +18,8 @@
#include "live_effects/effect.h"
#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/text.h"
#include "live_effects/parameter/point.h"
-#include "live_effects/parameter/path.h"
#include "live_effects/parameter/enum.h"
#include "live_effects/lpegroupbbox.h"
@@ -41,23 +41,32 @@ public:
virtual ~LPEMirrorSymmetry();
virtual void doOnApply (SPLPEItem const* lpeitem);
virtual void doBeforeEffect (SPLPEItem const* lpeitem);
+ virtual void doAfterEffect (SPLPEItem const* lpeitem);
virtual void transform_multiply(Geom::Affine const& postmul, bool set);
virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
+ virtual void doOnRemove (SPLPEItem const* /*lpeitem*/);
+ virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/);
virtual Gtk::Widget * newWidget();
+ void toMirror(Geom::Affine transform);
+ // void cloneAttrbutes(Inkscape::XML::Node * origin, Inkscape::XML::Node * dest, const char * first_attribute, ...);
+ void cloneD(SPObject *origin, SPObject *dest, bool live, bool root);
protected:
virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
private:
EnumParam<ModeType> mode;
+ ScalarParam split_gap;
BoolParam discard_orig_path;
BoolParam fuse_paths;
BoolParam oposite_fuse;
+ BoolParam split_items;
PointParam start_point;
PointParam end_point;
PointParam center_point;
+ TextParam id_origin;
Geom::Point previous_center;
-
+ SPObject * container;
LPEMirrorSymmetry(const LPEMirrorSymmetry&);
LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&);
};
diff --git a/src/live_effects/lpe-patternalongpath.cpp b/src/live_effects/lpe-patternalongpath.cpp
index c1853ef22..966e9020e 100644
--- a/src/live_effects/lpe-patternalongpath.cpp
+++ b/src/live_effects/lpe-patternalongpath.cpp
@@ -11,6 +11,7 @@
#include <2geom/bezier-to-sbasis.h>
#include "knotholder.h"
+#include <cmath>
#include <algorithm>
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
@@ -161,7 +162,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con
// spacing.param_set_range(-pattBndsX.extent()*.9, Geom::infinity());
// }
- y0+=noffset;
+ y0 += noffset;
std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in;
paths_in = split_at_discontinuities(pwd2_in);
@@ -197,7 +198,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con
case PAPCT_REPEATED_STRETCHED:
// if uskeleton is closed:
- if(path_i.segs.front().at0() == path_i.segs.back().at1()){
+ if (are_near(path_i.segs.front().at0(), path_i.segs.back().at1())){
nbCopies = std::max(1, static_cast<int>(std::floor((uskeleton.domain().extent() - toffset)/(pattBndsX->extent()+xspace))));
pattBndsX = Interval(pattBndsX->min(),pattBndsX->max()+xspace);
scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX->extent());
@@ -213,11 +214,13 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con
return pwd2_in;
};
+ //Ceil to 6 decimals
+ scaling = ceil(scaling * 1000000) / 1000000;
double pattWidth = pattBndsX->extent() * scaling;
- x*=scaling;
+ x *= scaling;
if ( scale_y_rel.get_value() ) {
- y*=(scaling * prop_scale);
+ y *= prop_scale * scaling;
} else {
y *= prop_scale;
}
@@ -235,7 +238,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con
offs+=pattWidth;
}
}
- if (fuse_tolerance > 0){
+ if (fuse_tolerance > 0){
pre_output = fuse_nearby_ends(pre_output, fuse_tolerance);
for (unsigned i=0; i<pre_output.size(); i++){
output.concat(pre_output[i]);
@@ -264,7 +267,6 @@ LPEPatternAlongPath::transform_multiply(Geom::Affine const& postmul, bool set)
pattern.param_transform_multiply(postmul, set);
pattern.write_to_SVG();
}
- sp_lpe_item_update_patheffect (sp_lpe_item, false, true);
}
void
diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp
index e0dac0687..18b5b724d 100644
--- a/src/live_effects/lpe-perspective-envelope.cpp
+++ b/src/live_effects/lpe-perspective-envelope.cpp
@@ -379,7 +379,7 @@ LPEPerspectiveEnvelope::newWidget()
hbox_down_handles->pack_start(*widg, true, true, 2);
}
if (tip) {
- widg->set_tooltip_text(*tip);
+ widg->set_tooltip_markup(*tip);
} else {
widg->set_tooltip_text("");
widg->set_has_tooltip(false);
diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp
index 1940806bd..83cd6623c 100644
--- a/src/live_effects/lpeobject-reference.cpp
+++ b/src/live_effects/lpeobject-reference.cpp
@@ -6,9 +6,11 @@
* Released under GNU GPL, read the file 'COPYING' for more information.
*/
+#include "live_effects/lpeobject-reference.h"
+
#include <string.h>
-#include "live_effects/lpeobject-reference.h"
+#include "bad-uri-exception.h"
#include "live_effects/lpeobject.h"
#include "uri.h"
diff --git a/src/live_effects/parameter/bool.cpp b/src/live_effects/parameter/bool.cpp
index af99ef362..813c06b4e 100644
--- a/src/live_effects/parameter/bool.cpp
+++ b/src/live_effects/parameter/bool.cpp
@@ -72,7 +72,7 @@ BoolParam::param_newWidget()
checkwdg->setActive(value);
checkwdg->setProgrammatically = false;
checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter"));
-
+ param_effect->upd_params = false;
return dynamic_cast<Gtk::Widget *> (checkwdg);
} else {
return NULL;
@@ -82,6 +82,7 @@ BoolParam::param_newWidget()
void
BoolParam::param_setValue(bool newvalue)
{
+ param_effect->upd_params = true;
value = newvalue;
}
diff --git a/src/live_effects/parameter/item-reference.cpp b/src/live_effects/parameter/item-reference.cpp
new file mode 100644
index 000000000..a775d93b7
--- /dev/null
+++ b/src/live_effects/parameter/item-reference.cpp
@@ -0,0 +1,44 @@
+/*
+ * The reference corresponding to href of LPE Item parameter.
+ *
+ * Copyright (C) 2008 Johan Engelen
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include "live_effects/parameter/item-reference.h"
+
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "sp-item-group.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+bool ItemReference::_acceptObject(SPObject * const obj) const
+{
+ if (SP_IS_SHAPE(obj) || SP_IS_TEXT(obj) || SP_IS_GROUP(obj)) {
+ /* Refuse references to lpeobject */
+ if (obj == getOwner()) {
+ return false;
+ }
+ // TODO: check whether the referred item has this LPE applied, if so: deny deny deny!
+ return URIReference::_acceptObject(obj);
+ } else {
+ return false;
+ }
+}
+
+} // namespace LivePathEffect
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/item-reference.h b/src/live_effects/parameter/item-reference.h
new file mode 100644
index 000000000..91231455a
--- /dev/null
+++ b/src/live_effects/parameter/item-reference.h
@@ -0,0 +1,56 @@
+#ifndef SEEN_LPE_ITEM_REFERENCE_H
+#define SEEN_LPE_ITEM_REFERENCE_H
+
+/*
+ * Copyright (C) 2008-2012 Authors
+ * Authors: Johan Engelen
+ * Abhishek Sharma
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include <uri-references.h>
+
+class SPItem;
+namespace Inkscape {
+namespace XML { class Node; }
+
+namespace LivePathEffect {
+
+/**
+ * The reference corresponding to href of LPE ItemParam.
+ */
+class ItemReference : public Inkscape::URIReference {
+public:
+ ItemReference(SPObject *owner) : URIReference(owner) {}
+
+ SPItem *getObject() const {
+ return (SPItem *)URIReference::getObject();
+ }
+
+protected:
+ virtual bool _acceptObject(SPObject * const obj) const;
+
+private:
+ ItemReference(const ItemReference&);
+ ItemReference& operator=(const ItemReference&);
+};
+
+} // namespace LivePathEffect
+
+} // namespace Inkscape
+
+
+
+#endif /* !SEEN_LPE_PATH_REFERENCE_H */
+
+/*
+ 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/parameter/item.cpp b/src/live_effects/parameter/item.cpp
new file mode 100644
index 000000000..93cf2b15f
--- /dev/null
+++ b/src/live_effects/parameter/item.cpp
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ * Abhishek Sharma
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/item.h"
+
+#include <glibmm/i18n.h>
+
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "bad-uri-exception.h"
+#include "ui/widget/point.h"
+
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+
+#include "widgets/icon.h"
+#include "selection-chemistry.h"
+#include "xml/repr.h"
+#include "desktop.h"
+#include "inkscape.h"
+#include "message-stack.h"
+
+// clipboard support
+#include "ui/clipboard.h"
+// required for linking to other paths
+#include "uri.h"
+
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+ItemParam::ItemParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, const gchar * default_value)
+ : Parameter(label, tip, key, wr, effect),
+ changed(true),
+ href(NULL),
+ ref( (SPObject*)effect->getLPEObj() )
+{
+ defvalue = g_strdup(default_value);
+ ref_changed_connection = ref.changedSignal().connect(sigc::mem_fun(*this, &ItemParam::ref_changed));
+}
+
+ItemParam::~ItemParam()
+{
+ remove_link();
+ g_free(defvalue);
+}
+
+void
+ItemParam::param_set_default()
+{
+ param_readSVGValue(defvalue);
+}
+
+
+void
+ItemParam::param_set_and_write_default()
+{
+ param_write_to_repr(defvalue);
+}
+
+bool
+ItemParam::param_readSVGValue(const gchar * strvalue)
+{
+ if (strvalue) {
+ remove_link();
+ if (strvalue[0] == '#') {
+ if (href)
+ g_free(href);
+ href = g_strdup(strvalue);
+ try {
+ ref.attach(Inkscape::URI(href));
+ //lp:1299948
+ SPItem* i = ref.getObject();
+ if (i) {
+ linked_modified_callback(i, SP_OBJECT_MODIFIED_FLAG);
+ } // else: document still processing new events. Repr of the linked object not created yet.
+ } catch (Inkscape::BadURIException &e) {
+ g_warning("%s", e.what());
+ ref.detach();
+ }
+ }
+ emit_changed();
+ return true;
+ }
+
+ return false;
+}
+
+gchar *
+ItemParam::param_getSVGValue() const
+{
+ return g_strdup(href);
+}
+
+Gtk::Widget *
+ItemParam::param_newWidget()
+{
+ Gtk::HBox * _widget = Gtk::manage(new Gtk::HBox());
+ Gtk::Widget* pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-clone"), Inkscape::ICON_SIZE_BUTTON) );
+ Gtk::Button * pButton = Gtk::manage(new Gtk::Button());
+ Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ pLabel->set_tooltip_text(param_tooltip);
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &ItemParam::on_link_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to item on clipboard"));
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+ItemParam::emit_changed()
+{
+ changed = true;
+ signal_item_changed.emit();
+}
+
+
+void
+ItemParam::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+}
+
+
+void
+ItemParam::start_listening(SPObject * to)
+{
+ if ( to == NULL ) {
+ return;
+ }
+ linked_delete_connection = to->connectDelete(sigc::mem_fun(*this, &ItemParam::linked_delete));
+ linked_modified_connection = to->connectModified(sigc::mem_fun(*this, &ItemParam::linked_modified));
+ if (SP_IS_ITEM(to)) {
+ linked_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::mem_fun(*this, &ItemParam::linked_transformed));
+ }
+ linked_modified(to, SP_OBJECT_MODIFIED_FLAG); // simulate linked_modified signal, so that path data is updated
+}
+
+void
+ItemParam::quit_listening(void)
+{
+ linked_modified_connection.disconnect();
+ linked_delete_connection.disconnect();
+ linked_transformed_connection.disconnect();
+}
+
+void
+ItemParam::ref_changed(SPObject */*old_ref*/, SPObject *new_ref)
+{
+ quit_listening();
+ if ( new_ref ) {
+ start_listening(new_ref);
+ }
+}
+
+void
+ItemParam::remove_link()
+{
+ if (href) {
+ ref.detach();
+ g_free(href);
+ href = NULL;
+ }
+}
+
+void
+ItemParam::linked_delete(SPObject */*deleted*/)
+{
+ quit_listening();
+ remove_link();
+}
+
+void ItemParam::linked_modified(SPObject *linked_obj, guint flags)
+{
+ linked_modified_callback(linked_obj, flags);
+}
+
+void ItemParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item)
+{
+ linked_transformed_callback(rel_transf, moved_item);
+}
+
+void
+ItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/)
+{
+ emit_changed();
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+void
+ItemParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ const gchar * iid = cm->getFirstObjectID();
+ if (!iid) {
+ return;
+ }
+
+ Glib::ustring itemid(iid);
+
+ if (itemid.empty()) {
+ return;
+ }
+
+ // add '#' at start to make it an uri.
+ itemid.insert(itemid.begin(), '#');
+ if ( href && strcmp(itemid.c_str(), href) == 0 ) {
+ // no change, do nothing
+ return;
+ } else {
+ // TODO:
+ // check if id really exists in document, or only in clipboard document: if only in clipboard then invalid
+ // check if linking to object to which LPE is applied (maybe delegated to PathReference
+
+ param_write_to_repr(itemid.c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link item parameter to path"));
+ }
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/item.h b/src/live_effects/parameter/item.h
new file mode 100644
index 000000000..6c719d451
--- /dev/null
+++ b/src/live_effects/parameter/item.h
@@ -0,0 +1,79 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/item-reference.h"
+#include <stddef.h>
+#include <sigc++/sigc++.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class ItemParam : public Parameter {
+public:
+ ItemParam ( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ const gchar * default_value = "");
+ virtual ~ItemParam();
+ virtual Gtk::Widget * param_newWidget();
+
+ virtual bool param_readSVGValue(const gchar * strvalue);
+ virtual gchar * param_getSVGValue() const;
+
+ virtual void param_set_default();
+ void param_set_and_write_default();
+ virtual void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector<Geom::PathVector> &hp_vec);
+
+ sigc::signal <void> signal_item_pasted;
+ sigc::signal <void> signal_item_changed;
+ bool changed; /* this gets set whenever the path is changed (this is set to true, and then the signal_item_changed signal is emitted).
+ * the user must set it back to false if she wants to use it sensibly */
+protected:
+
+ gchar * href; // contains link to other object, e.g. "#path2428", NULL if ItemParam contains pathdata itself
+ ItemReference ref;
+ sigc::connection ref_changed_connection;
+ sigc::connection linked_delete_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+ void ref_changed(SPObject *old_ref, SPObject *new_ref);
+ void remove_link();
+ void start_listening(SPObject * to);
+ void quit_listening(void);
+ void linked_delete(SPObject *deleted);
+ void linked_modified(SPObject *linked_obj, guint flags);
+ void linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item);
+ virtual void linked_modified_callback(SPObject *linked_obj, guint flags);
+ virtual void linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/) {};
+ void on_link_button_click();
+
+ void emit_changed();
+
+ gchar * defvalue;
+
+private:
+ ItemParam(const ItemParam&);
+ ItemParam& operator=(const ItemParam&);
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/originalitem.cpp b/src/live_effects/parameter/originalitem.cpp
new file mode 100644
index 000000000..053062128
--- /dev/null
+++ b/src/live_effects/parameter/originalitem.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtkmm/box.h>
+#include "live_effects/parameter/originalitem.h"
+
+#include "widgets/icon.h"
+#include <glibmm/i18n.h>
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "uri.h"
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "display/curve.h"
+#include "live_effects/effect.h"
+
+#include "inkscape.h"
+#include "desktop.h"
+#include "selection.h"
+#include "ui/icon-names.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+OriginalItemParam::OriginalItemParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect)
+ : ItemParam(label, tip, key, wr, effect, "")
+{
+}
+
+OriginalItemParam::~OriginalItemParam()
+{
+
+}
+
+Gtk::Widget *
+OriginalItemParam::param_newWidget()
+{
+ Gtk::HBox *_widget = Gtk::manage(new Gtk::HBox());
+
+ { // Label
+ Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ pLabel->set_tooltip_text(param_tooltip);
+ }
+
+ { // Paste item to link button
+ Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-paste"), Inkscape::ICON_SIZE_BUTTON) );
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_link_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Link to item"));
+ }
+
+ { // Select original button
+ Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon("edit-select-original", Inkscape::ICON_SIZE_BUTTON) );
+ Gtk::Button *pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_select_original_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ pButton->set_tooltip_text(_("Select original"));
+ }
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+OriginalItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/)
+{
+ if (!inverse) {
+ emit_changed();
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+}
+
+void
+OriginalItemParam::linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/)
+{
+/** \todo find good way to compensate for referenced item transform, like done for normal clones.
+ * See sp-use.cpp: sp_use_move_compensate */
+}
+
+
+void
+OriginalItemParam::on_select_original_button_click()
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ SPItem *original = ref.getObject();
+ if (desktop == NULL || original == NULL) {
+ return;
+ }
+ Inkscape::Selection *selection = desktop->getSelection();
+ selection->clear();
+ selection->set(original);
+}
+
+} /* namespace LivePathEffect */
+
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/originalitem.h b/src/live_effects/parameter/originalitem.h
new file mode 100644
index 000000000..58d04e05a
--- /dev/null
+++ b/src/live_effects/parameter/originalitem.h
@@ -0,0 +1,49 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H
+
+/*
+ * Inkscape::LiveItemEffectParameters
+ *
+* Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/item.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalItemParam: public ItemParam {
+public:
+ OriginalItemParam ( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+ virtual ~OriginalItemParam();
+ void setInverse(bool inversed) { inverse = inversed; }
+ bool linksToItem() const { return (href != NULL); }
+ SPItem * getObject() const { return ref.getObject(); }
+
+ virtual Gtk::Widget * param_newWidget();
+
+protected:
+ virtual void linked_modified_callback(SPObject *linked_obj, guint flags);
+ virtual void linked_transformed_callback(Geom::Affine const *rel_transf, SPItem *moved_item);
+
+ void on_select_original_button_click();
+
+private:
+ bool inverse;
+ OriginalItemParam(const OriginalItemParam&);
+ OriginalItemParam& operator=(const OriginalItemParam&);
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp
index f0c494267..dafc6d406 100644
--- a/src/live_effects/parameter/path.cpp
+++ b/src/live_effects/parameter/path.cpp
@@ -5,10 +5,16 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include "ui/widget/point.h"
+#include "live_effects/parameter/path.h"
+
#include <glibmm/i18n.h>
-#include "live_effects/parameter/path.h"
+#include <gtkmm/button.h>
+#include <gtkmm/label.h>
+
+#include "bad-uri-exception.h"
+#include "ui/widget/point.h"
+
#include "live_effects/effect.h"
#include "svg/svg.h"
#include <2geom/svg-path-parser.h>
@@ -17,7 +23,6 @@
#include <2geom/d2.h>
#include "widgets/icon.h"
-#include <gtk/gtk.h>
#include "selection-chemistry.h"
#include "xml/repr.h"
#include "desktop.h"
@@ -44,8 +49,6 @@
#include "ui/tool/multi-path-manipulator.h"
#include "ui/tool/shape-record.h"
-#include <gtkmm/button.h>
-#include <gtkmm/label.h>
#include "ui/icon-names.h"
namespace Inkscape {
diff --git a/src/live_effects/parameter/text.cpp b/src/live_effects/parameter/text.cpp
index 8cab68ad0..5c4cdf4c6 100644
--- a/src/live_effects/parameter/text.cpp
+++ b/src/live_effects/parameter/text.cpp
@@ -125,13 +125,14 @@ TextParam::param_newWidget()
rsu->setProgrammatically = false;
rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change text parameter"));
-
+ param_effect->upd_params = false;
return dynamic_cast<Gtk::Widget *> (rsu);
}
void
TextParam::param_setValue(const Glib::ustring newvalue)
{
+ param_effect->upd_params = true;
value = newvalue;
if (!_hide_canvas_text) {
sp_canvastext_set_text (canvas_text, newvalue.c_str());
diff --git a/src/live_effects/parameter/vector.cpp b/src/live_effects/parameter/vector.cpp
index aa16a2b98..55b4d4b32 100644
--- a/src/live_effects/parameter/vector.cpp
+++ b/src/live_effects/parameter/vector.cpp
@@ -116,7 +116,7 @@ VectorParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point
void
VectorParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
{
- set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() );
+ set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() );
}
diff --git a/src/main.cpp b/src/main.cpp
index 605c1207e..47cf43456 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -166,6 +166,7 @@ enum {
SP_ARG_SHELL,
SP_ARG_VERSION,
SP_ARG_VACUUM_DEFS,
+ SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING,
#ifdef WITH_DBUS
SP_ARG_DBUS_LISTEN,
SP_ARG_DBUS_NAME,
@@ -235,7 +236,6 @@ static gchar *sp_xverbs_yaml_utf8 = NULL;
static gchar *sp_xverbs_yaml = NULL;
#endif // WITH_YAML
-
/**
* Reset variables to default values.
*/
@@ -274,6 +274,7 @@ static void resetCommandlineGlobals() {
sp_query_all = FALSE;
sp_query_id = NULL;
sp_vacuum_defs = FALSE;
+ sp_no_convert_text_baseline_spacing = FALSE;
#ifdef WITH_DBUS
sp_dbus_listen = FALSE;
sp_dbus_name = NULL;
@@ -525,6 +526,11 @@ struct poptOption options[] = {
N_("Start Inkscape in interactive shell mode."),
NULL},
+ {"no-convert-text-baseline-spacing", 0,
+ POPT_ARG_NONE, &sp_no_convert_text_baseline_spacing, SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING,
+ N_("Do not fix legacy (pre-0.92) files' text baseline spacing on opening."),
+ NULL},
+
POPT_AUTOHELP POPT_TABLEEND
};
@@ -1636,7 +1642,6 @@ static int sp_do_export_png(SPDocument *doc)
return retcode;
}
-
/**
* Perform a PDF/PS/EPS export
*
diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h
index 75302806b..39bcf0c20 100644
--- a/src/menus-skeleton.h
+++ b/src/menus-skeleton.h
@@ -184,6 +184,7 @@ static char const menus_skeleton[] =
" <submenu name=\"" N_("_Object") "\">\n"
" <verb verb-id=\"DialogObjects\" />\n"
" <verb verb-id=\"DialogTags\" />\n"
+" <verb verb-id=\"DialogStyle\" />\n"
" <separator/>\n"
" <verb verb-id=\"DialogFillStroke\" />\n"
" <verb verb-id=\"DialogObjectProperties\" />\n"
diff --git a/src/object-set.h b/src/object-set.h
index a89a299bd..7c224f640 100644
--- a/src/object-set.h
+++ b/src/object-set.h
@@ -447,7 +447,7 @@ public:
// various
void getExportHints(Glib::ustring &filename, float *xdpi, float *ydpi);
bool fitCanvas(bool with_margins, bool skip_undo = false);
-
+ void swapFillStroke();
protected:
virtual void _connectSignals(SPObject* object) {};
diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp
index 741f433f2..b66bcf368 100644
--- a/src/path-chemistry.cpp
+++ b/src/path-chemistry.cpp
@@ -498,7 +498,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/)
/* Whole text's style */
Glib::ustring style_str =
- item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility
+ item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility
g_repr->setAttribute("style", style_str.c_str());
Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin();
@@ -519,7 +519,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/)
pos_obj = pos_obj->parent; // SPStrings don't have style
}
Glib::ustring style_str =
- pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility
+ pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility
// get path from iter to iter_next:
SPCurve *curve = te_get_layout(item)->convertToCurves(iter, iter_next);
@@ -579,7 +579,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/)
/* Style */
Glib::ustring style_str =
- item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility
+ item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility
repr->setAttribute("style", style_str.c_str());
/* Mask */
diff --git a/src/print.cpp b/src/print.cpp
index 7cd05ac93..479401995 100644
--- a/src/print.cpp
+++ b/src/print.cpp
@@ -79,6 +79,7 @@ unsigned int sp_print_text(SPPrintContext *ctx, char const *text, Geom::Point p,
void
sp_print_document(Gtk::Window& parentWindow, SPDocument *doc)
{
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
doc->ensureUpToDate();
// Build arena
@@ -88,6 +89,7 @@ sp_print_document(Gtk::Window& parentWindow, SPDocument *doc)
Inkscape::UI::Dialog::Print printop(doc,base);
Gtk::PrintOperationResult res = printop.run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, parentWindow);
(void)res; // TODO handle this
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
}
void sp_print_document_to_file(SPDocument *doc, gchar const *filename)
diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp
index 188b7d9c8..4901cf424 100644
--- a/src/resource-manager.cpp
+++ b/src/resource-manager.cpp
@@ -126,8 +126,22 @@ public:
*/
std::map<Glib::ustring, Glib::ustring> locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks);
+
+ /**
+ * Try to parse href into a local filename using standard methods.
+ *
+ * @return true if successfull.
+ */
bool extractFilepath( Glib::ustring const &href, std::string &uri );
+ /**
+ * Try to parse href into a local filename using some non-standard methods.
+ * This means the href is likely invalid and should be rewritten.
+ *
+ * @return true if successfull.
+ */
+ bool reconstructFilepath( Glib::ustring const &href, std::string &uri );
+
bool searchUpwards( std::string const &base, std::string const &subpath, std::string &dest );
protected:
@@ -156,9 +170,13 @@ bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::strin
// TODO debug g_message("--- is a file URI [%s]", href.c_str());
// throws Glib::ConvertError:
- uri = Glib::filename_from_uri(href); // TODO see if we can get this to throw
- // TODO debug g_message(" [%s]", uri.c_str());
- isFile = true;
+ try {
+ uri = Glib::filename_from_uri(href);
+ // TODO debug g_message(" [%s]", uri.c_str());
+ isFile = true;
+ } catch(Glib::ConvertError e) {
+ g_warning("%s", e.what().c_str());
+ }
}
} else {
// No scheme. Assuming it is a file path (absolute or relative).
@@ -170,6 +188,26 @@ bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::strin
return isFile;
}
+bool ResourceManagerImpl::reconstructFilepath( Glib::ustring const &href, std::string &uri )
+{
+ bool isFile = false;
+
+ uri.clear();
+
+ std::string scheme = Glib::uri_parse_scheme(href);
+ if ( !scheme.empty() ) {
+ if ( scheme == "file" ) {
+ // try to build a relative filename for URIs like "file:image.png"
+ // they're not standard conformant but not uncommon
+ Glib::ustring href_new = Glib::ustring(href, 5);
+ uri = Glib::filename_from_utf8(href_new);
+ // TODO debug g_message("reconstructed path for '%s' into '%s'", href.c_str(), uri.c_str());
+ isFile = true;
+ }
+ }
+ return isFile;
+}
+
std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc )
{
@@ -192,11 +230,14 @@ std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc
}
} else {
std::string combined = Glib::build_filename(doc->getBase(), uri);
- if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
+ if ( !Glib::file_test(combined, Glib::FILE_TEST_EXISTS) ) {
result.push_back(href);
uniques.insert(href);
}
}
+ } else if ( reconstructFilepath( href, uri ) ) {
+ result.push_back(href);
+ uniques.insert(href);
}
}
}
@@ -228,7 +269,7 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us
priorLocations.push_back(path);
}
} catch (Glib::ConvertError e) {
- g_warning("Bad URL ignored [%s]", uri.c_str());
+ g_warning("%s", e.what().c_str());
}
}
}
@@ -238,7 +279,7 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us
// TODO debug g_message("========{%s}", it->c_str());
std::string uri;
- if ( extractFilepath( *it, uri ) ) {
+ if ( extractFilepath( *it, uri ) || reconstructFilepath( *it, uri ) ) {
// We were able to get some path. Check it
std::string origPath = uri;
@@ -247,32 +288,31 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us
// TODO debug g_message(" not absolute. Fixing up as [%s]", uri.c_str());
}
- if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
- // TODO debug g_message(" DOES NOT EXIST.");
- std::string remainder;
- bool exists = searchUpwards( docbase, origPath, remainder );
+ bool exists = Glib::file_test(uri, Glib::FILE_TEST_EXISTS);
- if ( !exists ) {
- // TODO debug g_message("Expanding the search...");
+ // search in parent folders
+ if (!exists) {
+ exists = searchUpwards( docbase, origPath, uri );
+ }
- // Check if the MRU bases point us to it.
- if ( !Glib::path_is_absolute(origPath) ) {
- for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) {
- exists = searchUpwards( *it, origPath, remainder );
- }
+ // Check if the MRU bases point us to it.
+ if ( !exists ) {
+ if ( !Glib::path_is_absolute(origPath) ) {
+ for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) {
+ exists = searchUpwards( *it, origPath, uri );
}
}
+ }
- if ( exists ) {
- if ( Glib::path_is_absolute( remainder ) ) {
- // TODO debug g_message("Need to convert to relative if possible [%s]", remainder.c_str());
- remainder = convertPathToRelative( remainder, docbase );
- }
-
- bool isAbsolute = Glib::path_is_absolute( remainder );
- Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( remainder ) : Glib::filename_to_utf8( remainder );
- result[*it] = replacement;
+ if ( exists ) {
+ if ( Glib::path_is_absolute( uri ) ) {
+ // TODO debug g_message("Need to convert to relative if possible [%s]", uri.c_str());
+ uri = convertPathToRelative( uri, docbase );
}
+
+ bool isAbsolute = Glib::path_is_absolute( uri );
+ Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( uri ) : Glib::filename_to_utf8( uri );
+ result[*it] = replacement;
}
}
}
diff --git a/src/round.h b/src/round.h
deleted file mode 100644
index 16d280566..000000000
--- a/src/round.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef SEEN_ROUND_H
-#define SEEN_ROUND_H
-
-#include <cmath>
-
-namespace Inkscape {
-
-/** Returns x rounded to the nearest integer. It is unspecified what happens
- if x is half way between two integers: we may in future use rint/round
- on platforms that have them. If you depend on a particular rounding
- behaviour, then please change this documentation accordingly.
-**/
-inline double
-round(double const x)
-{
- return std::floor( x + .5 );
-}
-
-}
-
-#endif /* !SEEN_ROUND_H */
-
-/*
- 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/selection-chemistry.cpp b/src/selection-chemistry.cpp
index f8ab33ac4..70307579f 100644
--- a/src/selection-chemistry.cpp
+++ b/src/selection-chemistry.cpp
@@ -97,6 +97,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS;
#include "live_effects/parameter/originalpath.h"
#include "layer-manager.h"
#include "object-set.h"
+#include "svg/svg-color.h"
// For clippath editing
#include "ui/tools/node-tool.h"
@@ -1242,7 +1243,6 @@ void ObjectSet::pasteStyle()
}
}
-
void ObjectSet::pastePathEffect()
{
Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
@@ -3905,6 +3905,7 @@ void ObjectSet::setClipGroup()
|| apply_to_layer){
Geom::Affine oldtr=(*i)->transform;
+ oldtr *= SP_ITEM((*i)->parent)->i2doc_affine().inverse();
(*i)->doWriteTransform((*i)->getRepr(), (*i)->i2doc_affine());
Inkscape::XML::Node *dup = (*i)->getRepr()->duplicate(xml_doc);
(*i)->doWriteTransform((*i)->getRepr(), oldtr);
@@ -4169,6 +4170,75 @@ bool ObjectSet::fitCanvas(bool with_margins, bool skip_undo)
}
}
+void ObjectSet::swapFillStroke()
+{
+ if (desktop() == NULL) {
+ return;
+ }
+
+ SPIPaint *paint;
+ SPPaintServer *server;
+ Glib::ustring _paintserver_id;
+
+ auto list= items();
+ for (auto itemlist=list.begin();itemlist!=list.end();++itemlist) {
+ SPItem *item = *itemlist;
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+
+ _paintserver_id.clear();
+ paint = &(item->style->fill);
+ if (paint->set && paint->isNone())
+ sp_repr_css_set_property (css, "stroke", "none");
+ else if (paint->set && paint->isColor()) {
+ guint32 color = paint->value.color.toRGBA32(SP_SCALE24_TO_FLOAT (item->style->fill_opacity.value));
+ gchar c[64];
+ sp_svg_write_color (c, sizeof(c), color);
+ sp_repr_css_set_property (css, "stroke", c);
+ }
+ else if (!paint->set)
+ sp_repr_css_unset_property (css, "stroke");
+ else if (paint->set && paint->isPaintserver()) {
+ server = SP_STYLE_FILL_SERVER(item->style);
+ if (server) {
+ Inkscape::XML::Node *srepr = server->getRepr();
+ _paintserver_id += "url(#";
+ _paintserver_id += srepr->attribute("id");
+ _paintserver_id += ")";
+ sp_repr_css_set_property (css, "stroke", _paintserver_id.c_str());
+ }
+ }
+
+ _paintserver_id.clear();
+ paint = &(item->style->stroke);
+ if (paint->set && paint->isNone())
+ sp_repr_css_set_property (css, "fill", "none");
+ else if (paint->set && paint->isColor()) {
+ guint32 color = paint->value.color.toRGBA32(SP_SCALE24_TO_FLOAT (item->style->stroke_opacity.value));
+ gchar c[64];
+ sp_svg_write_color (c, sizeof(c), color);
+ sp_repr_css_set_property (css, "fill", c);
+ }
+ else if (!paint->set)
+ sp_repr_css_unset_property (css, "fill");
+ else if (paint->set && paint->isPaintserver()) {
+ server = SP_STYLE_STROKE_SERVER(item->style);
+ if (server) {
+ Inkscape::XML::Node *srepr = server->getRepr();
+ _paintserver_id += "url(#";
+ _paintserver_id += srepr->attribute("id");
+ _paintserver_id += ")";
+ sp_repr_css_set_property (css, "fill", _paintserver_id.c_str());
+ }
+ }
+
+ sp_desktop_apply_css_recursive(item, css, true);
+ sp_repr_css_attr_unref (css);
+ }
+
+ DocumentUndo::done(document(), SP_VERB_EDIT_SWAP_FILL_STROKE,
+ _("Swap fill and stroke of an object"));
+}
/**
* \param with_margins margins defined in the xml under <sodipodi:namedview>
diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp
index 9ce1a3b56..3c3b8abb0 100644
--- a/src/sp-conn-end.cpp
+++ b/src/sp-conn-end.cpp
@@ -1,11 +1,12 @@
+#include "sp-conn-end.h"
#include <cstring>
#include <string>
#include <limits>
+#include "bad-uri-exception.h"
#include "display/curve.h"
#include "xml/repr.h"
-#include "sp-conn-end.h"
#include "sp-path.h"
#include "uri.h"
#include "document.h"
diff --git a/src/sp-filter.cpp b/src/sp-filter.cpp
index 26ea03c6c..052fa7fe2 100644
--- a/src/sp-filter.cpp
+++ b/src/sp-filter.cpp
@@ -17,21 +17,25 @@
#include <config.h>
#endif
+#include "sp-filter.h"
+
#include <map>
#include <string.h>
-using std::map;
-using std::pair;
#include <glibmm.h>
+
+#include "bad-uri-exception.h"
#include "attributes.h"
#include "display/nr-filter.h"
#include "document.h"
-#include "sp-filter.h"
#include "sp-filter-reference.h"
#include "sp-filter-primitive.h"
#include "uri.h"
#include "xml/repr.h"
+using std::map;
+using std::pair;
+
#define SP_MACROS_SILENT
static void filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter);
diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp
index 264558014..d79652bf4 100644
--- a/src/sp-flowtext.cpp
+++ b/src/sp-flowtext.cpp
@@ -535,7 +535,7 @@ Inkscape::XML::Node *SPFlowtext::getAsText()
this->layout.getSourceOfCharacter(it, &rawptr, &span_text_start_iter);
SPObject *source_obj = reinterpret_cast<SPObject *>(rawptr);
- Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, this->style);
+ Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, this->style);
if (!style_text.empty()) {
span_tspan->setAttribute("style", style_text.c_str());
}
diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp
index f02149cbb..c1934dd92 100644
--- a/src/sp-gradient.cpp
+++ b/src/sp-gradient.cpp
@@ -24,6 +24,8 @@
#define noSP_GRADIENT_VERBOSE
//#define OBJECT_TRACE
+#include "sp-gradient.h"
+
#include <cstring>
#include <string>
@@ -34,12 +36,12 @@
#include <sigc++/functors/ptr_fun.h>
#include <sigc++/adaptors/bind.h>
+#include "bad-uri-exception.h"
#include "display/cairo-utils.h"
#include "svg/svg.h"
#include "svg/css-ostringstream.h"
#include "attributes.h"
#include "document-private.h"
-#include "sp-gradient.h"
#include "gradient-chemistry.h"
#include "sp-gradient-reference.h"
#include "sp-linear-gradient.h"
diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp
index 02d95c75c..f1958a53b 100644
--- a/src/sp-hatch.cpp
+++ b/src/sp-hatch.cpp
@@ -12,11 +12,15 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "sp-hatch.h"
+
#include <cstring>
#include <string>
+
#include <2geom/transforms.h>
#include <sigc++/functors/mem_fun.h>
+#include "bad-uri-exception.h"
#include "svg/svg.h"
#include "display/cairo-utils.h"
#include "display/drawing-context.h"
@@ -25,7 +29,6 @@
#include "display/drawing-pattern.h"
#include "attributes.h"
#include "document-private.h"
-#include "sp-hatch.h"
#include "sp-hatch-path.h"
SPHatch::SPHatch()
diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp
index 7b2507b5e..f2c0d2f2c 100644
--- a/src/sp-item-group.cpp
+++ b/src/sp-item-group.cpp
@@ -925,6 +925,15 @@ void SPGroup::update_patheffect(bool write) {
}
sp_group_perform_patheffect(this, this, write);
+
+ for (PathEffectList::iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it)
+ {
+ LivePathEffectObject *lpeobj = (*it)->lpeobject;
+
+ if (lpeobj && lpeobj->get_lpe()) {
+ lpeobj->get_lpe()->doAfterEffect(this);
+ }
+ }
}
}
@@ -950,25 +959,35 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write)
} else {
c = subShape->getCurve();
}
-
+ bool success = false;
// only run LPEs when the shape has a curve defined
if (c) {
c->transform(i2anc_affine(subitem, topgroup));
- topgroup->performPathEffect(c);
+ success = topgroup->performPathEffect(c, subShape);
c->transform(i2anc_affine(subitem, topgroup).inverse());
- subShape->setCurve(c, TRUE);
-
- if (write) {
- Inkscape::XML::Node *repr = subitem->getRepr();
- gchar *str = sp_svg_write_path(c->get_pathvector());
- repr->setAttribute("d", str);
+ Inkscape::XML::Node *repr = subitem->getRepr();
+ if (c && success) {
+ subShape->setCurve(c, TRUE);
+ if (write) {
+ gchar *str = sp_svg_write_path(c->get_pathvector());
+ repr->setAttribute("d", str);
#ifdef GROUP_VERBOSE
- g_message("sp_group_perform_patheffect writes 'd' attribute");
+ g_message("sp_group_perform_patheffect writes 'd' attribute");
#endif
- g_free(str);
+ g_free(str);
+ }
+ c->unref();
+ } else {
+ // LPE was unsuccesfull or doeffect stack return null. Read the old 'd'-attribute.
+ if (gchar const * value = repr->attribute("d")) {
+ Geom::PathVector pv = sp_svg_read_pathv(value);
+ SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
+ if (oldcurve) {
+ subShape->setCurve(oldcurve, TRUE);
+ oldcurve->unref();
+ }
+ }
}
-
- c->unref();
}
}
}
diff --git a/src/sp-item.cpp b/src/sp-item.cpp
index 5d02020c6..e2f678957 100644
--- a/src/sp-item.cpp
+++ b/src/sp-item.cpp
@@ -17,6 +17,10 @@
#endif
#include "sp-item.h"
+
+#include <glibmm/i18n.h>
+
+#include "bad-uri-exception.h"
#include "svg/svg.h"
#include "print.h"
#include "display/drawing-item.h"
@@ -25,10 +29,7 @@
#include "uri.h"
#include "inkscape.h"
#include "desktop.h"
-
-
#include "style.h"
-#include <glibmm/i18n.h>
#include "sp-root.h"
#include "sp-clippath.h"
#include "sp-mask.h"
diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp
index e2f61bfb5..ca0f78a00 100644
--- a/src/sp-lpe-item.cpp
+++ b/src/sp-lpe-item.cpp
@@ -15,15 +15,18 @@
#ifdef HAVE_CONFIG_H
#endif
-#include "ui/tool/multi-path-manipulator.h"
-
#include <glibmm/i18n.h>
+#include "bad-uri-exception.h"
+#include "ui/tool/multi-path-manipulator.h"
+
#include "live_effects/effect.h"
#include "live_effects/lpe-path_length.h"
#include "live_effects/lpeobject.h"
#include "live_effects/lpeobject-reference.h"
#include "live_effects/lpe-measure-line.h"
+#include "live_effects/lpe-mirror_symmetry.h"
+#include "live_effects/lpe-copy_rotate.h"
#include "sp-path.h"
#include "sp-item-group.h"
@@ -126,7 +129,10 @@ void SPLPEItem::set(unsigned int key, gchar const* value) {
if (!value) {
LivePathEffectObject *lpeobj = (*it)->lpeobject;
Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe();
- if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe)){
+ if (dynamic_cast<Inkscape::LivePathEffect::LPEMirrorSymmetry *>(lpe) ||
+ dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe) ||
+ dynamic_cast<Inkscape::LivePathEffect::LPECopyRotate *>(lpe) )
+ {
lpe->doOnRemove(this);
}
}
@@ -209,7 +215,7 @@ Inkscape::XML::Node* SPLPEItem::write(Inkscape::XML::Document *xml_doc, Inkscape
/**
* returns true when LPE was successful.
*/
-bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) {
+bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask) {
if (!curve) {
return false;
@@ -241,12 +247,15 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) {
}
if (!is_clip_or_mask || (is_clip_or_mask && lpe->apply_to_clippath_and_mask)) {
// Groups have their doBeforeEffect called elsewhere
+ if (current) {
+ lpe->setCurrentShape(current);
+ }
if (!SP_IS_GROUP(this)) {
lpe->doBeforeEffect_impl(this);
}
try {
- lpe->doEffect(curve);
+ lpe->doEffect(curve);
}
catch (std::exception & e) {
g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what());
@@ -257,6 +266,8 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) {
return false;
}
if (!SP_IS_GROUP(this)) {
+ lpe->pathvector_before_effect = curve->get_pathvector();
+ lpe->sp_curve->set_pathvector(lpe->pathvector_before_effect);
lpe->doAfterEffect(this);
}
}
@@ -604,7 +615,7 @@ bool SPLPEItem::hasPathEffect() const
return true;
}
-bool SPLPEItem::hasPathEffectOfType(int const type) const
+bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const
{
if (path_effect_list->empty()) {
return false;
@@ -616,7 +627,9 @@ bool SPLPEItem::hasPathEffectOfType(int const type) const
if (lpeobj) {
Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe();
if (lpe && (lpe->effectType() == type)) {
- return true;
+ if (is_ready || lpe->isReady()) {
+ return true;
+ }
}
}
}
@@ -695,10 +708,10 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item)
try {
if(SP_IS_GROUP(this)){
c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)));
- success = this->performPathEffect(c, true);
+ success = this->performPathEffect(c, SP_SHAPE(clip_mask), true);
c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)).inverse());
} else {
- success = this->performPathEffect(c, true);
+ success = this->performPathEffect(c, SP_SHAPE(clip_mask), true);
}
} catch (std::exception & e) {
g_warning("Exception during LPE execution. \n %s", e.what());
@@ -709,12 +722,13 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item)
success = false;
}
Inkscape::XML::Node *repr = clip_mask->getRepr();
- if (success) {
+ // This c check allow to not apply LPE if curve is NULL after performPathEffect used in clone.obgets LPE
+ if (success && c) {
gchar *str = sp_svg_write_path(c->get_pathvector());
repr->setAttribute("d", str);
g_free(str);
} else {
- // LPE was unsuccesfull. Read the old 'd'-attribute.
+ // LPE was unsuccesfull or doeffect stack return null.. Read the old 'd'-attribute.
if (gchar const * value = repr->attribute("d")) {
Geom::PathVector pv = sp_svg_read_pathv(value);
SPCurve *oldcurve = new (std::nothrow) SPCurve(pv);
@@ -724,7 +738,9 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item)
}
}
}
- c->unref();
+ if (c) {
+ c->unref();
+ }
}
}
}
diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h
index 9e5cb3329..0f198c49c 100644
--- a/src/sp-lpe-item.h
+++ b/src/sp-lpe-item.h
@@ -23,6 +23,7 @@
class LivePathEffectObject;
class SPCurve;
+class SPShape;
class SPDesktop;
namespace Inkscape{
@@ -69,11 +70,11 @@ public:
virtual void update_patheffect(bool write);
- bool performPathEffect(SPCurve *curve, bool is_clip_or_mask = false);
+ bool performPathEffect(SPCurve *curve, SPShape *current = NULL, bool is_clip_or_mask = false);
bool pathEffectsEnabled() const;
bool hasPathEffect() const;
- bool hasPathEffectOfType(int const type) const;
+ bool hasPathEffectOfType(int const type, bool is_ready = true) const;
bool hasPathEffectRecursive() const;
Inkscape::LivePathEffect::Effect* getPathEffectOfType(int type);
Inkscape::LivePathEffect::Effect const* getPathEffectOfType(int type) const;
diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp
index b9526433f..d430f6321 100644
--- a/src/sp-namedview.cpp
+++ b/src/sp-namedview.cpp
@@ -19,7 +19,12 @@
#include "event-log.h"
#include <2geom/transforms.h>
+#include "display/sp-canvas-group.h"
+#include "display/canvas-bpath.h"
+#include "display/canvas-temporary-item.h"
+#include "display/canvas-temporary-item-list.h"
#include "display/canvas-grid.h"
+#include "display/curve.h"
#include "util/units.h"
#include "svg/svg-color.h"
#include "xml/repr.h"
@@ -29,12 +34,15 @@
#include "desktop-events.h"
#include "sp-guide.h"
+#include "sp-root.h"
#include "sp-item-group.h"
#include "sp-namedview.h"
#include "preferences.h"
#include "desktop.h"
+#include "selection.h"
+#include "object-set.h"
+#include "inkscape.h"
#include "conn-avoid-ref.h" // for defaultConnSpacing.
-#include "sp-root.h"
#include <gtkmm/window.h>
using Inkscape::DocumentUndo;
@@ -72,6 +80,7 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) {
this->pagecolor = 0;
this->cx = 0;
this->pageshadow = 0;
+ this->document_rotation = 0;
this->window_width = 0;
this->window_height = 0;
this->window_maximized = 0;
@@ -92,9 +101,13 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) {
this->default_layer_id = 0;
this->connector_spacing = defaultConnSpacing;
+ this->page_border_rotated = NULL;
}
SPNamedView::~SPNamedView() {
+ if(!this->getViewList().empty()) { // >0 Desktops
+ this->getViewList()[0]->remove_temporary_canvasitem(this->page_border_rotated);
+ }
}
static void sp_namedview_generate_old_grid(SPNamedView * /*nv*/, SPDocument *document, Inkscape::XML::Node *repr) {
@@ -212,6 +225,7 @@ void SPNamedView::build(SPDocument *document, Inkscape::XML::Node *repr) {
this->readAttr( "inkscape:zoom" );
this->readAttr( "inkscape:cx" );
this->readAttr( "inkscape:cy" );
+ this->readAttr( "inkscape:document-rotation" );
this->readAttr( "inkscape:window-width" );
this->readAttr( "inkscape:window-height" );
this->readAttr( "inkscape:window-x" );
@@ -409,6 +423,11 @@ void SPNamedView::set(unsigned int key, const gchar* value) {
this->cy = value ? g_ascii_strtod(value, NULL) : HUGE_VAL; // HUGE_VAL means not set
this->requestModified(SP_OBJECT_MODIFIED_FLAG);
break;
+ case SP_ATTR_INKSCAPE_DOCUMENT_ROTATION:
+ this->document_rotation = value ? g_ascii_strtod(value, NULL) : 0;
+ sp_namedview_set_document_rotation(this);
+ this->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
case SP_ATTR_INKSCAPE_WINDOW_WIDTH:
this->window_width = value? atoi(value) : -1; // -1 means not set
this->requestModified(SP_OBJECT_MODIFIED_FLAG);
@@ -939,6 +958,81 @@ static void sp_namedview_lock_guides(SPNamedView *nv)
}
}
+//void sp_namedview_doc_rotate_guides(SPNamedView *nv)
+//{
+// bool saved = DocumentUndo::getUndoSensitive(nv->document);
+// DocumentUndo::setUndoSensitive(nv->document, false);
+// SPRoot * root = nv->document->getRoot();
+// Geom::Point page_center = root->viewBox.midpoint() * root->vbt;
+// Geom::Affine rot = Geom::identity();
+// rot *= Geom::Translate(page_center).inverse();
+// rot *= Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1));
+// rot *= Geom::Translate(page_center);
+// for(std::vector<SPGuide *>::iterator it=nv->guides.begin();it!=nv->guides.end();++it ) {
+// Geom::Point const on_line = (*it)->getPoint() * rot ;
+// (*it)->moveto(on_line, true);
+// Geom::Affine rot_normal_affine = Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1));
+// Geom::Point const rot_normal = (*it)->getNormal() * rot_normal_affine;
+// (*it)->set_normal(rot_normal, true);
+// }
+// DocumentUndo::setUndoSensitive(nv->document, saved);
+// nv->document->setModifiedSinceSave();
+//}
+
+void sp_namedview_set_document_rotation(SPNamedView *nv)
+{
+ if ( nv->document->getRoot()->get_rotation() == nv->document_rotation) return;
+ if(!nv->getViewList().empty()) { // >0 Desktops
+ SPDesktop *desktop = nv->getViewList()[0];
+ desktop->remove_temporary_canvasitem(nv->page_border_rotated);
+ SPRoot * root = nv->document->getRoot();
+ SPCurve *c = new SPCurve();
+ c->moveto(root->viewBox.min());
+ c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.min()[Geom::Y]));
+ c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.max()[Geom::Y]));
+ c->lineto(Geom::Point(root->viewBox.min()[Geom::X],root->viewBox.max()[Geom::Y]));
+ c->closepath();
+ Geom::Point page_center = root->viewBox.midpoint();
+ Geom::PathVector const box = c->get_pathvector();
+ Geom::Affine rot = Geom::identity();
+ rot *= Geom::Translate(page_center).inverse();
+ rot *= Geom::Rotate(Geom::rad_from_deg(nv->document_rotation * -1));
+ rot *= Geom::Translate(page_center);
+ if (nv->document_rotation) {
+ SPCanvasItem *canvas_border = sp_canvas_bpath_new(desktop->getTempGroup(), c, true);
+ sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvas_border), 0xFF00009A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
+ sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvas_border), 0, SP_WIND_RULE_NONZERO);
+ sp_canvas_item_affine_absolute(canvas_border, rot * root->vbt);
+ nv->page_border_rotated = desktop->add_temporary_canvasitem(canvas_border, 0);
+ }
+ //sp_namedview_doc_rotate_guides(nv);
+ nv->document->getRoot()->set_rotation(nv->document_rotation);
+ c->unref();
+ }
+ if (nv->document_rotation) {
+ nv->showborder = FALSE;
+ } else {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ nv->showborder = prefs->getBool("/template/base/showborder", 1.0);
+ }
+
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (desktop) {
+//TODO: Remove knots of shapes on selected items
+// Inkscape::Selection * sel = desktop->getSelection();
+// std::vector<SPItem*> il(sel->items().begin(), sel->items().end());
+// for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){
+// SPItem *item = *l;
+// sel->remove(item->getRepr());
+// sel->add(item->getRepr());
+// }
+ SPObject *updated = desktop->getDocument()->getRoot();
+ if (updated) {
+ updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ }
+}
+
static void sp_namedview_show_single_guide(SPGuide* guide, bool show)
{
if (show) {
@@ -953,6 +1047,8 @@ static void sp_namedview_lock_single_guide(SPGuide* guide, bool locked)
guide->set_locked(locked, true);
}
+
+
void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr)
{
unsigned int v;
diff --git a/src/sp-namedview.h b/src/sp-namedview.h
index d8ac1a77e..9d11c13b7 100644
--- a/src/sp-namedview.h
+++ b/src/sp-namedview.h
@@ -21,6 +21,7 @@
#include "snap.h"
#include "document.h"
#include "util/units.h"
+#include "display/sp-canvas.h"
#include <vector>
namespace Inkscape {
@@ -28,6 +29,9 @@ namespace Inkscape {
namespace Util {
class Unit;
}
+ namespace Display {
+ class TemporaryItem;
+ }
}
typedef unsigned int guint32;
@@ -38,7 +42,7 @@ enum {
SP_BORDER_LAYER_TOP
};
-class SPNamedView : public SPObjectGroup {
+class SPNamedView : public SPObjectGroup{
public:
SPNamedView();
virtual ~SPNamedView();
@@ -54,6 +58,7 @@ public:
double zoom;
double cx;
double cy;
+ double document_rotation;
int window_width;
int window_height;
int window_x;
@@ -66,7 +71,7 @@ public:
Inkscape::Util::Unit const *display_units; // Units used for the UI (*not* the same as units of SVG coordinates)
Inkscape::Util::Unit const *page_size_units; // Only used in "Custom size" part of Document Properties dialog
-
+ Inkscape::Display::TemporaryItem *page_border_rotated;
GQuark default_layer_id;
double connector_spacing;
@@ -121,7 +126,7 @@ SPNamedView const *sp_document_namedview(SPDocument const *document, char const
void sp_namedview_window_from_document(SPDesktop *desktop);
void sp_namedview_document_from_window(SPDesktop *desktop);
void sp_namedview_update_layers_from_document (SPDesktop *desktop);
-
+void sp_namedview_set_document_rotation(SPNamedView *nv);
void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr);
void sp_namedview_guides_toggle_lock(SPDocument *doc, Inkscape::XML::Node *repr);
void sp_namedview_show_grids(SPNamedView *namedview, bool show, bool dirty_document);
diff --git a/src/sp-object.cpp b/src/sp-object.cpp
index 75f4657ef..7807703f6 100644
--- a/src/sp-object.cpp
+++ b/src/sp-object.cpp
@@ -775,6 +775,21 @@ void SPObject::appendChild(Inkscape::XML::Node *child) {
repr->appendChild(child);
}
+SPObject* SPObject::nthChild(unsigned index) {
+ g_assert(this->repr);
+ if (hasChildren()) {
+ std::vector<SPObject*> l;
+ unsigned counter = 0;
+ for (auto& child: children) {
+ if (counter == index) {
+ return &child;
+ }
+ counter++;
+ }
+ }
+ return NULL;
+}
+
void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
{
g_assert(this->repr);
@@ -1046,7 +1061,9 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
}
if (style) {
- Glib::ustring s = style->write(SP_STYLE_FLAG_IFSET);
+ // Write if property set by style attribute in this object
+ Glib::ustring s =
+ style->write(SP_STYLE_FLAG_IFSET | SP_STYLE_FLAG_IFSRC, SP_STYLE_SRC_STYLE_PROP);
// Check for valid attributes. This may be time consuming.
// It is useful, though, for debugging Inkscape code.
diff --git a/src/sp-object.h b/src/sp-object.h
index 9abbd324b..d145e966b 100644
--- a/src/sp-object.h
+++ b/src/sp-object.h
@@ -318,6 +318,9 @@ public:
SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); }
SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); }
+ SPObject *nthChild(unsigned index);
+ SPObject const *nthChild(unsigned index) const;
+
enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow };
/**
diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp
index 9e2264d76..e5b1b5d69 100644
--- a/src/sp-offset.cpp
+++ b/src/sp-offset.cpp
@@ -18,13 +18,17 @@
#include <config.h>
#endif
+#include "sp-offset.h"
+
#include <cstring>
#include <string>
+#include <glibmm/i18n.h>
+
+#include "bad-uri-exception.h"
#include "svg/svg.h"
#include "attributes.h"
#include "display/curve.h"
-#include <glibmm/i18n.h>
#include "livarot/Path.h"
#include "livarot/Shape.h"
@@ -32,7 +36,6 @@
#include "enums.h"
#include "preferences.h"
#include "sp-text.h"
-#include "sp-offset.h"
#include "sp-use-reference.h"
#include "uri.h"
diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp
index 9d6296a0d..377c035fc 100644
--- a/src/sp-pattern.cpp
+++ b/src/sp-pattern.cpp
@@ -16,11 +16,14 @@
#include <config.h>
#endif
+#include "sp-pattern.h"
+
#include <cstring>
#include <string>
#include <glibmm.h>
#include <2geom/transforms.h>
+#include "bad-uri-exception.h"
#include "svg/svg.h"
#include "display/cairo-utils.h"
#include "display/drawing-context.h"
@@ -29,7 +32,6 @@
#include "display/drawing-group.h"
#include "attributes.h"
#include "document-private.h"
-#include "sp-pattern.h"
#include "sp-factory.h"
diff --git a/src/sp-style-elem.cpp b/src/sp-style-elem.cpp
index 80e45677c..c15f810e9 100644
--- a/src/sp-style-elem.cpp
+++ b/src/sp-style-elem.cpp
@@ -3,6 +3,7 @@
#include "xml/repr.h"
#include "document.h"
#include "sp-style-elem.h"
+#include "sp-root.h"
#include "attributes.h"
#include "style.h"
using Inkscape::XML::TEXT_NODE;
@@ -64,6 +65,7 @@ content_changed_cb(Inkscape::XML::Node *, gchar const *, gchar const *,
SPObject *obj = reinterpret_cast<SPObject *>(data);
g_assert(data != NULL);
obj->read_content();
+ obj->document->getRoot()->emitModified( SP_OBJECT_MODIFIED_CASCADE );
}
static void
@@ -249,6 +251,19 @@ property_cb(CRDocHandler *const a_handler,
g_return_if_fail(append_status == CR_OK);
}
+void update_style_recursively( SPObject *object ) {
+ if (object) {
+ // std::cout << "update_style_recursively: "
+ // << (object->getId()?object->getId():"null") << std::endl;
+ if (object->style) {
+ object->style->readFromObject( object );
+ }
+ for (auto& child : object->children) {
+ update_style_recursively( &child );
+ }
+ }
+}
+
void SPStyleElem::read_content() {
/* fixme: If there's more than one <style> element in a document, then the document stylesheet
* will be set to a random one of them, even switching between them.
@@ -315,9 +330,13 @@ void SPStyleElem::read_content() {
// the <style> is a child of the object that uses a style from it. It just forces the parent of
// <style> to reread its style as soon as the stylesheet is fully loaded. Naturally, this won't
// work if the user of the stylesheet is its grandparent or precedent.
- if ( parent ) {
- parent->style->readFromObject( parent );
- }
+ // if ( parent ) {
+ // parent->style->readFromObject( parent );
+ // }
+
+ // If style sheet has changed, we need to cascade the entire object tree, top down
+ // Get root, read style, loop through children
+ update_style_recursively( (SPObject *)document->getRoot() );
}
/**
diff --git a/src/sp-tag-use-reference.cpp b/src/sp-tag-use-reference.cpp
index cca24ed85..bb03c120a 100644
--- a/src/sp-tag-use-reference.cpp
+++ b/src/sp-tag-use-reference.cpp
@@ -6,11 +6,12 @@
* Released under GNU GPL, read the file 'COPYING' for more information.
*/
+#include "sp-tag-use-reference.h"
+
#include <cstring>
#include <string>
-#include "sp-tag-use-reference.h"
-
+#include "bad-uri-exception.h"
#include "livarot/Path.h"
#include "preferences.h"
#include "sp-shape.h"
diff --git a/src/sp-tag-use.cpp b/src/sp-tag-use.cpp
index 935f7429e..1312b923f 100644
--- a/src/sp-tag-use.cpp
+++ b/src/sp-tag-use.cpp
@@ -10,10 +10,14 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "sp-tag-use.h"
+
#include <cstring>
#include <string>
#include <glibmm/i18n.h>
+
+#include "bad-uri-exception.h"
#include "display/drawing-group.h"
#include "attributes.h"
#include "document.h"
@@ -23,7 +27,6 @@
#include "style.h"
#include "sp-factory.h"
#include "sp-symbol.h"
-#include "sp-tag-use.h"
#include "sp-tag-use-reference.h"
SPTagUse::SPTagUse()
diff --git a/src/sp-tag-use.h b/src/sp-tag-use.h
index 3f238d654..651c8f045 100644
--- a/src/sp-tag-use.h
+++ b/src/sp-tag-use.h
@@ -22,6 +22,7 @@
#define SP_TAG_USE(obj) (dynamic_cast<SPTagUse*> (obj))
#define SP_IS_TAG_USE(obj) (dynamic_cast<SPTagUse*> (obj) != NULL)
+class SPItem;
class SPTagUse;
class SPTagUseReference;
diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp
index e25ddb5a4..014876478 100644
--- a/src/sp-tref.cpp
+++ b/src/sp-tref.cpp
@@ -16,13 +16,15 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "sp-tref.h"
+
#include <glibmm/i18n.h>
+#include "bad-uri-exception.h"
#include "attributes.h"
#include "document.h"
#include "sp-factory.h"
#include "sp-text.h"
-#include "sp-tref.h"
#include "style.h"
#include "text-editing.h"
diff --git a/src/sp-use-reference.cpp b/src/sp-use-reference.cpp
index 3dd63df40..ea8bc5e86 100644
--- a/src/sp-use-reference.cpp
+++ b/src/sp-use-reference.cpp
@@ -7,11 +7,13 @@
* Released under GNU GPL, read the file 'COPYING' for more information.
*/
+#include "sp-use-reference.h"
+
#include <cstring>
#include <string>
+#include "bad-uri-exception.h"
#include "enums.h"
-#include "sp-use-reference.h"
#include "display/curve.h"
#include "livarot/Path.h"
diff --git a/src/sp-use.cpp b/src/sp-use.cpp
index 6ba03dad9..648852961 100644
--- a/src/sp-use.cpp
+++ b/src/sp-use.cpp
@@ -19,6 +19,8 @@
#include <2geom/transforms.h>
#include <glibmm/i18n.h>
#include <glibmm/markup.h>
+
+#include "bad-uri-exception.h"
#include "display/drawing-group.h"
#include "attributes.h"
#include "document.h"
diff --git a/src/style-internal.cpp b/src/style-internal.cpp
index 5a2a2f913..54d1a0867 100644
--- a/src/style-internal.cpp
+++ b/src/style-internal.cpp
@@ -27,6 +27,10 @@
#endif
#include "style-internal.h"
+
+#include <glibmm/regex.h>
+
+#include "bad-uri-exception.h"
#include "style.h"
#include "svg/svg.h"
@@ -40,8 +44,6 @@
#include "svg/css-ostringstream.h"
#include "util/units.h"
-#include <glibmm/regex.h>
-
// TODO REMOVE OR MAKE MEMBER FUNCTIONS
void sp_style_fill_paint_server_ref_changed( SPObject *old_ref, SPObject *ref, SPStyle *style);
void sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style);
@@ -53,6 +55,20 @@ using Inkscape::CSSOStringStream;
// SPIBase --------------------------------------------------------------
+// Standard criteria for writing a property
+// dfp == different from parent
+inline bool should_write( guint const flags, bool set, bool dfp, bool src) {
+
+ bool should_write = false;
+ if ( ((flags & SP_STYLE_FLAG_ALWAYS)) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && set && src) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && set && src && dfp)) {
+ should_write = true;
+ }
+ return should_write;
+}
+
+
// SPIFloat -------------------------------------------------------------
@@ -75,14 +91,12 @@ SPIFloat::read( gchar const *str ) {
}
const Glib::ustring
-SPIFloat::write( guint const flags, SPIBase const *const base) const {
+SPIFloat::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIFloat const *const my_base = dynamic_cast<const SPIFloat*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -151,14 +165,12 @@ SPIScale24::read( gchar const *str ) {
}
const Glib::ustring
-SPIScale24::write( guint const flags, SPIBase const *const base) const {
+SPIScale24::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIScale24 const *const my_base = dynamic_cast<const SPIScale24*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -303,14 +315,12 @@ SPILength::read( gchar const *str ) {
}
const Glib::ustring
-SPILength::write( guint const flags, SPIBase const *const base) const {
+SPILength::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -460,18 +470,16 @@ SPILengthOrNormal::read( gchar const *str ) {
};
const Glib::ustring
-SPILengthOrNormal::write( guint const flags, SPIBase const *const base) const {
+SPILengthOrNormal::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->normal) {
return (name + ":normal;");
} else {
- return SPILength::write(flags, base);
+ return SPILength::write(flags, style_src_req, base);
}
}
return Glib::ustring("");
@@ -547,14 +555,12 @@ SPIEnum::read( gchar const *str ) {
}
const Glib::ustring
-SPIEnum::write( guint const flags, SPIBase const *const base) const {
+SPIEnum::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -686,14 +692,12 @@ SPIEnumBits::read( gchar const *str ) {
}
const Glib::ustring
-SPIEnumBits::write( guint const flags, SPIBase const *const base) const {
+SPIEnumBits::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -757,14 +761,12 @@ SPILigatures::read( gchar const *str ) {
}
const Glib::ustring
-SPILigatures::write( guint const flags, SPIBase const *const base) const {
+SPILigatures::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -858,14 +860,12 @@ SPINumeric::read( gchar const *str ) {
}
const Glib::ustring
-SPINumeric::write( guint const flags, SPIBase const *const base) const {
+SPINumeric::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
}
@@ -929,14 +929,12 @@ SPIString::read( gchar const *str ) {
// This routine is actually rarely used. Writing is done usually
// in sp_repr_css_write_string...
const Glib::ustring
-SPIString::write( guint const flags, SPIBase const *const base) const {
+SPIString::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIString const *const my_base = dynamic_cast<const SPIString*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this) ); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else {
@@ -1040,14 +1038,12 @@ void SPIColor::read( gchar const *str ) {
}
const Glib::ustring
-SPIColor::write( guint const flags, SPIBase const *const base) const {
+SPIColor::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIColor const *const my_base = dynamic_cast<const SPIColor*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->currentcolor) {
@@ -1269,14 +1265,12 @@ SPIPaint::read( gchar const *str, SPStyle &style_in, SPDocument *document_in ) {
}
const Glib::ustring
-SPIPaint::write( guint const flags, SPIBase const *const base) const {
+SPIPaint::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIPaint const *const my_base = dynamic_cast<const SPIPaint*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -1541,14 +1535,12 @@ SPIPaintOrder::read( gchar const *str ) {
}
const Glib::ustring
-SPIPaintOrder::write( guint const flags, SPIBase const *const base) const {
+SPIPaintOrder::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIPaintOrder const *const my_base = dynamic_cast<const SPIPaintOrder*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -1689,14 +1681,14 @@ SPIFilter::read( gchar const *str ) {
}
}
-const Glib::ustring SPIFilter::write( guint const flags, SPIBase const *const /*base*/) const
+const Glib::ustring SPIFilter::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const /*base*/) const
{
// TODO: fix base
//SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set))
- {
+ // bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool dfp = true;
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else if(this->href && this->href->getURI()) {
@@ -1828,14 +1820,12 @@ SPIDashArray::read( gchar const *str ) {
}
const Glib::ustring
-SPIDashArray::write( guint const flags, SPIBase const *const base) const {
+SPIDashArray::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIDashArray const *const my_base = dynamic_cast<const SPIDashArray*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
if (this->inherit) {
return (name + ":inherit;");
} else if (this->values.empty() ) {
@@ -1945,14 +1935,12 @@ SPIFontSize::read( gchar const *str ) {
}
const Glib::ustring
-SPIFontSize::write( guint const flags, SPIBase const *const base) const {
+SPIFontSize::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIFontSize const *const my_base = dynamic_cast<const SPIFontSize*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -2239,7 +2227,7 @@ SPIFont::read( gchar const *str ) {
}
}
-const Glib::ustring SPIFont::write( guint const /*flags*/, SPIBase const *const /*base*/) const
+const Glib::ustring SPIFont::write( guint const /*flags*/, SPStyleSrc const & /*style_src_req*/, SPIBase const *const /*base*/) const
{
// At the moment, do nothing. We could add a preference to write out
// 'font' shorthand rather than longhand properties.
@@ -2316,14 +2304,12 @@ SPIBaselineShift::read( gchar const *str ) {
}
const Glib::ustring
-SPIBaselineShift::write( guint const flags, SPIBase const *const base) const {
+SPIBaselineShift::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPIBaselineShift const *const my_base = dynamic_cast<const SPIBaselineShift*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || !this->isZero() )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
CSSOStringStream css;
if (this->inherit) {
@@ -2507,13 +2493,11 @@ SPITextDecorationLine::read( gchar const *str ) {
}
const Glib::ustring
-SPITextDecorationLine::write( guint const flags, SPIBase const *const base) const {
+SPITextDecorationLine::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPITextDecorationLine const *const my_base = dynamic_cast<const SPITextDecorationLine*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
Inkscape::CSSOStringStream os;
os << name << ":";
if( inherit ) {
@@ -2639,13 +2623,11 @@ SPITextDecorationStyle::read( gchar const *str ) {
}
const Glib::ustring
-SPITextDecorationStyle::write( guint const flags, SPIBase const *const base) const {
+SPITextDecorationStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPITextDecorationStyle const *const my_base = dynamic_cast<const SPITextDecorationStyle*>(base);
- if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
- ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
- ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
- && (!my_base->set || this != my_base )))
- {
+ bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent
+ bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC));
+ if (should_write(flags, set, dfp, src)) {
Inkscape::CSSOStringStream os;
os << name << ":";
if( inherit ) {
@@ -2795,7 +2777,7 @@ SPITextDecoration::read( gchar const *str ) {
// Returns CSS2 'text-decoration' (using settings in SPTextDecorationLine)
// This is required until all SVG renderers support CSS3 'text-decoration'
const Glib::ustring
-SPITextDecoration::write( guint const flags, SPIBase const *const base) const {
+SPITextDecoration::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const {
SPITextDecoration const *const my_base = dynamic_cast<const SPITextDecoration*>(base);
if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
((flags & SP_STYLE_FLAG_IFSET) && style->text_decoration_line.set) ||
diff --git a/src/style-internal.h b/src/style-internal.h
index 6cc8c6c4e..69b03f503 100644
--- a/src/style-internal.h
+++ b/src/style-internal.h
@@ -35,12 +35,13 @@ struct SPStyleEnum;
static const unsigned SP_STYLE_FLAG_ALWAYS (1 << 2);
static const unsigned SP_STYLE_FLAG_IFSET (1 << 0);
static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1);
+static const unsigned SP_STYLE_FLAG_IFSRC (1 << 3); // If source matches
enum SPStyleSrc {
SP_STYLE_SRC_UNSET,
- SP_STYLE_SRC_STYLE_PROP,
- SP_STYLE_SRC_STYLE_SHEET,
- SP_STYLE_SRC_ATTRIBUTE
+ SP_STYLE_SRC_ATTRIBUTE, // fill="red"
+ SP_STYLE_SRC_STYLE_PROP, // style="fill:red"
+ SP_STYLE_SRC_STYLE_SHEET, // .red { fill:red; }
};
/* General comments:
@@ -77,6 +78,7 @@ enum SPStyleSrc {
* IFSET: Write a property if 'set' flag is true, otherwise return empty string.
* IFDIFF: Write a property if computed values are different, otherwise return empty string,
* This is only used for text!!
+ * IFSRC Write a property if the source matches the requested source (style sheet, etc.).
*
* read(): Set a property value from a string.
* clear(): Set a property to its default value and set the 'set' flag to false.
@@ -127,17 +129,19 @@ public:
{}
virtual void read( gchar const *str ) = 0;
- virtual void readIfUnset( gchar const *str ) {
+ virtual void readIfUnset( gchar const *str, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ) {
if ( !set ) {
read( str );
+ style_src = source;
}
}
virtual void readAttribute( Inkscape::XML::Node *repr ) {
- readIfUnset( repr->attribute( name.c_str() ) );
+ readIfUnset( repr->attribute( name.c_str() ), SP_STYLE_SRC_ATTRIBUTE );
}
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const = 0;
virtual void clear() {
set = false, inherit = false;
@@ -176,7 +180,7 @@ public:
unsigned inherits : 1; // Property inherits by default from parent.
unsigned set : 1; // Property has been explicitly set (vs. inherited).
unsigned inherit : 1; // Property value set to 'inherit'.
- SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME
+ SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet).
// To do: make private after g_asserts removed
public:
@@ -202,6 +206,7 @@ public:
virtual ~SPIFloat() {}
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -283,6 +288,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -355,6 +361,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -411,6 +418,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPILength::clear();
@@ -474,6 +482,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -528,6 +537,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
};
@@ -554,6 +564,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
};
@@ -577,6 +588,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
};
@@ -606,6 +618,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear(); // TODO check about value and value_default
virtual void cascade( const SPIBase* const parent );
@@ -653,6 +666,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -734,6 +748,7 @@ public:
virtual void read( gchar const *str );
virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0);
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear();
virtual void reset( bool init ); // Used internally when reading or cascading
@@ -833,6 +848,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -885,6 +901,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -924,6 +941,7 @@ public:
virtual ~SPIFilter();
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear();
virtual void cascade( const SPIBase* const parent );
@@ -968,6 +986,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1024,6 +1043,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1068,6 +1088,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1124,6 +1145,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1171,6 +1193,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
@@ -1227,6 +1250,7 @@ public:
virtual void read( gchar const *str );
virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPIBase const *const base = NULL ) const;
virtual void clear() {
SPIBase::clear();
diff --git a/src/style.cpp b/src/style.cpp
index c513b735d..0b0358bb2 100644
--- a/src/style.cpp
+++ b/src/style.cpp
@@ -23,10 +23,15 @@
#include <config.h>
#endif
+#include "style.h"
+
#include <cstring>
#include <string>
#include <algorithm>
+#include <glibmm/regex.h>
+
+#include "bad-uri-exception.h"
#include "libcroco/cr-sel-eng.h"
#include "xml/croco-node-iface.h"
@@ -38,14 +43,11 @@
#include "uri-references.h"
#include "uri.h"
#include "sp-paint-server.h"
-#include "style.h"
#include "svg/css-ostringstream.h"
#include "xml/simple-document.h"
#include "util/units.h"
#include "preferences.h"
-#include <glibmm/regex.h>
-
using Inkscape::CSSOStringStream;
using std::vector;
@@ -678,22 +680,22 @@ SPStyle::readFromPrefs(Glib::ustring const &path) {
// Matches sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
void
-SPStyle::readIfUnset( gint id, gchar const *val ) {
+SPStyle::readIfUnset( gint id, gchar const *val, SPStyleSrc const &source ) {
- // std::cout << "SPStyle::readIfUnset: Entrance: " << (val?val:"null") << std::endl;
+ // std::cout << "SPStyle::readIfUnset: Entrance: " << id << ": " << (val?val:"null") << std::endl;
// To Do: If it is not too slow, use std::map instead of std::vector inorder to remove switch()
// (looking up SP_PROP_xxxx already uses a hash).
g_return_if_fail(val != NULL);
switch (id) {
case SP_PROP_INKSCAPE_FONT_SPEC:
- font_specification.readIfUnset( val );
+ font_specification.readIfUnset( val, source );
break;
case SP_PROP_FONT_FAMILY:
- font_family.readIfUnset( val );
+ font_family.readIfUnset( val, source );
break;
case SP_PROP_FONT_SIZE:
- font_size.readIfUnset( val );
+ font_size.readIfUnset( val, source );
break;
case SP_PROP_FONT_SIZE_ADJUST:
if (strcmp(val, "none") != 0) {
@@ -701,105 +703,105 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
}
break;
case SP_PROP_FONT_STYLE:
- font_style.readIfUnset( val );
+ font_style.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT:
- font_variant.readIfUnset( val );
+ font_variant.readIfUnset( val, source );
break;
case SP_PROP_FONT_WEIGHT:
- font_weight.readIfUnset( val );
+ font_weight.readIfUnset( val, source );
break;
case SP_PROP_FONT_STRETCH:
- font_stretch.readIfUnset( val );
+ font_stretch.readIfUnset( val, source );
break;
case SP_PROP_FONT:
- font.readIfUnset( val );
+ font.readIfUnset( val, source );
break;
/* Font Variants CSS 3 */
case SP_PROP_FONT_VARIANT_LIGATURES:
- font_variant_ligatures.readIfUnset( val );
+ font_variant_ligatures.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_POSITION:
- font_variant_position.readIfUnset( val );
+ font_variant_position.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_CAPS:
- font_variant_caps.readIfUnset( val );
+ font_variant_caps.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_NUMERIC:
- font_variant_numeric.readIfUnset( val );
+ font_variant_numeric.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_ALTERNATES:
- font_variant_alternates.readIfUnset( val );
+ font_variant_alternates.readIfUnset( val, source );
break;
case SP_PROP_FONT_VARIANT_EAST_ASIAN:
- font_variant_east_asian.readIfUnset( val );
+ font_variant_east_asian.readIfUnset( val, source );
break;
case SP_PROP_FONT_FEATURE_SETTINGS:
- font_feature_settings.readIfUnset( val );
+ font_feature_settings.readIfUnset( val, source );
break;
/* Text */
case SP_PROP_TEXT_INDENT:
- text_indent.readIfUnset( val );
+ text_indent.readIfUnset( val, source );
break;
case SP_PROP_TEXT_ALIGN:
- text_align.readIfUnset( val );
+ text_align.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION:
- text_decoration.readIfUnset( val );
+ text_decoration.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION_LINE:
- text_decoration_line.readIfUnset( val );
+ text_decoration_line.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION_STYLE:
- text_decoration_style.readIfUnset( val );
+ text_decoration_style.readIfUnset( val, source );
break;
case SP_PROP_TEXT_DECORATION_COLOR:
- text_decoration_color.readIfUnset( val );
+ text_decoration_color.readIfUnset( val, source );
break;
case SP_PROP_LINE_HEIGHT:
- line_height.readIfUnset( val );
+ line_height.readIfUnset( val, source );
break;
case SP_PROP_LETTER_SPACING:
- letter_spacing.readIfUnset( val );
+ letter_spacing.readIfUnset( val, source );
break;
case SP_PROP_WORD_SPACING:
- word_spacing.readIfUnset( val );
+ word_spacing.readIfUnset( val, source );
break;
case SP_PROP_TEXT_TRANSFORM:
- text_transform.readIfUnset( val );
+ text_transform.readIfUnset( val, source );
break;
/* Text (css3) */
case SP_PROP_DIRECTION:
- direction.readIfUnset( val );
+ direction.readIfUnset( val, source );
break;
case SP_PROP_WRITING_MODE:
- writing_mode.readIfUnset( val );
+ writing_mode.readIfUnset( val, source );
break;
case SP_PROP_TEXT_ORIENTATION:
- text_orientation.readIfUnset( val );
+ text_orientation.readIfUnset( val, source );
break;
case SP_PROP_TEXT_ANCHOR:
- text_anchor.readIfUnset( val );
+ text_anchor.readIfUnset( val, source );
break;
case SP_PROP_WHITE_SPACE:
- white_space.readIfUnset( val );
+ white_space.readIfUnset( val, source );
break;
case SP_PROP_SHAPE_INSIDE:
- shape_inside.readIfUnset( val );
+ shape_inside.readIfUnset( val, source );
break;
case SP_PROP_SHAPE_PADDING:
- shape_padding.readIfUnset( val );
+ shape_padding.readIfUnset( val, source );
break;
case SP_PROP_DOMINANT_BASELINE:
- dominant_baseline.readIfUnset( val );
+ dominant_baseline.readIfUnset( val, source );
break;
case SP_PROP_BASELINE_SHIFT:
- baseline_shift.readIfUnset( val );
+ baseline_shift.readIfUnset( val, source );
break;
case SP_PROP_TEXT_RENDERING:
- text_rendering.readIfUnset( val );
+ text_rendering.readIfUnset( val, source );
break;
case SP_PROP_ALIGNMENT_BASELINE:
g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val);
@@ -818,25 +820,25 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
g_warning("Unimplemented style property SP_PROP_CLIP: value: %s", val);
break;
case SP_PROP_COLOR:
- color.readIfUnset( val );
+ color.readIfUnset( val, source );
break;
case SP_PROP_CURSOR:
g_warning("Unimplemented style property SP_PROP_CURSOR: value: %s", val);
break;
case SP_PROP_DISPLAY:
- display.readIfUnset( val );
+ display.readIfUnset( val, source );
break;
case SP_PROP_OVERFLOW:
- overflow.readIfUnset( val );
+ overflow.readIfUnset( val, source );
break;
case SP_PROP_VISIBILITY:
- visibility.readIfUnset( val );
+ visibility.readIfUnset( val, source );
break;
case SP_PROP_ISOLATION:
- isolation.readIfUnset( val );
+ isolation.readIfUnset( val, source );
break;
case SP_PROP_MIX_BLEND_MODE:
- mix_blend_mode.readIfUnset( val );
+ mix_blend_mode.readIfUnset( val, source );
break;
/* SVG */
@@ -854,7 +856,7 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
this->object->getRepr()->setAttribute("clip-path", val);
break;
case SP_PROP_CLIP_RULE:
- clip_rule.readIfUnset( val );
+ clip_rule.readIfUnset( val, source );
break;
case SP_PROP_MASK:
/** \todo
@@ -866,14 +868,14 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
this->object->getRepr()->setAttribute("mask", val);
break;
case SP_PROP_OPACITY:
- opacity.readIfUnset( val );
+ opacity.readIfUnset( val, source );
break;
case SP_PROP_ENABLE_BACKGROUND:
- enable_background.readIfUnset( val );
+ enable_background.readIfUnset( val, source );
break;
/* Filter */
case SP_PROP_FILTER:
- if( !filter.inherit ) filter.readIfUnset( val );
+ if( !filter.inherit ) filter.readIfUnset( val, source );
break;
case SP_PROP_FLOOD_COLOR:
g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val);
@@ -898,86 +900,86 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
/* Paint */
case SP_PROP_COLOR_INTERPOLATION:
// We read it but issue warning
- color_interpolation.readIfUnset( val );
+ color_interpolation.readIfUnset( val, source );
if( color_interpolation.value != SP_CSS_COLOR_INTERPOLATION_SRGB ) {
g_warning("Inkscape currently only supports color-interpolation = sRGB");
}
break;
case SP_PROP_COLOR_INTERPOLATION_FILTERS:
- color_interpolation_filters.readIfUnset( val );
+ color_interpolation_filters.readIfUnset( val, source );
break;
case SP_PROP_COLOR_PROFILE:
g_warning("Unimplemented style property SP_PROP_COLOR_PROFILE: value: %s", val);
break;
case SP_PROP_COLOR_RENDERING:
- color_rendering.readIfUnset( val );
+ color_rendering.readIfUnset( val, source );
break;
case SP_PROP_SOLID_COLOR:
- solid_color.readIfUnset( val );
+ solid_color.readIfUnset( val, source );
break;
case SP_PROP_SOLID_OPACITY:
- solid_opacity.readIfUnset( val );
+ solid_opacity.readIfUnset( val, source );
break;
case SP_PROP_VECTOR_EFFECT:
vector_effect.readIfUnset( val );
break;
case SP_PROP_FILL:
- fill.readIfUnset( val );
+ fill.readIfUnset( val, source );
break;
case SP_PROP_FILL_OPACITY:
- fill_opacity.readIfUnset( val );
+ fill_opacity.readIfUnset( val, source );
break;
case SP_PROP_FILL_RULE:
- fill_rule.readIfUnset( val );
+ fill_rule.readIfUnset( val, source );
break;
case SP_PROP_IMAGE_RENDERING:
- image_rendering.readIfUnset( val );
+ image_rendering.readIfUnset( val, source );
break;
case SP_PROP_MARKER:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker.readIfUnset( val );
+ marker.readIfUnset( val, source );
break;
case SP_PROP_MARKER_START:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker_start.readIfUnset( val );
+ marker_start.readIfUnset( val, source );
break;
case SP_PROP_MARKER_MID:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker_mid.readIfUnset( val );
+ marker_mid.readIfUnset( val, source );
break;
case SP_PROP_MARKER_END:
/* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
- marker_end.readIfUnset( val );
+ marker_end.readIfUnset( val, source );
break;
case SP_PROP_SHAPE_RENDERING:
- shape_rendering.readIfUnset( val );
+ shape_rendering.readIfUnset( val, source );
break;
case SP_PROP_STROKE:
- stroke.readIfUnset( val );
+ stroke.readIfUnset( val, source );
break;
case SP_PROP_STROKE_WIDTH:
- stroke_width.readIfUnset( val );
+ stroke_width.readIfUnset( val, source );
break;
case SP_PROP_STROKE_DASHARRAY:
- stroke_dasharray.readIfUnset( val );
+ stroke_dasharray.readIfUnset( val, source );
break;
case SP_PROP_STROKE_DASHOFFSET:
- stroke_dashoffset.readIfUnset( val );
+ stroke_dashoffset.readIfUnset( val, source );
break;
case SP_PROP_STROKE_LINECAP:
- stroke_linecap.readIfUnset( val );
+ stroke_linecap.readIfUnset( val, source );
break;
case SP_PROP_STROKE_LINEJOIN:
- stroke_linejoin.readIfUnset( val );
+ stroke_linejoin.readIfUnset( val, source );
break;
case SP_PROP_STROKE_MITERLIMIT:
- stroke_miterlimit.readIfUnset( val );
+ stroke_miterlimit.readIfUnset( val, source );
break;
case SP_PROP_STROKE_OPACITY:
- stroke_opacity.readIfUnset( val );
+ stroke_opacity.readIfUnset( val, source );
break;
case SP_PROP_PAINT_ORDER:
- paint_order.readIfUnset( val );
+ paint_order.readIfUnset( val, source );
break;
default:
g_warning("SPIStyle::readIfUnset(): Invalid style property id: %d value: %s", id, val);
@@ -999,16 +1001,16 @@ SPStyle::readIfUnset( gint id, gchar const *val ) {
* \post ret != NULL.
*/
Glib::ustring
-SPStyle::write( guint const flags, SPStyle const *const base ) const {
+SPStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPStyle const *const base ) const {
- // std::cout << "SPStyle::write" << std::endl;
+ // std::cout << "SPStyle::write: flags: " << flags << std::endl;
Glib::ustring style_string;
for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) {
if( base != NULL ) {
- style_string += _properties[i]->write( flags, base->_properties[i] );
+ style_string += _properties[i]->write( flags, style_src_req, base->_properties[i] );
} else {
- style_string += _properties[i]->write( flags, NULL );
+ style_string += _properties[i]->write( flags, style_src_req, NULL );
}
}
// for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) {
@@ -1112,13 +1114,13 @@ SPStyle::_mergeString( gchar const *const p ) {
CRDeclaration *const decl_list
= cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8);
if (decl_list) {
- _mergeDeclList( decl_list );
+ _mergeDeclList( decl_list, SP_STYLE_SRC_STYLE_PROP );
cr_declaration_destroy(decl_list);
}
}
void
-SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) {
+SPStyle::_mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source ) {
// std::cout << "SPStyle::_mergeDeclList" << std::endl;
@@ -1126,13 +1128,13 @@ SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) {
// (Properties are only set if not previously set. See:
// Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.)
if (decl_list->next) {
- _mergeDeclList( decl_list->next );
+ _mergeDeclList( decl_list->next, source );
}
- _mergeDecl( decl_list );
+ _mergeDecl( decl_list, source );
}
void
-SPStyle::_mergeDecl( CRDeclaration const *const decl ) {
+SPStyle::_mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source ) {
// std::cout << "SPStyle::_mergeDecl" << std::endl;
@@ -1145,7 +1147,7 @@ SPStyle::_mergeDecl( CRDeclaration const *const decl ) {
*/
guchar *const str_value_unsigned = cr_term_to_string(decl->value);
gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned);
- readIfUnset( prop_idx, str_value );
+ readIfUnset( prop_idx, str_value, source );
g_free(str_value);
}
}
@@ -1160,7 +1162,7 @@ SPStyle::_mergeProps( CRPropList *const props ) {
_mergeProps( cr_prop_list_get_next( props ) );
CRDeclaration *decl = NULL;
cr_prop_list_get_decl(props, &decl);
- _mergeDecl( decl );
+ _mergeDecl( decl, SP_STYLE_SRC_STYLE_SHEET );
}
}
diff --git a/src/style.h b/src/style.h
index c8192f782..00c8c032a 100644
--- a/src/style.h
+++ b/src/style.h
@@ -50,8 +50,9 @@ public:
void read(SPObject *object, Inkscape::XML::Node *repr);
void readFromObject(SPObject *object);
void readFromPrefs(Glib::ustring const &path);
- void readIfUnset( int id, char const *val );
+ void readIfUnset( int id, char const *val, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP );
Glib::ustring write( unsigned int const flags = SP_STYLE_FLAG_IFSET,
+ SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP,
SPStyle const *const base = NULL ) const;
void cascade( SPStyle const *const parent );
void merge( SPStyle const *const parent );
@@ -64,8 +65,8 @@ public:
private:
void _mergeString( char const *const p );
- void _mergeDeclList( CRDeclaration const *const decl_list );
- void _mergeDecl( CRDeclaration const *const decl );
+ void _mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source );
+ void _mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source );
void _mergeProps( CRPropList *const props );
void _mergeObjectStylesheet( SPObject const *const object );
diff --git a/src/svg/svg-length.h b/src/svg/svg-length.h
index bd3435ca6..4663aced4 100644
--- a/src/svg/svg-length.h
+++ b/src/svg/svg-length.h
@@ -12,6 +12,8 @@
#ifndef SEEN_SP_SVG_LENGTH_H
#define SEEN_SP_SVG_LENGTH_H
+#include <string>
+
/**
* SVG length type
*/
diff --git a/src/trace/trace.h b/src/trace/trace.h
index 662b2537e..f562e89aa 100644
--- a/src/trace/trace.h
+++ b/src/trace/trace.h
@@ -13,10 +13,6 @@
# include "config.h"
#endif
-#ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-#endif
-
#ifdef HAVE_STRING_H
# include <string.h>
#endif
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index f2a256698..11a0c351f 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -57,6 +57,7 @@ set(ui_SRC
dialog/calligraphic-profile-rename.cpp
dialog/clonetiler.cpp
dialog/color-item.cpp
+ dialog/cssdialog.cpp
dialog/debug.cpp
dialog/desktop-tracker.cpp
dialog/dialog-manager.cpp
@@ -99,6 +100,7 @@ set(ui_SRC
dialog/print-colors-preview-dialog.cpp
dialog/print.cpp
dialog/spellcheck.cpp
+ dialog/styledialog.cpp
dialog/svg-fonts-dialog.cpp
dialog/swatches.cpp
dialog/symbols.cpp
@@ -195,6 +197,7 @@ set(ui_SRC
dialog/calligraphic-profile-rename.h
dialog/clonetiler.h
dialog/color-item.h
+ dialog/cssdialog.h
dialog/debug.h
dialog/desktop-tracker.h
dialog/dialog-manager.h
@@ -239,6 +242,7 @@ set(ui_SRC
dialog/print-colors-preview-dialog.h
dialog/print.h
dialog/spellcheck.h
+ dialog/styledialog.h
dialog/svg-fonts-dialog.h
dialog/swatches.h
dialog/symbols.h
diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp
index 73b632a2c..e719f8d63 100644
--- a/src/ui/clipboard.cpp
+++ b/src/ui/clipboard.cpp
@@ -644,7 +644,6 @@ Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop)
return svgd;
}
-
/**
* Iterate over a list of items and copy them to the clipboard.
*/
@@ -690,25 +689,6 @@ void ClipboardManagerImpl::_copySelection(ObjectSet *selection)
else
obj_copy = _copyNode(obj, _doc, _clipnode);
- // For lpe items, copy lpe stack if applicable
- SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item);
- if (lpeitem) {
- Inkscape::SVGOStringStream os;
- if (lpeitem->hasPathEffect()) {
- for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it)
- {
- LivePathEffectObject *lpeobj = (*it)->lpeobject;
- if (lpeobj) {
- Inkscape::XML::Node * lpeobjcopy = _copyNode(lpeobj->getRepr(), _doc, _defs);
- gchar *new_conflict_id = sp_object_get_unique_id(lpeobj, lpeobj->getAttribute("id"));
- lpeobjcopy->setAttribute("id", new_conflict_id);
- g_free(new_conflict_id);
- os << "#" << lpeobjcopy->attribute("id") << ";";
- }
- }
- }
- obj_copy->setAttribute("inkscape:path-effect", os.str().c_str());
- }
// copy complete inherited style
SPCSSAttr *css = sp_repr_css_attr_inherited(obj, "style");
sp_repr_css_set(obj_copy, css, "style");
@@ -740,6 +720,13 @@ void ClipboardManagerImpl::_copySelection(ObjectSet *selection)
sp_repr_css_set(_clipnode, style, "style");
sp_repr_css_attr_unref(style);
}
+ // copy path effect from the first path
+ if (object) {
+ gchar const *effect =object->getRepr()->attribute("inkscape:path-effect");
+ if (effect) {
+ _clipnode->setAttribute("inkscape:path-effect", effect);
+ }
+ }
}
Geom::OptRect size = selection->visualBounds();
@@ -842,6 +829,19 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item)
}
}
+ // For lpe items, copy lpe stack if applicable
+ SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item);
+ if (lpeitem) {
+ if (lpeitem->hasPathEffect()) {
+ for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it){
+ LivePathEffectObject *lpeobj = (*it)->lpeobject;
+ if (lpeobj) {
+ _copyNode(lpeobj->getRepr(), _doc, _defs);
+ }
+ }
+ }
+ }
+
// recurse
for(auto& o: item->children) {
SPItem *childItem = dynamic_cast<SPItem *>(&o);
diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp
index 8f0545e96..40b948a23 100644
--- a/src/ui/dialog/aboutbox.cpp
+++ b/src/ui/dialog/aboutbox.cpp
@@ -152,16 +152,16 @@ Gtk::Widget *build_splash_widget() {
the `screens' directory. Thus the translation of "about.svg" should be
the filename of its translated version, e.g. about.zh.svg for Chinese.
- N.B. about.svg changes once per release. (We should probably rename
- the original to about-0.40.svg etc. as soon as we have a translation.
- If we do so, then add an item to release-checklist saying that the
- string here should be changed.) */
+ Please don't translate the filename unless the translated picture exists. */
// FIXME? INKSCAPE_SCREENSDIR and "about.svg" are in UTF-8, not the
// native filename encoding... and the filename passed to sp_document_new
// should be in UTF-*8..
char *about=g_build_filename(INKSCAPE_SCREENSDIR, _("about.svg"), NULL);
+ if (!g_file_test (about, G_FILE_TEST_EXISTS)) {
+ about=g_build_filename(INKSCAPE_SCREENSDIR, "about.svg", NULL);
+ }
SPDocument *doc=SPDocument::createNewDoc (about, TRUE);
g_free(about);
g_return_val_if_fail(doc != NULL, NULL);
diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp
new file mode 100644
index 000000000..fa266b012
--- /dev/null
+++ b/src/ui/dialog/cssdialog.cpp
@@ -0,0 +1,125 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "cssdialog.h"
+#include "ui/widget/addtoicon.h"
+#include "widgets/icon.h"
+#include "verbs.h"
+#include "sp-object.h"
+#include "selection.h"
+#include "xml/attribute-record.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief CssDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * This function sets the style of '+'button at the bottom of dialog.
+ */
+void CssDialog::_styleButton(Gtk::Button& btn, char const* iconName,
+ char const* tooltip)
+{
+ GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName);
+ gtk_widget_show(child);
+ btn.add(*manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+ btn.set_tooltip_text(tooltip);
+}
+
+/**
+ * Constructor
+ * A treeview whose each row corresponds to a CSS property of selector selected.
+ * New CSS property can be added by clicking '+' at bottom of the CSS pane. '-'
+ * in front of the CSS property row can be clicked to delete the CSS property.
+ * Besides clicking on an already selected property row makes the property editable
+ * and clicking 'Enter' updates the property with changes reflected in the
+ * drawing.
+ */
+CssDialog::CssDialog():
+ UI::Widget::Panel("", "/dialogs/css", SP_VERB_DIALOG_CSS),
+ _desktop(0)
+{
+ set_size_request(20, 15);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _treeView.set_headers_visible(false);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ _store = Gtk::ListStore::create(_cssColumns);
+ _treeView.set_model(_store);
+
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI::
+ Widget::AddToIcon());
+ addRenderer->property_active() = false;
+
+ int addCol = _treeView.append_column("Unset Property", *addRenderer) - 1;
+ Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
+ if (col) {
+ col->add_attribute(addRenderer->property_active(), _cssColumns._colUnsetProp);
+ }
+ _textRenderer = Gtk::manage(new Gtk::CellRendererText());
+ _textRenderer->property_editable() = true;
+
+ int nameColNum = _treeView.append_column("Property", *_textRenderer) - 1;
+ _propCol = _treeView.get_column(nameColNum);
+
+ Gtk::Button* create = manage(new Gtk::Button());
+ _styleButton(*create, "list-add", "Add a new property");
+
+ _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
+ _buttonBox.pack_start(*create, Gtk::PACK_SHRINK);
+
+ _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET);
+
+ _targetDesktop = getDesktop();
+ setDesktop(_targetDesktop);
+
+ create->signal_clicked().connect(sigc::mem_fun(*this, &CssDialog::_addProperty));
+}
+
+/**
+ * @brief CssDialog::~CssDialog
+ * Class destructor
+ */
+CssDialog::~CssDialog()
+{
+ setDesktop(NULL);
+}
+
+/**
+ * @brief CssDialog::setDesktop
+ * @param desktop
+ * This function sets the 'desktop' for the CSS pane.
+ */
+void CssDialog::setDesktop(SPDesktop* desktop)
+{
+ _desktop = desktop;
+}
+
+/**
+ * @brief CssDialog::_addProperty
+ * This function is a slot to signal_clicked for '+' button at the bottom of CSS
+ * panel. A new row is added, double clicking which text for new property can be
+ * added. _newProperty is set to true in which case the value is appended.
+ */
+void CssDialog::_addProperty()
+{
+ _propRow = *(_store->append());
+ _newProperty = true;
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h
new file mode 100644
index 000000000..3bbab5031
--- /dev/null
+++ b/src/ui/dialog/cssdialog.h
@@ -0,0 +1,75 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef CSSDIALOG_H
+#define CSSDIALOG_H
+
+#include <gtkmm/treeview.h>
+#include <gtkmm/liststore.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/dialog.h>
+#include <ui/widget/panel.h>
+
+#include "desktop.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief The CssDialog class
+ * This dialog allows to add, delete and modify CSS properties for selectors
+ * created in Style Dialog. Double clicking any selector in Style dialog, a list
+ * of CSS properties will show up in this dialog (if any exist), else new properties
+ * can be added and each new property forms a new row in this pane.
+ */
+class CssDialog : public UI::Widget::Panel
+{
+public:
+ CssDialog();
+ ~CssDialog();
+
+ static CssDialog &getInstance() { return *new CssDialog(); }
+ void setDesktop(SPDesktop* desktop);
+
+ class CssColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+ CssColumns()
+ { add(_colUnsetProp); add(_propertyLabel); }
+ Gtk::TreeModelColumn<bool> _colUnsetProp;
+ Gtk::TreeModelColumn<Glib::ustring> _propertyLabel;
+ };
+
+ SPDesktop* _desktop;
+ SPDesktop* _targetDesktop;
+ CssColumns _cssColumns;
+ Gtk::VBox _mainBox;
+ Gtk::HBox _buttonBox;
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<Gtk::ListStore> _store;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::TreeModel::Row _propRow;
+ Gtk::CellRendererText *_textRenderer;
+ Gtk::TreeViewColumn *_propCol;
+ Glib::ustring _editedProp;
+ bool _newProperty;
+
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+ void _addProperty();
+};
+
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // CSSDIALOG_H
diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp
index c53112656..b407f8200 100644
--- a/src/ui/dialog/dialog-manager.cpp
+++ b/src/ui/dialog/dialog-manager.cpp
@@ -57,6 +57,8 @@
#include "ui/dialog/svg-fonts-dialog.h"
#include "ui/dialog/objects.h"
#include "ui/dialog/tags.h"
+#include "ui/dialog/styledialog.h"
+#include "ui/dialog/cssdialog.h"
namespace Inkscape {
namespace UI {
@@ -125,6 +127,8 @@ DialogManager::DialogManager() {
registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>);
registerFactory("TileDialog", &create<ArrangeDialog, FloatingBehavior>);
registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>);
+ registerFactory("StyleDialog", &create<StyleDialog, FloatingBehavior>);
+ registerFactory("CssDialog", &create<CssDialog, FloatingBehavior>);
#if HAVE_POTRACE
registerFactory("Trace", &create<TraceDialog, FloatingBehavior>);
@@ -164,6 +168,8 @@ DialogManager::DialogManager() {
registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>);
registerFactory("TileDialog", &create<ArrangeDialog, DockBehavior>);
registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>);
+ registerFactory("StyleDialog", &create<StyleDialog, DockBehavior>);
+ registerFactory("CssDialog", &create<CssDialog, DockBehavior>);
#if HAVE_POTRACE
registerFactory("Trace", &create<TraceDialog, DockBehavior>);
diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp
index 1bb952de4..b7207fd50 100644
--- a/src/ui/dialog/export.cpp
+++ b/src/ui/dialog/export.cpp
@@ -978,7 +978,9 @@ void Export::onExport ()
SPNamedView *nv = desktop->getNamedView();
SPDocument *doc = desktop->getDocument();
-
+ Geom::Affine rot = doc->getRoot()->c2p;
+ doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p;
+ doc->ensureUpToDate();
bool exportSuccessful = false;
bool hide = hide_export.get_active ();
@@ -1003,6 +1005,7 @@ void Export::onExport ()
if (num < 1) {
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No items selected."));
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1094,6 +1097,7 @@ void Export::onExport ()
if (filename.empty()) {
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename."));
sp_ui_error_dialog(_("You have to enter a filename"));
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1110,6 +1114,7 @@ void Export::onExport ()
if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The chosen area to be exported is invalid."));
sp_ui_error_dialog(_("The chosen area to be exported is invalid"));
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1132,6 +1137,7 @@ void Export::onExport ()
g_free(safeDir);
g_free(error);
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
return;
}
@@ -1281,6 +1287,7 @@ void Export::onExport ()
}
}
}
+ doc->getRoot()->c2p *= doc->getRoot()->rotation;
} // end of sp_export_export_clicked()
/// Called when Browse button is clicked
diff --git a/src/ui/dialog/polar-arrange-tab.cpp b/src/ui/dialog/polar-arrange-tab.cpp
index c51881a96..9485b6ba3 100644
--- a/src/ui/dialog/polar-arrange-tab.cpp
+++ b/src/ui/dialog/polar-arrange-tab.cpp
@@ -43,14 +43,14 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_)
anchorPointLabel.set_alignment(Gtk::ALIGN_START);
pack_start(anchorPointLabel, false, false);
- anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Object's bounding box:"));
+ anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Objects' bounding boxes:"));
anchorRadioGroup = anchorBoundingBoxRadio.get_group();
anchorBoundingBoxRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed));
pack_start(anchorBoundingBoxRadio, false, false);
pack_start(anchorSelector, false, false);
- anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Object's rotational center"));
+ anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Objects' rotational centers"));
anchorObjectPivotRadio.set_group(anchorRadioGroup);
anchorObjectPivotRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed));
pack_start(anchorObjectPivotRadio, false, false);
diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp
new file mode 100644
index 000000000..5246290b4
--- /dev/null
+++ b/src/ui/dialog/styledialog.cpp
@@ -0,0 +1,1116 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "styledialog.h"
+#include "ui/widget/addtoicon.h"
+#include "widgets/icon.h"
+#include "verbs.h"
+#include "sp-object.h"
+#include "selection.h"
+#include "xml/attribute-record.h"
+#include <glibmm/regex.h>
+
+using Inkscape::Util::List;
+using Inkscape::XML::AttributeRecord;
+
+/**
+ * This macro is used to remove spaces around selectors or any strings when
+ * parsing is done to update XML style element or row labels in this dialog.
+ */
+#define REMOVE_SPACES(x) x.erase(0, x.find_first_not_of(' ')); \
+ x.erase(x.find_last_not_of(' ') + 1);
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief StyleDialog::_styleButton
+ * @param btn
+ * @param iconName
+ * @param tooltip
+ * This function sets the style of '+' and '-' buttons at the bottom of dialog.
+ */
+void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName,
+ char const* tooltip)
+{
+ GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName);
+ gtk_widget_show(child);
+ btn.add(*manage(Glib::wrap(child)));
+ btn.set_relief(Gtk::RELIEF_NONE);
+ btn.set_tooltip_text (tooltip);
+}
+
+/**
+ * Constructor
+ * A treeview and a set of two buttons are added to the dialog. _addSelector
+ * adds selectors to treeview. _delSelector deletes the selector from the dialog.
+ * Any addition/deletion of the selectors updates XML style element accordingly.
+ */
+StyleDialog::StyleDialog() :
+ UI::Widget::Panel("", "/dialogs/style", SP_VERB_DIALOG_STYLE),
+ _desktop(0)
+{
+ set_size_request(200, 200);
+
+ _paned.pack1(_mainBox, Gtk::SHRINK);
+ _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET);
+ _treeView.set_headers_visible(false);
+ _scrolledWindow.add(_treeView);
+ _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ _store = Gtk::TreeStore::create(_mColumns);
+ _treeView.set_model(_store);
+
+ Inkscape::UI::Widget::AddToIcon * addRenderer = manage(
+ new Inkscape::UI::Widget::AddToIcon() );
+ addRenderer->property_active() = true;
+
+ int addCol = _treeView.append_column("type", *addRenderer) - 1;
+
+ Gtk::TreeViewColumn *col = _treeView.get_column(addCol);
+ if ( col ) {
+ col->add_attribute( addRenderer->property_active(), _mColumns._colAddRemove );
+ }
+
+ _treeView.append_column("Selector Name", _mColumns._selectorLabel);
+ _treeView.set_expander_column(*(_treeView.get_column(1)));
+
+ create = manage( new Gtk::Button() );
+ _styleButton(*create, "list-add", "Add a new CSS Selector");
+ create->signal_clicked().connect(sigc::mem_fun(*this,
+ &StyleDialog::_addSelector));
+
+ del = manage( new Gtk::Button() );
+ _styleButton(*del, "list-remove", "Remove a CSS Selector");
+ del->signal_clicked().connect(sigc::mem_fun(*this,
+ &StyleDialog::_delSelector));
+ del->set_sensitive(false);
+
+ _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK);
+
+ _buttonBox.pack_start(*create, Gtk::PACK_SHRINK);
+ _buttonBox.pack_start(*del, Gtk::PACK_SHRINK);
+
+ _getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET);
+
+ _targetDesktop = getDesktop();
+ setDesktop(_targetDesktop);
+
+ /**
+ * @brief document
+ * If an existing document is opened, its XML representation is obtained
+ * and is then used to populate the treeview with the already existing
+ * selectors in the style element.
+ */
+ _styleExists = false;
+ _document = _targetDesktop->doc();
+ _selectorValue = _populateTree(_getSelectorVec());
+
+ _treeView.signal_button_press_event().connect(sigc::mem_fun(*this,
+ &StyleDialog::
+ _handleButtonEvent),
+ false);
+
+ _treeView.signal_button_press_event().connect_notify(sigc::mem_fun
+ (*this, &StyleDialog::
+ _buttonEventsSelectObjs),
+ false);
+
+ _cssPane = new CssDialog;
+
+ _treeView.get_selection()->signal_changed().connect(sigc::mem_fun(*this,
+ &StyleDialog::
+ _selChanged));
+}
+
+/**
+ * @brief StyleDialog::~StyleDialog
+ * Class destructor
+ */
+StyleDialog::~StyleDialog()
+{
+ setDesktop(NULL);
+}
+
+/**
+ * @brief StyleDialog::setDesktop
+ * @param desktop
+ * This function sets the 'desktop' for the Style Dialog.
+ */
+void StyleDialog::setDesktop( SPDesktop* desktop )
+{
+ Panel::setDesktop(desktop);
+ _desktop = Panel::getDesktop();
+ _desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &StyleDialog::
+ _selectRow));
+}
+
+/**
+ * @brief StyleDialog::_addSelector
+ * This function is the slot to the signal emitted when '+' at the bottom of
+ * the dialog is clicked.
+ */
+void StyleDialog::_addSelector()
+{
+ Gtk::TreeModel::Row row = *(_store->append());
+
+ /**
+ * On clicking '+' button, an entrybox with default text opens up. If an
+ * object is already selected, a selector with value in the entry
+ * is added to a new style element.
+ */
+ Gtk::Dialog *textDialogPtr = new Gtk::Dialog();
+ Gtk::Entry *textEditPtr = manage ( new Gtk::Entry() );
+ textDialogPtr->add_button("Add", Gtk::RESPONSE_OK);
+ textDialogPtr->get_vbox()->pack_start(*textEditPtr, Gtk::PACK_SHRINK);
+
+ /**
+ * By default, the entrybox contains 'Class1' as text. However, if object(s)
+ * is(are) selected and user clicks '+' at the bottom of dialog, the
+ * entrybox will have the id(s) of the selected objects as text.
+ */
+ if (_desktop->getSelection()->isEmpty()) {
+ textEditPtr->set_text("Class1");
+ }
+ else {
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject*> selected = std::vector<SPObject *>(selection
+ ->objects().begin(),
+ selection->
+ objects().end());
+ textEditPtr->set_text(_setClassAttribute(selected));
+ }
+
+ textDialogPtr->set_size_request(200, 100);
+ textDialogPtr->show_all();
+ int result = textDialogPtr->run();
+
+ /**
+ * @brief selectorName
+ * This string stores selector name. The text from entrybox is saved as name
+ * for selector. If the entrybox is empty, the text (thus selectorName) is
+ * set to ".Class1"
+ */
+ if (!textEditPtr->get_text().empty()) {
+ _selectorName = textEditPtr->get_text();
+ }
+ else {
+ _selectorName = ".Class1";
+ }
+
+ del->set_sensitive(true);
+
+ /**
+ * The selector name objects is set to the text that the user sets in the
+ * entrybox. If the attribute does not exist, it is
+ * created. In case the attribute already has a value, the new value entered
+ * is appended to the values. If a style attribute does not exist, it is
+ * created with an empty value. Also if a class selector is added, then
+ * class attribute for the selected object is set too.
+ */
+ std::vector<SPObject *> objVec;
+
+ bool objExists = false;
+ if (!_desktop->getSelection()->isEmpty()) {
+ for (auto& obj: _desktop->getSelection()->objects()) {
+ objExists = true;
+ if (!obj->getRepr()->attribute("style")) {
+ obj->getRepr()->setAttribute("style", NULL);
+ }
+
+ if (obj->getAttribute("style") == NULL) {
+ _selectorValue = _selectorName + "{" + "}" + "\n";
+ }
+ else {
+ _selectorValue = _selectorName + "{"
+ + obj->getAttribute("style") + "}" + "\n";
+ }
+
+ if (_selectorName[0] == '.') {
+ if (!obj->getRepr()->attribute("class")) {
+ obj->getRepr()->setAttribute("class", textEditPtr->get_text()
+ .erase(0,1));
+ }
+ else {
+ obj->getRepr()->setAttribute("class", std::string(obj->
+ getRepr()->
+ attribute("class"))
+ + " " + textEditPtr->get_text()
+ .erase(0,1));
+ }
+ }
+ }
+ }
+ else {
+ _selectorValue = _selectorName + "{" + "}" + "\n";
+ objExists = false;
+ }
+
+ switch (result) {
+ case Gtk::RESPONSE_OK:
+ textDialogPtr->hide();
+ row[_mColumns._selectorLabel] = _selectorName;
+ row[_mColumns._colAddRemove] = true;
+ if (objExists) {
+ Inkscape::Selection* selection = _desktop->getSelection();
+ row[_mColumns._colObj] = std::vector<SPObject *>(selection->objects()
+ .begin(), selection
+ ->objects().end());
+ objVec = row[_mColumns._colObj];
+ }
+ break;
+ default:
+ break;
+ }
+
+ /**
+ * A new style element is added to the document with value obtained
+ * from selectorValue above. If style element already exists, then
+ * the new selector's content is appended to its previous content.
+ */
+ inkSelector._selector = _selectorName;
+ inkSelector._matchingObjs = objVec;
+ inkSelector._xmlContent = _selectorValue;
+ _selectorVec.push_back(inkSelector);
+
+ if (_styleElementNode()) {
+ _styleChild = _styleElementNode();
+ _updateStyleContent();
+ }
+ else if (_styleExists && !_newDrawing) {
+ _updateStyleContent();
+ }
+ else if (!_styleExists) {
+ Inkscape::XML::Node *root = _document->getReprDoc()->root();
+ Inkscape::XML::Node *newChild = _document->getReprDoc()
+ ->createElement("svg:style");
+ Inkscape::XML::Node *smallChildren = _document->getReprDoc()
+ ->createTextNode(_selectorValue.c_str());
+
+ newChild->appendChild(smallChildren);
+ Inkscape::GC::release(smallChildren);
+
+ root->addChild(newChild, NULL);
+ Inkscape::GC::release(newChild);
+ _styleChild = newChild;
+ }
+ _selAdd(row);
+}
+
+/**
+ * @brief StyleDialog::_updateStyleContent
+ * This function updates the content in style element as new selectors (or
+ * objects) are added/removed.
+ */
+void StyleDialog::_updateStyleContent()
+{
+ std::string styleContent = "";
+ for (unsigned i = 0; i < _selectorVec.size(); ++i) {
+ styleContent = styleContent + _selectorVec[i]._xmlContent;
+ }
+ _styleChild->firstChild()->setContent(styleContent.c_str());
+}
+
+/**
+ * @brief StyleDialog::_delSelector
+ * This function deletes selector when '-' at the bottom is clicked. The index
+ * of selected row is obtained and the corresponding selector and its values are
+ * deleted from the selector vector. If a row has no parent, it is directly
+ * erased from the vector along with its child rows. The style element is updated
+ * accordingly.
+ */
+void StyleDialog::_delSelector()
+{
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end();) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+ if (key == matchSelector) {
+ it = _selectorVec.erase(it);
+ _store->erase(row);
+ }
+ else {
+ ++it;
+ }
+
+ /**
+ * The _stylechild is obtained which contains the style element and
+ * the content in style element is updated. If _selectorVec is
+ * empty, the style element is removed from the XML repr else
+ * the content is updated simply using _updateStyleContent().
+ */
+ _styleChild = _styleElementNode();
+ if (_store->children().empty()) {
+ _document->getReprRoot()->removeChild(_styleChild);
+ _styleExists = false;
+ }
+ else {
+ _updateStyleContent();
+ }
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_styleElementNode
+ * @return
+ * This function returns the node containing style element. The document's
+ * children are iterated and the repr of the style element that occurs is
+ * obtained.
+ */
+Inkscape::XML::Node* StyleDialog::_styleElementNode()
+{
+ for (unsigned i = 0; i < _document->getReprRoot()->childCount(); ++i) {
+ if (std::string(_document->getReprRoot()->nthChild(i)->name())
+ == "svg:style") {
+ _styleExists = true;
+ _newDrawing = true;
+ return _document->getReprRoot()->nthChild(i);
+ }
+ }
+ return NULL;
+}
+
+/**
+ * @brief StyleDialog::_setClassAttribute
+ * @param sel
+ * @return This function returns the ids of objects selected which are passed
+ * to entrybox.
+ */
+std::string StyleDialog::_setClassAttribute(std::vector<SPObject*> sel)
+{
+ std::string str = "";
+ for ( unsigned i = 0; i < sel.size(); ++i ) {
+ SPObject *obj = sel.at(i);
+ str = str + "#" + std::string(obj->getId()) + " ";
+ }
+ return str;
+}
+
+/**
+ * @brief StyleDialog::_getSelectorVec
+ * @return selVec
+ * This function returns a vector whose key is the style selector name and value
+ * is the style properties. All style selectors are extracted from svg:style
+ * element. _newDrawing is flag is set to false check if an existing drawing is
+ * opened.
+ */
+std::vector<StyleDialog::InkSelector> StyleDialog::_getSelectorVec()
+{
+ for (unsigned i = 0; i < _document->getReprRoot()->childCount(); ++i) {
+ if (std::string(_document->getReprRoot()->nthChild(i)->name()) == "svg:style") {
+ _styleExists = true;
+ _newDrawing = false;
+ _styleChild = _document->getReprRoot()->nthChild(i);
+
+ // Get content from first style element.
+ std::string content = _styleChild->firstChild()->content();
+
+ // Remove end-of-lines (check it works on Windoze).
+ content.erase(std::remove(content.begin(), content.end(), '\n'), content.end());
+
+ // First split into selector/value chunks.
+ // An attempt to use Glib::Regex failed. A C++11 version worked but
+ // reportedly has problems on Windows. Using split_simple() is simpler
+ // and probably faster.
+ //
+ // Glib::RefPtr<Glib::Regex> regex1 =
+ // Glib::Regex::create("([^\\{]+)\\{([^\\{]+)\\}");
+ //
+ // Glib::MatchInfo minfo;
+ // regex1->match(content, minfo);
+
+ // Split on curly brackets. Even tokens are selectors, odd are values.
+ std::vector<std::string> tokens = Glib::Regex::split_simple("[}{]", content);
+
+ for (unsigned i = 0; i < tokens.size()-1; i += 2) {
+ std::string selectors = tokens[i];
+ REMOVE_SPACES(selectors); // Remove leading/trailing spaces
+
+ /** Make a list of all objects that selector matches. This is
+ * currently limited to simple id, class, and element selectors.
+ * Expanding this would take integrating a true CSS parser.
+ */
+ std::vector<SPObject *>objVec;
+
+ // Split selector string into individual selectors (which are comma separated).
+ std::vector<std::string> tokens2 = Glib::Regex::split_simple
+ ("\\s*,\\s*", selectors );
+
+ for(unsigned i = 0; i < tokens2.size(); ++i) {
+ std::string token2 = tokens2[i];
+
+ // Find objects that match class selector
+ if (token2[0] == '.') {
+ token2.erase(0,1);
+ std::vector<SPObject *> objects = _document
+ ->getObjectsByClass(token2);
+ objVec.insert(objVec.end(), objects.begin(), objects.end());
+ }
+
+ // Find objects that match id selector
+ else if (token2[0] == '#') {
+ token2.erase(0,1);
+ SPObject * object = _document->getObjectById(token2);
+ if (object) {
+ objVec.push_back(object);
+ }
+ }
+
+ // Find objects that match element selector
+ else {
+ std::vector<SPObject *> objects = _document->
+ getObjectsByElement(token2);
+ objVec.insert(objVec.end(), objects.begin(), objects.end());
+ }
+ }
+
+ std::string values;
+ // Check to make sure we do have a value to match selector.
+ if ((i+1) < tokens.size()) {
+ values = tokens[i+1];
+ } else {
+ std::cerr << "StyleDialog::_getSelectorVec: Missing values "
+ "for last selector!" << std::endl;
+ }
+
+ _selectorValue = selectors + "{" + values + "}\n";
+ inkSelector._selector = selectors;
+ inkSelector._matchingObjs = objVec;
+ inkSelector._xmlContent = _selectorValue;
+ _selectorVec.push_back(inkSelector);
+ }
+ }
+ }
+ return _selectorVec;
+}
+
+/**
+ * @brief StyleDialog::_populateTree
+ * @param _selVec
+ * This function populates the treeview with selectors available in the
+ * stylesheet.
+ */
+std::string StyleDialog::_populateTree(std::vector<InkSelector> _selVec)
+{
+ _selectorVec = _selVec;
+ std::string selectorValue;
+
+ for(unsigned it = 0; it < _selectorVec.size(); ++it) {
+ Gtk::TreeModel::Row row = *(_store->append());
+ row[_mColumns._selectorLabel] = _selectorVec[it]._selector;
+ row[_mColumns._colAddRemove] = true;
+ row[_mColumns._colObj] = _selectorVec[it]._matchingObjs;
+ std::string selValue = _selectorVec[it]._xmlContent;
+ selectorValue.append(selValue.c_str());
+ }
+
+ if (_selectorVec.size() > 0) {
+ del->set_sensitive(true);
+ }
+
+ return selectorValue;
+}
+
+/**
+ * @brief StyleDialog::_handleButtonEvent
+ * @param event
+ * @return
+ * This function handles the event when '+' button in front of a selector name
+ * is clicked. The selected objects (if any) is added to the selector as a child
+ * in the treeview.
+ */
+bool StyleDialog::_handleButtonEvent(GdkEventButton *event)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ Gtk::TreeViewColumn *col = 0;
+ Gtk::TreeModel::Path path;
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ int x2 = 0;
+ int y2 = 0;
+ if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _treeView.get_column(0)) {
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection =
+ _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->
+ get_selected();
+ Gtk::TreeModel::Row row = *iter;
+
+ /**
+ * This adds child rows to selected rows. If the parent row is
+ * a class selector, then the class attribute of object added
+ * to child row is appended with class in the parent row. The
+ * else below deletes objects from selectors when 'delete' button
+ * in front of child row is clicked. The class attribute is updated
+ * by removing the parent row's class selector name.
+ */
+ if (!row.parent()) {
+ _selAdd(row);
+ }
+
+ else {
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ Gtk::TreeModel::Row parentRow = *(row).parent();
+ Glib::ustring parentKey = parentRow[_mColumns._selectorLabel];
+
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ /**
+ * @brief matchSelector
+ * For id selectors, whenever any child row is deleted,
+ * the row label is updated and so is the entry for the
+ * selector in style element.
+ */
+ std::string matchSelector = parentKey;
+ REMOVE_SPACES(matchSelector);
+ if (key == matchSelector) {
+ if (key[0] == '#') {
+ std::string s = parentKey;
+ Glib::ustring toDelRow = row[_mColumns._selectorLabel];
+ std::string toDelKey = toDelRow;
+ std::size_t idFound = s.find(toDelKey);
+ if (idFound != std::string::npos) {
+ if (idFound == 0) {
+ s.erase(idFound, toDelKey.length()+1);
+ parentKey = s;
+ parentRow[_mColumns._selectorLabel] = parentKey;
+ (*it)._xmlContent.erase(idFound, toDelKey.length());
+ }
+ else {
+ s.erase(idFound-2, toDelKey.length()+2);
+ parentKey = s;
+ parentRow[_mColumns._selectorLabel] = parentKey;
+ (*it)._xmlContent.erase(idFound-2, toDelKey.
+ length()+2);
+ }
+ }
+ }
+ }
+
+ if (parentKey[0] == '.') {
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ std::string classAttr = std::string(obj->getRepr()
+ ->attribute("class"));
+ std::size_t found = classAttr.find(parentKey.erase(0,1));
+ if (found != std::string::npos) {
+ classAttr.erase(found, parentKey.length()+1);
+ obj->getRepr()->setAttribute("class", classAttr);
+ }
+ }
+ }
+
+ if (parentKey.empty()) {
+ (*it)._xmlContent = "";
+ }
+ }
+
+ if (_styleChild) {
+ _updateStyleContent();
+ }
+ else {
+ _styleChild = _styleElementNode();
+ _updateStyleContent();
+ }
+
+ _store->erase(row);
+
+ /**
+ * On continuous deletion of objects (child rows) from the
+ * selector (parent row), if the parent row has no child, then
+ * the row is erased from the _store. Further if there is no
+ * row left in _store, which implies there is no content in
+ * XML style element, then the 'svg:style' element is also
+ * removed.
+ */
+ if (parentKey.empty()) {
+ _store->erase(parentRow);
+ }
+
+ if (parentKey.empty() && _store->children().empty()) {
+ _document->getReprRoot()->removeChild(_styleChild);
+ _styleExists = false;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief StyleDialog::_selAdd
+ * @param row
+ * This routine is called when an object is added to a selector by clicking on
+ * '+' in front of the row with selector's label.
+ */
+void StyleDialog::_selAdd(Gtk::TreeModel::Row row)
+{
+ Glib::ustring selectorName;
+ Gtk::TreeModel::Row childrow;
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject *> sel = std::vector<SPObject *>
+ (selection->objects().begin(), selection->objects().end());
+ for (auto& obj: selection->objects()) {
+ if (*row) {
+ if (_selectorVec.size() != 0) {
+ childrow = *(_store->append(row->children()));
+ childrow[_mColumns._selectorLabel] = "#" +
+ std::string(obj->getId());
+ childrow[_mColumns._colAddRemove] = false;
+ childrow[_mColumns._colObj] = sel;
+ Glib::ustring key = row[_mColumns._selectorLabel];
+ if (key[0] == '.') {
+ if (!obj->getRepr()->attribute("class")) {
+ obj->setAttribute("class", key.erase(0,1));
+ }
+ else {
+ if (obj->getRepr()->attribute("class") != key
+ .erase(0,1)) {
+ obj->setAttribute("class", std::string
+ (obj->getRepr()->
+ attribute("class"))
+ + " " + key
+ .erase(0,1));
+ }
+ }
+ }
+ }
+ selectorName = row[_mColumns._selectorLabel];
+ }
+
+ /**
+ * If the object's parent row is a class selector, then
+ * there are no changes in style element except the class
+ * attribute is updated. For the id selector cases, XML
+ * content's style element is updated.
+ */
+ REMOVE_SPACES(selectorName);
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ std::string sel, key, value;
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+ if (key == matchSelector) {
+ REMOVE_SPACES((*it)._selector);
+ if (selectorName[0] == '#') {
+ if ("#" + std::string(obj->getId()) != selectorName) {
+ inkSelector._selector = (*it)._selector;
+ inkSelector._selector.append(", #" + std::string(obj->getId()));
+ inkSelector._xmlContent = inkSelector._selector + "{" + value + "}\n";
+ row[_mColumns._selectorLabel] = selectorName + ", " +
+ childrow[_mColumns._selectorLabel];
+ }
+ }
+ else if (selectorName[0] == '.') {
+ inkSelector._xmlContent = (*it)._selector + "{" + value + "}\n";
+ }
+
+ it = _selectorVec.erase(it);
+ it = _selectorVec.insert(it, inkSelector);
+ }
+ }
+ }
+ if (_styleElementNode()) {
+ _styleChild = _styleElementNode();
+ _updateStyleContent();
+ }
+ else if (_styleExists && !_newDrawing) {
+ _updateStyleContent();
+ }
+}
+
+/**
+ * @brief StyleDialog::_selectObjects
+ * @param event
+ * This function detects single or double click on a selector in any row. Single
+ * click on a selector selects the matching objects. A double click on any
+ * selector selects the matching objects as well as will open CSS dialog. It
+ * calls _selectObjects to add objects to selection.
+ */
+void StyleDialog::_buttonEventsSelectObjs(GdkEventButton* event )
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ _selectObjects(x, y);
+ }
+ else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ _selectObjects(x, y);
+
+ //Open CSS dialog here.
+ if (!_cssPane->get_visible()) {
+ _paned.pack2(*_cssPane, Gtk::SHRINK);
+ _cssPane->show_all();
+ }
+
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+
+ if (key == matchSelector) {
+ _cssPane->_store->clear();
+ std::stringstream ss(value);
+ std::string token;
+ std::size_t found = value.find(";");
+ if (found!=std::string::npos) {
+ while(std::getline(ss, token, ';')) {
+ REMOVE_SPACES(token);
+ if (!token.empty()) {
+ _cssPane->_propRow = *(_cssPane->_store->append());
+ _cssPane->_propRow[_cssPane->_cssColumns._colUnsetProp] = false;
+ _cssPane->_propRow[_cssPane->_cssColumns._propertyLabel] = token;
+ _cssPane->_propCol->add_attribute(_cssPane->_textRenderer
+ ->property_text(),
+ _cssPane->_cssColumns
+ ._propertyLabel);
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ _cssPane->_store->clear();
+ _cssPane->hide();
+ }
+
+ _cssPane->_textRenderer->signal_edited().connect(sigc::mem_fun(*this,
+ &StyleDialog::
+ _handleEdited));
+ _cssPane->_treeView.signal_button_press_event().connect(sigc::mem_fun
+ (*this, &StyleDialog::
+ _delProperty),
+ false);
+ }
+}
+
+/**
+ * @brief StyleDialog::_selChanged
+ * When no row in _treeView of Style Dialog is selected, the _cssPane is hidden.
+ */
+void StyleDialog::_selChanged() {
+ if (_treeView.get_selection()->count_selected_rows() == 0) {
+ _cssPane->hide();
+ }
+}
+
+/**
+ * @brief StyleDialog::_handleEdited
+ * @param path
+ * @param new_text
+ * This function edits CSS properties of the selector chosen. new_text is used
+ * to update the property in XML repr. The value from selected selector is
+ * obtained and modified as per value of new_text. If a new property is added,
+ * value is appended with new_text. Later _updateStyleContent() is called to
+ * update XML repr and hence changes are reflected in the drawing too.
+ */
+void StyleDialog::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text)
+{
+ Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path);
+ if (iterCss) {
+ Gtk::TreeModel::Row row = *iterCss;
+ row[_cssPane->_cssColumns._propertyLabel] = new_text;
+ _cssPane->_editedProp = new_text;
+ }
+
+ // Selected selector row is obtained here to get corresponding key and value.
+ Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection();
+ Gtk::TreeModel::iterator iter = refTreeSelection->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+
+ if (key == matchSelector) {
+ /** If a new property is added, existing value is appended with new
+ * property, else replacements in value are done in the 'else' block.
+ */
+ if (_cssPane->_newProperty) {
+ if (!new_text.empty()) {
+ value.append((new_text + ";").c_str());
+ _cssPane->_propCol->add_attribute(_cssPane->_textRenderer
+ ->property_text(),
+ _cssPane->_cssColumns
+ ._propertyLabel);
+ _cssPane->_newProperty = false;
+ }
+ }
+ else {
+ std::stringstream ss(value);
+ std::string token, editedToken;
+ std::size_t found = value.find(";");
+ if (found!=std::string::npos) {
+ while(std::getline(ss, token, ';')) {
+ REMOVE_SPACES(token);
+ if (!token.empty()) {
+ if (token.substr(0, token.find(":")) == _cssPane
+ ->_editedProp.substr(0, _cssPane->_editedProp
+ .find(":"))) {
+ editedToken = _cssPane->_editedProp;
+ size_t startPos = value.find(token);
+ value.replace(startPos, token.length(), editedToken);
+ }
+ }
+ }
+ }
+ }
+ value.erase(std::remove(value.begin(), value.end(), '\n'), value.end());
+ (*it)._xmlContent = key + "{" + value + "}\n";
+ _updateStyleContent();
+ }
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_delProperty
+ * @param event
+ * @return
+ * This function deletes property when '-' in front of property in CSS panel is
+ * clicked. The property row is deleted from CSS panel and XML repr is updated.
+ * toDelProperty is the property to be deleted which is looked in 'value' and is
+ * erased from 'value'.
+ */
+bool StyleDialog::_delProperty(GdkEventButton *event)
+{
+ if (event->type == GDK_BUTTON_PRESS && event->button == 1) {
+ Gtk::TreeViewColumn *col = 0;
+ Gtk::TreeModel::Path path;
+ int x = static_cast<int>(event->x);
+ int y = static_cast<int>(event->y);
+ int x2 = 0;
+ int y2 = 0;
+ Gtk::TreeModel::Row cssRow;
+ Glib::ustring toDelProperty;
+ if (_cssPane->_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _cssPane->_treeView.get_column(0)) {
+ Gtk::TreeModel::iterator cssIter = _cssPane->_treeView.get_selection()
+ ->get_selected();
+ if (cssIter) {
+ cssRow = *cssIter;
+ toDelProperty = cssRow[_cssPane->_cssColumns._propertyLabel];
+ }
+
+ Gtk::TreeModel::iterator iter = _treeView.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::string sel, key, value;
+ std::vector<InkSelector>::iterator it;
+ for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) {
+ sel = (*it)._xmlContent;
+ REMOVE_SPACES(sel);
+ if (!sel.empty()) {
+ key = strtok((char*)sel.c_str(), "{");
+ REMOVE_SPACES(key);
+ char *temp = strtok(NULL, "}");
+ if (strtok(temp, "}") != NULL) {
+ value = strtok(temp, "}");
+ }
+ }
+
+ Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel];
+ std::string matchSelector = selectedRowLabel;
+ REMOVE_SPACES(matchSelector);
+
+ if (key == matchSelector) {
+ std::size_t found = value.find(toDelProperty);
+ if (found!=std::string::npos) {
+ if (!toDelProperty.empty()) {
+ value.erase(found, toDelProperty.length()+1);
+ (*it)._xmlContent = key + "{" + value + "}\n";
+ _updateStyleContent();
+ _cssPane->_store->erase(cssRow);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * @brief StyleDialog::_selectObjects
+ * @param eventX
+ * @param eventY
+ * This function selects objects in the drawing corresponding to the selector
+ * selected in the treeview.
+ */
+void StyleDialog::_selectObjects(int eventX, int eventY)
+{
+ _desktop->selection->clear();
+ Gtk::TreeViewColumn *col = _treeView.get_column(1);
+ Gtk::TreeModel::Path path;
+ int x = eventX;
+ int y = eventY;
+ int x2 = 0;
+ int y2 = 0;
+ if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) {
+ if (col == _treeView.get_column(1)) {
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ Gtk::TreeModel::Children children = row.children();
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ _desktop->selection->add(obj);
+ }
+ if (children) {
+ _checkAllChildren(children);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_checkAllChildren
+ * @param children
+ * This function iterates children of the row selected in treeview and selects
+ * the objects corresponding to any selector in child rows.
+ */
+void StyleDialog::_checkAllChildren(Gtk::TreeModel::Children& children)
+{
+ for (Gtk::TreeModel::Children::iterator iter = children.begin();
+ iter!= children.end(); ++iter) {
+ Gtk::TreeModel::Row childrow = *iter;
+ std::vector<SPObject *> objVec = childrow[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ SPObject *obj = objVec[i];
+ _desktop->selection->add(obj);
+ }
+ }
+}
+
+/**
+ * @brief StyleDialog::_selectRow
+ * This function selects the rows in treeview corresponding to an object selected
+ * in the drawing.
+ */
+void StyleDialog::_selectRow(Selection */*sel*/)
+{
+ SPObject *obj = NULL;
+ if (!_desktop->selection->isEmpty()) {
+ Inkscape::Selection* selection = _desktop->getSelection();
+ std::vector<SPObject*> selected = std::vector<SPObject *>
+ (selection->objects().begin(), selection->objects().end());
+ obj = selected.back();
+ }
+
+ /**
+ * If obj has some SPObject, then it is added to desktop's selection. If a
+ * row in treeview has children, those rows are checked too against selected
+ * object's id. If an object which is not present in any selector is selected,
+ * the treeview's selections are unselected.
+ */
+ if (obj != NULL) {
+ Gtk::TreeModel::Children children = _store->children();
+ for(Gtk::TreeModel::Children::iterator iter = children.begin();
+ iter != children.end(); ++iter) {
+ Gtk::TreeModel::Row row = *iter;
+ std::vector<SPObject *> objVec = row[_mColumns._colObj];
+ for (unsigned i = 0; i < objVec.size(); ++i) {
+ if (obj->getId() == objVec[i]->getId()) {
+ _treeView.get_selection()->select(row);
+ }
+ }
+ }
+ }
+ else {
+ _treeView.get_selection()->unselect_all();
+ }
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h
new file mode 100644
index 000000000..b03a1d2e1
--- /dev/null
+++ b/src/ui/dialog/styledialog.h
@@ -0,0 +1,117 @@
+/** @file
+ * @brief A dialog for CSS selectors
+ */
+/* Authors:
+ * Kamalpreet Kaur Grewal
+ *
+ * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef STYLEDIALOG_H
+#define STYLEDIALOG_H
+
+#include <ui/widget/panel.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+#include <gtkmm/dialog.h>
+#include <gtkmm/treeselection.h>
+#include <gtkmm/paned.h>
+
+#include "desktop.h"
+#include "document.h"
+#include "ui/dialog/cssdialog.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+/**
+ * @brief The StyleDialog class
+ * A list of CSS selectors will show up in this dialog. This dialog allows to
+ * add and delete selectors. Objects can be added to and removed from the selectors
+ * in the dialog. Besides, selection of any selector row selects the matching
+ * objects in the drawing and vice-versa.
+ */
+typedef std::pair<std::pair<std::string, std::vector<SPObject *> >, std::string>
+_selectorVecType;
+
+class StyleDialog : public UI::Widget::Panel
+{
+public:
+ StyleDialog();
+ ~StyleDialog();
+
+ static StyleDialog &getInstance() { return *new StyleDialog(); }
+ void setDesktop(SPDesktop* desktop);
+
+private:
+ void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip);
+ std::string _setClassAttribute(std::vector<SPObject *>);
+
+ class InkSelector {
+ public:
+ std::string _selector;
+ std::vector<SPObject *> _matchingObjs;
+ std::string _xmlContent;
+ };
+
+ InkSelector inkSelector;
+ std::vector<InkSelector> _selectorVec;
+ std::vector<InkSelector> _getSelectorVec();
+ std::string _populateTree(std::vector<InkSelector>);
+ bool _handleButtonEvent(GdkEventButton *event);
+ void _buttonEventsSelectObjs(GdkEventButton *event);
+ void _selectObjects(int, int);
+ void _checkAllChildren(Gtk::TreeModel::Children& children);
+ Inkscape::XML::Node *_styleElementNode();
+ void _updateStyleContent();
+ void _selectRow(Selection *);
+
+ class ModelColumns : public Gtk::TreeModel::ColumnRecord
+ {
+ public:
+ ModelColumns()
+ { add(_selectorLabel); add(_colAddRemove); add(_colObj); }
+ Gtk::TreeModelColumn<Glib::ustring> _selectorLabel;
+ Gtk::TreeModelColumn<bool> _colAddRemove;
+ Gtk::TreeModelColumn<std::vector<SPObject *> > _colObj;
+ };
+
+ SPDesktop* _desktop;
+ SPDesktop* _targetDesktop;
+ ModelColumns _mColumns;
+ Gtk::VPaned _paned;
+ Gtk::VBox _mainBox;
+ Gtk::HBox _buttonBox;
+ Gtk::TreeView _treeView;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::ScrolledWindow _scrolledWindow;
+ Gtk::Button* del;
+ Gtk::Button* create;
+ SPDocument* _document;
+ bool _styleExists;
+ Inkscape::XML::Node *_styleChild;
+ std::string _selectorName;
+ std::string _selectorValue;
+ bool _newDrawing;
+ CssDialog *_cssPane;
+ void _selAdd(Gtk::TreeModel::Row row);
+
+ // Signal handlers
+ void _addSelector();
+ void _delSelector();
+ void _selChanged();
+
+ // Signal handler for CssDialog
+ void _handleEdited(const Glib::ustring& path, const Glib::ustring& new_text);
+ bool _delProperty(GdkEventButton *event);
+};
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+#endif // STYLEDIALOG_H
diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h
index c908cede2..4f152e0a2 100644
--- a/src/ui/tool/multi-path-manipulator.h
+++ b/src/ui/tool/multi-path-manipulator.h
@@ -82,8 +82,19 @@ private:
template <typename R>
void invokeForAll(R (PathManipulator::*method)()) {
- for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) {
- ((i->second.get())->*method)();
+ for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ) {
+ // Sometimes the PathManipulator got freed at loop end, thus
+ // invalidating the iterator so make sure that next_i will
+ // be a valid iterator and then assign i to it.
+ MapType::iterator next_i = i;
+ ++next_i;
+ // i->second is a boost::shared_ptr so try to hold on to it so
+ // it won't get freed prematurely by the WriteXML() method or
+ // whatever. See https://bugs.launchpad.net/inkscape/+bug/1617615
+ // Applicable to empty paths.
+ boost::shared_ptr<PathManipulator> hold(i->second);
+ ((hold.get())->*method)();
+ i = next_i;
}
}
template <typename R, typename A>
diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h
index 025c460e2..a05f0e3b9 100644
--- a/src/ui/tool/node.h
+++ b/src/ui/tool/node.h
@@ -20,12 +20,7 @@
#include <iosfwd>
#include <stdexcept>
#include <cstddef>
-
-#if __cplusplus >= 201103L
#include <functional>
-#else
-#include <tr1/functional>
-#endif
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp
index 33015fe11..083a7d0ba 100644
--- a/src/ui/tool/transform-handle-set.cpp
+++ b/src/ui/tool/transform-handle-set.cpp
@@ -183,6 +183,11 @@ void TransformHandle::ungrabbed(GdkEventButton *)
_setState(_state);
endTransform();
_th.signal_commit.emit(getCommitEvent());
+
+ //updates the positions of the nodes
+ Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context);
+ ControlPointSelection* selection = nt->_selected_nodes;
+ selection->setOriginalPoints();
}
diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp
index 1801a0ea0..6e1d085aa 100644
--- a/src/ui/tools/flood-tool.cpp
+++ b/src/ui/tools/flood-tool.cpp
@@ -21,6 +21,7 @@
#include <config.h>
#endif
+#include <cmath>
#include "trace/potrace/inkscape-potrace.h"
#include <2geom/pathvector.h>
#include <gdk/gdkkeysyms.h>
@@ -186,6 +187,21 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width
}
/**
+ * \brief Check whether two unsigned integers are close to each other
+ *
+ * \param[in] a The 1st unsigned int
+ * \param[in] b The 2nd unsigned int
+ * \param[in] d The threshold for comparison
+ *
+ * \return true if |a-b| <= d; false otherwise
+ */
+static bool compare_guint32(guint32 const a, guint32 const b, guint32 const d)
+{
+ const int difference = std::abs(static_cast<int>(a) - static_cast<int>(b));
+ return difference <= d;
+}
+
+/**
* Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation.
* @param check The pixel in the pixel buffer to check.
* @param orig The original selected pixel to use as the fill target color.
@@ -196,7 +212,6 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width
*/
static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixel, guint32 dtc, int threshold, PaintBucketChannels method)
{
- int diff = 0;
float hsl_check[3] = {0,0,0}, hsl_orig[3] = {0,0,0};
guint32 ac = 0, rc = 0, gc = 0, bc = 0;
@@ -222,27 +237,35 @@ static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixe
switch (method) {
case FLOOD_CHANNELS_ALPHA:
- return abs(static_cast<int>(ac) - ao) <= threshold;
+ return compare_guint32(ac, ao, threshold);
case FLOOD_CHANNELS_R:
- return abs(static_cast<int>(ac ? unpremul_alpha(rc, ac) : 0) - (ao ? unpremul_alpha(ro, ao) : 0)) <= threshold;
+ return compare_guint32(ac ? unpremul_alpha(rc, ac) : 0,
+ ao ? unpremul_alpha(ro, ao) : 0,
+ threshold);
case FLOOD_CHANNELS_G:
- return abs(static_cast<int>(ac ? unpremul_alpha(gc, ac) : 0) - (ao ? unpremul_alpha(go, ao) : 0)) <= threshold;
+ return compare_guint32(ac ? unpremul_alpha(gc, ac) : 0,
+ ao ? unpremul_alpha(go, ao) : 0,
+ threshold);
case FLOOD_CHANNELS_B:
- return abs(static_cast<int>(ac ? unpremul_alpha(bc, ac) : 0) - (ao ? unpremul_alpha(bo, ao) : 0)) <= threshold;
+ return compare_guint32(ac ? unpremul_alpha(bc, ac) : 0,
+ ao ? unpremul_alpha(bo, ao) : 0,
+ threshold);
case FLOOD_CHANNELS_RGB:
- guint32 amc, rmc, bmc, gmc;
- //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
- //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
- amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
- rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
- gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
- bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
-
- diff += abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - (amop ? unpremul_alpha(rmop, amop) : 0));
- diff += abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - (amop ? unpremul_alpha(gmop, amop) : 0));
- diff += abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - (amop ? unpremul_alpha(bmop, amop) : 0));
- return ((diff / 3) <= ((threshold * 3) / 4));
-
+ {
+ guint32 amc, rmc, bmc, gmc;
+ //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
+ //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
+ amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
+ rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
+ gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
+ bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
+
+ int diff = 0; // The total difference between each of the 3 color components
+ diff += std::abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(rmop, amop) : 0));
+ diff += std::abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(gmop, amop) : 0));
+ diff += std::abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(bmop, amop) : 0));
+ return ((diff / 3) <= ((threshold * 3) / 4));
+ }
case FLOOD_CHANNELS_H:
return ((int)(fabs(hsl_check[0] - hsl_orig[0]) * 100.0) <= threshold);
case FLOOD_CHANNELS_S:
diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp
index e628094d9..ac43b6c9d 100644
--- a/src/ui/tools/mesh-tool.cpp
+++ b/src/ui/tools/mesh-tool.cpp
@@ -1137,7 +1137,7 @@ static void sp_mesh_new_default(MeshTool &rc) {
Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
// Ensure mesh is immediately editable.
- // Editting both fill and stroke at same time doesn't work well so avoid.
+ // Editing both fill and stroke at same time doesn't work well so avoid.
if (fill_or_stroke == Inkscape::FOR_FILL) {
prefs->setBool("/tools/mesh/edit_fill", true );
prefs->setBool("/tools/mesh/edit_stroke", false);
diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp
index 8a35882b9..aab256712 100644
--- a/src/ui/tools/tool-base.cpp
+++ b/src/ui/tools/tool-base.cpp
@@ -94,6 +94,7 @@ ToolBase::ToolBase(gchar const *const *cursor_shape, gint hot_x, gint hot_y, boo
, _grdrag(NULL)
, shape_editor(NULL)
, space_panning(false)
+ , rotating_mode(false)
, _delayed_snap_event(NULL)
, _dse_callback_in_process(false)
, desktop(NULL)
@@ -327,99 +328,130 @@ bool ToolBase::root_handler(GdkEvent* event) {
static unsigned int panning = 0;
static unsigned int panning_cursor = 0;
static unsigned int zoom_rb = 0;
+ static double angle = 0;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
/// @todo REmove redundant /value in preference keys
tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
bool allow_panning = prefs->getBool("/options/spacebarpans/value");
+ int rotation_snap = 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12);
gint ret = FALSE;
switch (event->type) {
- case GDK_2BUTTON_PRESS:
- if (panning) {
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
- ret = TRUE;
- } else {
- /* sp_desktop_dialog(); */
- }
- break;
-
- case GDK_BUTTON_PRESS:
- // save drag origin
- xp = (gint) event->button.x;
- yp = (gint) event->button.y;
- within_tolerance = true;
-
- button_w = Geom::Point(event->button.x, event->button.y);
-
- switch (event->button.button) {
- case 1:
- if (this->space_panning) {
- // When starting panning, make sure there are no snap events pending because these might disable the panning again
- if (_uses_snap) {
- sp_event_context_discard_delayed_snap_event(this);
- }
- panning = 1;
-
- sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
- GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK
- | GDK_POINTER_MOTION_MASK
- | GDK_POINTER_MOTION_HINT_MASK, NULL,
- event->button.time - 1);
-
+ case GDK_2BUTTON_PRESS:
+ if (panning) {
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
ret = TRUE;
+ } else {
+ /* sp_desktop_dialog(); */
}
break;
- case 2:
- if (event->button.state & GDK_SHIFT_MASK) {
- zoom_rb = 2;
- } else {
- // When starting panning, make sure there are no snap events pending because these might disable the panning again
- if (_uses_snap) {
- sp_event_context_discard_delayed_snap_event(this);
- }
- panning = 2;
+ case GDK_BUTTON_PRESS:
+ // save drag origin
+ xp = (gint) event->button.x;
+ yp = (gint) event->button.y;
+ within_tolerance = true;
- sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
- GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
- | GDK_POINTER_MOTION_HINT_MASK, NULL,
- event->button.time - 1);
+ button_w = Geom::Point(event->button.x, event->button.y);
+ switch (event->button.button) {
+ case 1:
+ if (this->space_panning) {
+ // When starting panning, make sure there are no snap events pending because these might disable the panning again
+ if (_uses_snap) {
+ sp_event_context_discard_delayed_snap_event(this);
+ }
+ panning = 1;
- }
+ sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+ GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK, NULL,
+ event->button.time - 1);
- ret = TRUE;
- break;
+ ret = TRUE;
+ }
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ break;
- case 3:
- if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) {
- // When starting panning, make sure there are no snap events pending because these might disable the panning again
- if (_uses_snap) {
- sp_event_context_discard_delayed_snap_event(this);
+ case 2:
+ if (event->button.state & GDK_CONTROL_MASK) {
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+ desktop->canvas->startRotateTo(desktop->namedview->document_rotation);
+ this->rotating_mode = true;
+ this->message_context->set(Inkscape::INFORMATION_MESSAGE,
+ _("<b>MMB + mouse move</b> to rotate canvas, use modifiers on screen to change snaps"));
+ } else {
+ if (event->button.state & GDK_SHIFT_MASK) {
+ zoom_rb = 2;
+ } else {
+ // When starting panning, make sure there are no snap events pending because these might disable the panning again
+ if (_uses_snap) {
+ sp_event_context_discard_delayed_snap_event(this);
+ }
+ panning = 2;
+
+ sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK, NULL,
+ event->button.time - 1);
+ }
+ ret = TRUE;
}
- panning = 3;
+ ret = TRUE;
+ break;
- sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
- GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
- | GDK_POINTER_MOTION_HINT_MASK, NULL,
- event->button.time);
+ case 3:
+ if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) {
+ // When starting panning, make sure there are no snap events pending because these might disable the panning again
+ if (_uses_snap) {
+ sp_event_context_discard_delayed_snap_event(this);
+ }
+ panning = 3;
+
+ sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate),
+ GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK, NULL,
+ event->button.time);
+ ret = TRUE;
+ } else if( !this->space_panning) {
+ sp_event_root_menu_popup(desktop, NULL, event);
+ }
ret = TRUE;
- } else {
- sp_event_root_menu_popup(desktop, NULL, event);
- }
- break;
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ break;
- default:
+ default:
+ break;
+ }
break;
- }
- break;
- case GDK_MOTION_NOTIFY:
- if (panning) {
- if (panning == 4 && !xp && !yp ) {
+ case GDK_MOTION_NOTIFY:
+ if (this->rotating_mode) {
+ button_w = Geom::Point(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt(desktop->doc2dt(desktop->w2d(button_w)));
+ Geom::Rect view = desktop->get_display_area();
+ Geom::Point view_center = desktop->doc2dt(view.midpoint());
+ Geom::Ray center_ray(motion_dt, view_center);
+ angle = Geom::deg_from_rad(center_ray.angle()) - 90;
+ if (event->motion.state & GDK_SHIFT_MASK && event->motion.state & GDK_CONTROL_MASK) {
+ angle = 0;
+ } else if(event->motion.state & GDK_CONTROL_MASK) {
+ angle = floor(angle/rotation_snap) * rotation_snap;
+ } else if (event->motion.state & GDK_SHIFT_MASK) {
+ angle = desktop->namedview->document_rotation;
+ } else if (event->motion.state & GDK_MOD1_MASK) {
+ //Decimal raw angle
+ } else {
+ angle = floor(angle);
+ }
+ desktop->canvas->rotateTo(angle);
+ } else if (panning == 4 && !xp && !yp ) {
// <Space> + mouse panning started, save location and grab canvas
xp = event->motion.x;
yp = event->motion.y;
@@ -431,401 +463,465 @@ bool ToolBase::root_handler(GdkEvent* event) {
| GDK_POINTER_MOTION_HINT_MASK, NULL,
event->motion.time - 1);
}
+ if (panning && !this->rotating_mode) {
+ if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK))
+ || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK))
+ || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) {
+ /* Gdk seems to lose button release for us sometimes :-( */
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+ ret = TRUE;
+ } else {
+ if (within_tolerance && (abs((gint) event->motion.x - xp)
+ < tolerance) && (abs((gint) event->motion.y - yp)
+ < tolerance)) {
+ // do not drag if we're within tolerance from origin
+ break;
+ }
+
+ // Once the user has moved farther than tolerance from
+ // the original location (indicating they intend to move
+ // the object, not click), then always process the motion
+ // notify coordinates as given (no snapping back to origin)
+ within_tolerance = false;
+
+ // gobble subsequent motion events to prevent "sticking"
+ // when scrolling is slow
+ gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning
+ == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK));
+
+ if (panning_cursor == 0) {
+ panning_cursor = 1;
+ this->sp_event_context_set_cursor(GDK_FLEUR);
+ }
+
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const moved_w(motion_w - button_w);
+ this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw
+ ret = TRUE;
+ }
+ } else if (zoom_rb) {
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt(desktop->w2d(motion_w));
- if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK))
- || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK))
- || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) {
- /* Gdk seems to lose button release for us sometimes :-( */
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
- ret = TRUE;
- } else {
if (within_tolerance && (abs((gint) event->motion.x - xp)
< tolerance) && (abs((gint) event->motion.y - yp)
< tolerance)) {
- // do not drag if we're within tolerance from origin
- break;
+ break; // do not drag if we're within tolerance from origin
}
- // Once the user has moved farther than tolerance from
- // the original location (indicating they intend to move
- // the object, not click), then always process the motion
- // notify coordinates as given (no snapping back to origin)
+ // Once the user has moved farther than tolerance from the original location
+ // (indicating they intend to move the object, not click), then always process the
+ // motion notify coordinates as given (no snapping back to origin)
within_tolerance = false;
- // gobble subsequent motion events to prevent "sticking"
- // when scrolling is slow
- gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning
- == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK));
-
- if (panning_cursor == 0) {
- panning_cursor = 1;
- this->sp_event_context_set_cursor(GDK_FLEUR);
+ if (Inkscape::Rubberband::get(desktop)->is_started()) {
+ Inkscape::Rubberband::get(desktop)->move(motion_dt);
+ } else {
+ Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt);
}
- Geom::Point const motion_w(event->motion.x, event->motion.y);
- Geom::Point const moved_w(motion_w - button_w);
- this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw
- ret = TRUE;
- }
- } else if (zoom_rb) {
- Geom::Point const motion_w(event->motion.x, event->motion.y);
- Geom::Point const motion_dt(desktop->w2d(motion_w));
-
- if (within_tolerance && (abs((gint) event->motion.x - xp)
- < tolerance) && (abs((gint) event->motion.y - yp)
- < tolerance)) {
- break; // do not drag if we're within tolerance from origin
+ if (zoom_rb == 2) {
+ gobble_motion_events(GDK_BUTTON2_MASK);
+ }
}
+ break;
- // Once the user has moved farther than tolerance from the original location
- // (indicating they intend to move the object, not click), then always process the
- // motion notify coordinates as given (no snapping back to origin)
- within_tolerance = false;
-
- if (Inkscape::Rubberband::get(desktop)->is_started()) {
- Inkscape::Rubberband::get(desktop)->move(motion_dt);
+ case GDK_BUTTON_RELEASE:
+ if (this->rotating_mode && event->button.button == 2) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ if (desktop->canvas->endRotateTo()) {
+ sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle);
+ }
} else {
- Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt);
- }
+ xp = yp = 0;
+ if (panning_cursor == 1) {
+ panning_cursor = 0;
+ GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
+ gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
+ }
- if (zoom_rb == 2) {
- gobble_motion_events(GDK_BUTTON2_MASK);
- }
- }
- break;
+ if (within_tolerance && (panning || zoom_rb)) {
+ zoom_rb = 0;
- case GDK_BUTTON_RELEASE:
- xp = yp = 0;
+ if (panning) {
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+ event->button.time);
+ }
- if (panning_cursor == 1) {
- panning_cursor = 0;
- GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
- gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
- }
+ Geom::Point const event_w(event->button.x, event->button.y);
+ Geom::Point const event_dt(desktop->w2d(event_w));
- if (within_tolerance && (panning || zoom_rb)) {
- zoom_rb = 0;
+ double const zoom_inc = prefs->getDoubleLimited(
+ "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
- if (panning) {
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
- event->button.time);
- }
+ desktop->zoom_relative_keep_point(event_dt, (event->button.state
+ & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc);
- Geom::Point const event_w(event->button.x, event->button.y);
- Geom::Point const event_dt(desktop->w2d(event_w));
+ desktop->updateNow();
+ ret = TRUE;
+ } else if (panning == event->button.button) {
+ panning = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+ event->button.time);
- double const zoom_inc = prefs->getDoubleLimited(
- "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
+ // in slow complex drawings, some of the motion events are lost;
+ // to make up for this, we scroll it once again to the button-up event coordinates
+ // (i.e. canvas will always get scrolled all the way to the mouse release point,
+ // even if few intermediate steps were visible)
+ Geom::Point const motion_w(event->button.x, event->button.y);
+ Geom::Point const moved_w(motion_w - button_w);
- desktop->zoom_relative_keep_point(event_dt, (event->button.state
- & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc);
+ this->desktop->scroll_world(moved_w);
+ desktop->updateNow();
+ ret = TRUE;
+ } else if (zoom_rb == event->button.button) {
+ zoom_rb = 0;
- desktop->updateNow();
- ret = TRUE;
- } else if (panning == event->button.button) {
- panning = 0;
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
- event->button.time);
-
- // in slow complex drawings, some of the motion events are lost;
- // to make up for this, we scroll it once again to the button-up event coordinates
- // (i.e. canvas will always get scrolled all the way to the mouse release point,
- // even if few intermediate steps were visible)
- Geom::Point const motion_w(event->button.x, event->button.y);
- Geom::Point const moved_w(motion_w - button_w);
-
- this->desktop->scroll_world(moved_w);
- desktop->updateNow();
- ret = TRUE;
- } else if (zoom_rb == event->button.button) {
- zoom_rb = 0;
+ Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
+ Inkscape::Rubberband::get(desktop)->stop();
- Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle();
- Inkscape::Rubberband::get(desktop)->stop();
+ if (b && !within_tolerance) {
+ desktop->set_display_area(*b, 10);
+ }
- if (b && !within_tolerance) {
- desktop->set_display_area(*b, 10);
+ ret = TRUE;
+ }
+ if (this->rotating_mode && event->button.button != 3) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ desktop->canvas->endRotateTo();
+ }
}
+ break;
- ret = TRUE;
- }
- break;
-
- case GDK_KEY_PRESS: {
- double const acceleration = prefs->getDoubleLimited(
- "/options/scrollingacceleration/value", 0, 0, 6);
- int const key_scroll = prefs->getIntLimited("/options/keyscroll/value",
- 10, 0, 1000);
-
- switch (get_group0_keyval(&event->key)) {
- // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets
- // in the editing window). So we resteal them back and run our regular shortcut
- // invoker on them.
- unsigned int shortcut;
- case GDK_KEY_Tab:
- case GDK_KEY_ISO_Left_Tab:
- case GDK_KEY_F1:
- shortcut = get_group0_keyval(&event->key);
-
- if (event->key.state & GDK_SHIFT_MASK) {
- shortcut |= SP_SHORTCUT_SHIFT_MASK;
+ case GDK_KEY_PRESS: {
+ double const acceleration = prefs->getDoubleLimited(
+ "/options/scrollingacceleration/value", 0, 0, 6);
+ int const key_scroll = prefs->getIntLimited("/options/keyscroll/value",
+ 10, 0, 1000);
+
+ if (this->rotating_mode &&
+ get_group0_keyval(&event->key) != GDK_KEY_space &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_R )
+ {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ ret = TRUE;
+ desktop->canvas->endRotateTo();
+ break;
}
- if (event->key.state & GDK_CONTROL_MASK) {
- shortcut |= SP_SHORTCUT_CONTROL_MASK;
- }
+ switch (get_group0_keyval(&event->key)) {
+ // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets
+ // in the editing window). So we resteal them back and run our regular shortcut
+ // invoker on them.
+ unsigned int shortcut;
+ case GDK_KEY_Tab:
+ case GDK_KEY_ISO_Left_Tab:
+ case GDK_KEY_F1:
+ shortcut = get_group0_keyval(&event->key);
+
+ if (event->key.state & GDK_SHIFT_MASK) {
+ shortcut |= SP_SHORTCUT_SHIFT_MASK;
+ }
- if (event->key.state & GDK_MOD1_MASK) {
- shortcut |= SP_SHORTCUT_ALT_MASK;
- }
+ if (event->key.state & GDK_CONTROL_MASK) {
+ shortcut |= SP_SHORTCUT_CONTROL_MASK;
+ }
- ret = sp_shortcut_invoke(shortcut, desktop);
- break;
+ if (event->key.state & GDK_MOD1_MASK) {
+ shortcut |= SP_SHORTCUT_ALT_MASK;
+ }
- case GDK_KEY_Q:
- case GDK_KEY_q:
- if (desktop->quick_zoomed()) {
- ret = TRUE;
- }
- if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) {
- desktop->zoom_quick(true);
- ret = TRUE;
- }
- break;
+ ret = sp_shortcut_invoke(shortcut, desktop);
+ break;
- case GDK_KEY_W:
- case GDK_KEY_w:
- case GDK_KEY_F4:
- /* Close view */
- if (MOD__CTRL_ONLY(event)) {
- sp_ui_close_view(NULL);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Q:
+ case GDK_KEY_q:
+ if (desktop->quick_zoomed()) {
+ ret = TRUE;
+ }
+ if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) {
+ desktop->zoom_quick(true);
+ ret = TRUE;
+ }
+ break;
- case GDK_KEY_Left: // Ctrl Left
- case GDK_KEY_KP_Left:
- case GDK_KEY_KP_4:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ case GDK_KEY_W:
+ case GDK_KEY_w:
+ case GDK_KEY_F4:
+ /* Close view */
+ if (MOD__CTRL_ONLY(event)) {
+ sp_ui_close_view(NULL);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(i, 0);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Left: // Ctrl Left
+ case GDK_KEY_KP_Left:
+ case GDK_KEY_KP_4:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Up: // Ctrl Up
- case GDK_KEY_KP_Up:
- case GDK_KEY_KP_8:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(i, 0);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(0, i);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Up: // Ctrl Up
+ case GDK_KEY_KP_Up:
+ case GDK_KEY_KP_8:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Right: // Ctrl Right
- case GDK_KEY_KP_Right:
- case GDK_KEY_KP_6:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(0, i);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(-i, 0);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Right: // Ctrl Right
+ case GDK_KEY_KP_Right:
+ case GDK_KEY_KP_6:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Down: // Ctrl Down
- case GDK_KEY_KP_Down:
- case GDK_KEY_KP_2:
- if (MOD__CTRL_ONLY(event)) {
- int i = (int) floor(key_scroll * accelerate_scroll(event,
- acceleration, desktop->getCanvas()));
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(-i, 0);
+ ret = TRUE;
+ }
+ break;
- gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
- this->desktop->scroll_world(0, -i);
- ret = TRUE;
- }
- break;
+ case GDK_KEY_Down: // Ctrl Down
+ case GDK_KEY_KP_Down:
+ case GDK_KEY_KP_2:
+ if (MOD__CTRL_ONLY(event)) {
+ int i = (int) floor(key_scroll * accelerate_scroll(event,
+ acceleration, desktop->getCanvas()));
- case GDK_KEY_Menu:
- sp_event_root_menu_popup(desktop, NULL, event);
- ret = TRUE;
- break;
+ gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK);
+ this->desktop->scroll_world(0, -i);
+ ret = TRUE;
+ }
+ break;
- case GDK_KEY_F10:
- if (MOD__SHIFT_ONLY(event)) {
+ case GDK_KEY_Menu:
sp_event_root_menu_popup(desktop, NULL, event);
ret = TRUE;
- }
- break;
+ break;
- case GDK_KEY_space:
- within_tolerance = true;
- xp = yp = 0;
- if (!allow_panning) break;
- panning = 4;
- this->space_panning = true;
- this->message_context->set(Inkscape::INFORMATION_MESSAGE,
- _("<b>Space+mouse move</b> to pan canvas"));
+ case GDK_KEY_F10:
+ if (MOD__SHIFT_ONLY(event)) {
+ sp_event_root_menu_popup(desktop, NULL, event);
+ ret = TRUE;
+ }
+ break;
- ret = TRUE;
+ case GDK_KEY_space:
+// if (event->key.state & GDK_CONTROL_MASK) {
+// sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+// desktop->canvas->startRotateTo(desktop->namedview->document_rotation);
+// this->rotating_mode = true;
+// this->message_context->set(Inkscape::INFORMATION_MESSAGE,
+// _("<b>Space+mouse move</b> to rotate canvas, use modifiers on screen to change snaps"));
+// } else {
+ within_tolerance = true;
+ xp = yp = 0;
+ if (!allow_panning) break;
+ panning = 4;
+ this->space_panning = true;
+ this->message_context->set(Inkscape::INFORMATION_MESSAGE,
+ _("<b>Space+mouse move</b> to pan canvas"));
+// }
+ ret = TRUE;
+ break;
+
+ case GDK_KEY_z:
+ case GDK_KEY_Z:
+ if (MOD__ALT_ONLY(event)) {
+ desktop->zoom_grab_focus();
+ ret = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
break;
- case GDK_KEY_z:
- case GDK_KEY_Z:
- if (MOD__ALT_ONLY(event)) {
- desktop->zoom_grab_focus();
+ case GDK_KEY_RELEASE:
+ if (this->rotating_mode &&
+ get_group0_keyval(&event->key) != GDK_KEY_space &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Shift_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Control_R &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_L &&
+ get_group0_keyval(&event->key) != GDK_KEY_Alt_R )
+ {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
ret = TRUE;
+ desktop->canvas->endRotateTo();
+ break;
}
- break;
- default:
- break;
+ // Stop panning on any key release
+ if (this->space_panning) {
+ this->space_panning = false;
+ this->message_context->clear();
}
- }
- break;
- case GDK_KEY_RELEASE:
- // Stop panning on any key release
- if (this->space_panning) {
- this->space_panning = false;
- this->message_context->clear();
- }
+ if (panning) {
+ panning = 0;
+ xp = yp = 0;
- if (panning) {
- panning = 0;
- xp = yp = 0;
+ sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
+ event->key.time);
- sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate),
- event->key.time);
+ desktop->updateNow();
+ }
- desktop->updateNow();
- }
+ if (panning_cursor == 1) {
+ panning_cursor = 0;
+ GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
+ gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
+ }
- if (panning_cursor == 1) {
- panning_cursor = 0;
- GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas());
- gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor);
- }
+ switch (get_group0_keyval(&event->key)) {
+ case GDK_KEY_space:
+// if (this->rotating_mode) {
+// desktop->canvas->clearRotateTo();
+// this->rotating_mode = false;
+// ret = TRUE;
+// if (desktop->canvas->endRotateTo()) {
+// sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle);
+// }
+// }
+ if (within_tolerance) {
+ // Space was pressed, but not panned
+ sp_toggle_selector(desktop);
+
+ // Be careful, sp_toggle_selector will delete ourselves.
+ // Thus, make sure we return immediately.
+ return true;
+ }
+ break;
- switch (get_group0_keyval(&event->key)) {
- case GDK_KEY_space:
- if (within_tolerance) {
- // Space was pressed, but not panned
- sp_toggle_selector(desktop);
+ case GDK_KEY_Q:
+ case GDK_KEY_q:
+ if (desktop->quick_zoomed()) {
+ desktop->zoom_quick(false);
+ ret = TRUE;
+ }
+ break;
- // Be careful, sp_toggle_selector will delete ourselves.
- // Thus, make sure we return immediately.
- return true;
+ default:
+ break;
}
-
break;
- case GDK_KEY_Q:
- case GDK_KEY_q:
- if (desktop->quick_zoomed()) {
- desktop->zoom_quick(false);
- ret = TRUE;
+ case GDK_SCROLL: {
+ if (this->rotating_mode) {
+ desktop->canvas->clearRotateTo();
+ this->rotating_mode = false;
+ desktop->canvas->endRotateTo();
}
- break;
-
- default:
- break;
- }
- break;
+ bool ctrl = (event->scroll.state & GDK_CONTROL_MASK);
+ bool wheelzooms = prefs->getBool("/options/wheelzooms/value");
- case GDK_SCROLL: {
- bool ctrl = (event->scroll.state & GDK_CONTROL_MASK);
- bool wheelzooms = prefs->getBool("/options/wheelzooms/value");
+ int const wheel_scroll = prefs->getIntLimited(
+ "/options/wheelscroll/value", 40, 0, 1000);
- int const wheel_scroll = prefs->getIntLimited(
- "/options/wheelscroll/value", 40, 0, 1000);
+ // Size of smooth-scrolls (only used in GTK+ 3)
+ gdouble delta_x = 0;
+ gdouble delta_y = 0;
- // Size of smooth-scrolls (only used in GTK+ 3)
- gdouble delta_x = 0;
- gdouble delta_y = 0;
-
- /* shift + wheel, pan left--right */
- if (event->scroll.state & GDK_SHIFT_MASK) {
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- desktop->scroll_world(wheel_scroll, 0);
- break;
+ /* shift + wheel, pan left--right */
+ if (event->scroll.state & GDK_SHIFT_MASK) {
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ desktop->scroll_world(wheel_scroll, 0);
+ break;
- case GDK_SCROLL_DOWN:
- desktop->scroll_world(-wheel_scroll, 0);
- break;
+ case GDK_SCROLL_DOWN:
+ desktop->scroll_world(-wheel_scroll, 0);
+ break;
- default:
- break;
- }
+ default:
+ break;
+ }
- /* ctrl + wheel, zoom in--out */
- } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) {
- double rel_zoom;
- double const zoom_inc = prefs->getDoubleLimited(
- "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
+ /* ctrl + wheel, zoom in--out */
+ } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) {
+ double rel_zoom;
+ double const zoom_inc = prefs->getDoubleLimited(
+ "/options/zoomincrement/value", M_SQRT2, 1.01, 10);
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- rel_zoom = zoom_inc;
- break;
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ rel_zoom = zoom_inc;
+ break;
- case GDK_SCROLL_DOWN:
- rel_zoom = 1 / zoom_inc;
- break;
+ case GDK_SCROLL_DOWN:
+ rel_zoom = 1 / zoom_inc;
+ break;
- default:
- rel_zoom = 0.0;
- break;
- }
+ default:
+ rel_zoom = 0.0;
+ break;
+ }
- if (rel_zoom != 0.0) {
- Geom::Point const scroll_dt = desktop->point();
- desktop->zoom_relative_keep_point(scroll_dt, rel_zoom);
- }
+ if (rel_zoom != 0.0) {
+ Geom::Point const scroll_dt = desktop->point();
+ desktop->zoom_relative_keep_point(scroll_dt, rel_zoom);
+ }
- /* no modifier, pan up--down (left--right on multiwheel mice?) */
- } else {
- switch (event->scroll.direction) {
- case GDK_SCROLL_UP:
- desktop->scroll_world(0, wheel_scroll);
- break;
+ /* no modifier, pan up--down (left--right on multiwheel mice?) */
+ } else {
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ desktop->scroll_world(0, wheel_scroll);
+ break;
- case GDK_SCROLL_DOWN:
- desktop->scroll_world(0, -wheel_scroll);
- break;
+ case GDK_SCROLL_DOWN:
+ desktop->scroll_world(0, -wheel_scroll);
+ break;
- case GDK_SCROLL_LEFT:
- desktop->scroll_world(wheel_scroll, 0);
- break;
+ case GDK_SCROLL_LEFT:
+ desktop->scroll_world(wheel_scroll, 0);
+ break;
- case GDK_SCROLL_RIGHT:
- desktop->scroll_world(-wheel_scroll, 0);
- break;
+ case GDK_SCROLL_RIGHT:
+ desktop->scroll_world(-wheel_scroll, 0);
+ break;
- case GDK_SCROLL_SMOOTH:
- gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
- desktop->scroll_world(delta_x, delta_y);
- break;
+ case GDK_SCROLL_SMOOTH:
+ gdk_event_get_scroll_deltas(event, &delta_x, &delta_y);
+ desktop->scroll_world(delta_x, delta_y);
+ break;
+ }
}
+ break;
}
- break;
- }
- default:
- break;
+ default:
+ break;
}
-
return ret;
}
diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h
index 58eb6f88e..3d22fc66f 100644
--- a/src/ui/tools/tool-base.h
+++ b/src/ui/tools/tool-base.h
@@ -176,6 +176,7 @@ public:
ShapeEditor* shape_editor;
bool space_panning;
+ bool rotating_mode;
DelayedSnapEvent *_delayed_snap_event;
bool _dse_callback_in_process;
diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp
index fa7a83732..0370d55db 100644
--- a/src/ui/widget/selected-style.cpp
+++ b/src/ui/widget/selected-style.cpp
@@ -781,62 +781,7 @@ void SelectedStyle::on_stroke_paste() {
}
void SelectedStyle::on_fillstroke_swap() {
- SPCSSAttr *css = sp_repr_css_attr_new ();
-
- switch (_mode[SS_FILL]) {
- case SS_NA:
- case SS_MANY:
- break;
- case SS_NONE:
- sp_repr_css_set_property (css, "stroke", "none");
- break;
- case SS_UNSET:
- sp_repr_css_unset_property (css, "stroke");
- break;
- case SS_COLOR:
- gchar c[64];
- sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]);
- sp_repr_css_set_property (css, "stroke", c);
- break;
- case SS_LGRADIENT:
- case SS_RGRADIENT:
-#ifdef WITH_MESH
- case SS_MGRADIENT:
-#endif
- case SS_PATTERN:
- sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str());
- break;
- }
-
- switch (_mode[SS_STROKE]) {
- case SS_NA:
- case SS_MANY:
- break;
- case SS_NONE:
- sp_repr_css_set_property (css, "fill", "none");
- break;
- case SS_UNSET:
- sp_repr_css_unset_property (css, "fill");
- break;
- case SS_COLOR:
- gchar c[64];
- sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]);
- sp_repr_css_set_property (css, "fill", c);
- break;
- case SS_LGRADIENT:
- case SS_RGRADIENT:
-#ifdef WITH_MESH
- case SS_MGRADIENT:
-#endif
- case SS_PATTERN:
- sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str());
- break;
- }
-
- sp_desktop_set_style (_desktop, css);
- sp_repr_css_attr_unref (css);
- DocumentUndo::done(_desktop->getDocument(), SP_VERB_DIALOG_FILL_STROKE,
- _("Swap fill and stroke"));
+ _desktop->getSelection()->swapFillStroke();
}
void SelectedStyle::on_fill_edit() {
diff --git a/src/uri-references.cpp b/src/uri-references.cpp
index 170c98beb..7d6abd10a 100644
--- a/src/uri-references.cpp
+++ b/src/uri-references.cpp
@@ -11,16 +11,19 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include "uri-references.h"
+
#include <iostream>
#include <cstring>
+#include <glibmm/miscutils.h>
+
+#include "bad-uri-exception.h"
#include "document.h"
#include "sp-object.h"
#include "uri.h"
-#include "uri-references.h"
#include "extract-uri.h"
#include "sp-tag-use.h"
-#include <glibmm/miscutils.h>
namespace Inkscape {
@@ -103,7 +106,7 @@ bool URIReference::_acceptObject(SPObject *obj) const
-void URIReference::attach(const URI &uri) throw(BadURIException)
+void URIReference::attach(const URI &uri)
{
SPDocument *document = NULL;
diff --git a/src/uri-references.h b/src/uri-references.h
index e56ea0612..ce0f5f2ae 100644
--- a/src/uri-references.h
+++ b/src/uri-references.h
@@ -20,7 +20,6 @@
#include <sigc++/connection.h>
#include <sigc++/trackable.h>
-#include "bad-uri-exception.h"
#include "sp-object.h"
#include "sp-item.h"
#include "sp-use.h"
@@ -66,7 +65,7 @@ public:
* @param rel_document document for relative URIs
* @param uri the URI to watch
*/
- void attach(URI const& uri) throw(BadURIException);
+ void attach(URI const& uri);
/**
* Detaches from the currently attached URI target, if any;
diff --git a/src/uri.cpp b/src/uri.cpp
index f2578b989..9178c8ae8 100644
--- a/src/uri.cpp
+++ b/src/uri.cpp
@@ -8,11 +8,13 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include <glib.h>
#include "uri.h"
+
#include <glibmm/ustring.h>
#include <glibmm/miscutils.h>
+#include "bad-uri-exception.h"
+
namespace Inkscape {
URI::URI() {
@@ -25,7 +27,7 @@ URI::URI(const URI &uri) {
_impl = uri._impl;
}
-URI::URI(gchar const *preformed) throw(BadURIException) {
+URI::URI(gchar const *preformed) {
xmlURIPtr uri;
if (!preformed) {
throw MalformedURIException();
@@ -132,7 +134,7 @@ const gchar *URI::Impl::getOpaque() const {
return (gchar *)_uri->opaque;
}
-gchar *URI::to_native_filename(gchar const* uri) throw(BadURIException)
+gchar *URI::to_native_filename(gchar const* uri)
{
gchar *filename = NULL;
URI tmp(uri);
@@ -166,7 +168,7 @@ const std::string URI::getFullPath(std::string const &base) const {
/* TODO !!! proper error handling */
-gchar *URI::toNativeFilename() const throw(BadURIException) {
+gchar *URI::toNativeFilename() const {
gchar *uriString = toString();
if (isRelativePath()) {
return uriString;
@@ -181,7 +183,7 @@ gchar *URI::toNativeFilename() const throw(BadURIException) {
}
}
-URI URI::fromUtf8( gchar const* path ) throw (BadURIException) {
+URI URI::fromUtf8( gchar const* path ) {
if ( !path ) {
throw MalformedURIException();
}
@@ -215,7 +217,7 @@ URI URI::fromUtf8( gchar const* path ) throw (BadURIException) {
}
/* TODO !!! proper error handling */
-URI URI::from_native_filename(gchar const *path) throw(BadURIException) {
+URI URI::from_native_filename(gchar const *path) {
gchar *uri = g_filename_to_uri(path, NULL, NULL);
URI result(uri);
g_free( uri );
diff --git a/src/uri.h b/src/uri.h
index bdf5f1baa..7b57e1ae9 100644
--- a/src/uri.h
+++ b/src/uri.h
@@ -13,7 +13,6 @@
#include <exception>
#include <libxml/uri.h>
-#include "bad-uri-exception.h"
#include <string>
namespace Inkscape {
@@ -37,7 +36,7 @@ public:
*
* @param preformed Properly quoted C-style string to be represented.
*/
- explicit URI(char const *preformed) throw(BadURIException);
+ explicit URI(char const *preformed);
/**
* Destructor.
@@ -98,15 +97,15 @@ public:
const char *getOpaque() const { return _impl->getOpaque(); }
- static URI fromUtf8( char const* path ) throw (BadURIException);
+ static URI fromUtf8( char const* path );
- static URI from_native_filename(char const *path) throw(BadURIException);
+ static URI from_native_filename(char const *path);
- static char *to_native_filename(char const* uri) throw(BadURIException);
+ static char *to_native_filename(char const* uri);
const std::string getFullPath(std::string const &base) const;
- char *toNativeFilename() const throw(BadURIException);
+ char *toNativeFilename() const;
/**
* Returns a glib string version of this URI.
diff --git a/src/verbs.cpp b/src/verbs.cpp
index 975a3679e..fce823408 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -1086,6 +1086,9 @@ void EditVerb::perform(SPAction *action, void *data)
case SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER:
sp_selection_next_patheffect_param(dt);
break;
+ case SP_VERB_EDIT_SWAP_FILL_STROKE:
+ dt->selection->swapFillStroke();
+ break;
case SP_VERB_EDIT_LINK_COLOR_PROFILE:
break;
case SP_VERB_EDIT_REMOVE_COLOR_PROFILE:
@@ -2154,6 +2157,12 @@ void DialogVerb::perform(SPAction *action, void *data)
case SP_VERB_DIALOG_PRINT_COLORS_PREVIEW:
dt->_dlg_mgr->showDialog("PrintColorsPreviewDialog");
break;
+ case SP_VERB_DIALOG_STYLE:
+ dt->_dlg_mgr->showDialog("StyleDialog");
+ break;
+ case SP_VERB_DIALOG_CSS:
+ dt->_dlg_mgr->showDialog("CssDialog");
+ break;
default:
break;
@@ -2585,6 +2594,8 @@ Verb *Verb::_base_verbs[] = {
N_("Create four guides aligned with the page borders"), NULL),
new EditVerb(SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER, "EditNextPathEffectParameter", N_("Next path effect parameter"),
N_("Show next editable path effect parameter"), INKSCAPE_ICON("path-effect-parameter-next")),
+ new EditVerb(SP_VERB_EDIT_SWAP_FILL_STROKE, "EditSwapFillStroke", N_("Swap fill and stroke"),
+ N_("Swap fill and stroke of an object"), NULL),
// Selection
new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"),
@@ -3000,6 +3011,10 @@ Verb *Verb::_base_verbs[] = {
N_("View Objects"), INKSCAPE_ICON("dialog-layers")),
new DialogVerb(SP_VERB_DIALOG_TAGS, "DialogTags", N_("Selection se_ts..."),
N_("View Tags"), INKSCAPE_ICON("edit-select-all-layers")),
+ new DialogVerb(SP_VERB_DIALOG_STYLE, "DialogStyle", N_("Style Dialog..."),
+ N_("View Style Dialog"), NULL),
+ new DialogVerb(SP_VERB_DIALOG_CSS, "DialogCss", N_("Css Dialog..."),
+ N_("View Css Dialog"), NULL),
new DialogVerb(SP_VERB_DIALOG_LIVE_PATH_EFFECT, "DialogLivePathEffect", N_("Path E_ffects ..."),
N_("Manage, edit, and apply path effects"), INKSCAPE_ICON("dialog-path-effects")),
new DialogVerb(SP_VERB_DIALOG_FILTER_EFFECTS, "DialogFilterEffects", N_("Filter _Editor..."),
diff --git a/src/verbs.h b/src/verbs.h
index d7e966ae4..21e9b5547 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -110,6 +110,7 @@ enum {
SP_VERB_EDIT_GUIDES_TOGGLE_LOCK,
SP_VERB_EDIT_GUIDES_AROUND_PAGE,
SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER,
+ SP_VERB_EDIT_SWAP_FILL_STROKE,
/* Selection */
SP_VERB_SELECTION_TO_FRONT,
SP_VERB_SELECTION_TO_BACK,
@@ -316,6 +317,8 @@ enum {
SP_VERB_DIALOG_LAYERS,
SP_VERB_DIALOG_OBJECTS,
SP_VERB_DIALOG_TAGS,
+ SP_VERB_DIALOG_STYLE,
+ SP_VERB_DIALOG_CSS,
SP_VERB_DIALOG_LIVE_PATH_EFFECT,
SP_VERB_DIALOG_FILTER_EFFECTS,
SP_VERB_DIALOG_SVG_FONTS,
diff --git a/src/viewbox.cpp b/src/viewbox.cpp
index 1b50fe71c..ebfc04d8a 100644
--- a/src/viewbox.cpp
+++ b/src/viewbox.cpp
@@ -17,6 +17,8 @@
#include "viewbox.h"
#include "enums.h"
#include "sp-item.h"
+#include "inkscape.h"
+#include "desktop.h"
SPViewBox::SPViewBox()
: viewBox_set(false)
@@ -25,6 +27,11 @@ SPViewBox::SPViewBox()
, aspect_align(SP_ASPECT_XMID_YMID) // Default per spec
, aspect_clip(SP_ASPECT_MEET)
, c2p(Geom::identity())
+ , vbt(Geom::identity())
+ , rotation(Geom::identity())
+ , angle(0)
+ , previous_angle(0)
+ , rotated(false)
{
}
@@ -159,6 +166,16 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) {
}
}
+double SPViewBox::get_rotation() {
+ return this->angle;
+}
+
+void SPViewBox::set_rotation(double angle_val) {
+ this->previous_angle = this->angle;
+ this->angle = angle_val;
+ this->rotated = true;
+}
+
// Apply scaling from viewbox
void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) {
@@ -222,22 +239,41 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) {
break;
}
}
-
/* Viewbox transform from scale and position */
- Geom::Affine q;
- q[0] = scale_x;
- q[1] = 0.0;
- q[2] = 0.0;
- q[3] = scale_y;
- q[4] = x - scale_x * this->viewBox.left();
- q[5] = y - scale_y * this->viewBox.top();
-
- // std::cout << " q\n" << q << std::endl;
-
- /* Append viewbox transformation */
- this->c2p = q * this->c2p;
+ vbt = Geom::identity();
+ vbt[0] = scale_x;
+ vbt[1] = 0.0;
+ vbt[2] = 0.0;
+ vbt[3] = scale_y;
+ vbt[4] = x - scale_x * this->viewBox.left();
+ vbt[5] = y - scale_y * this->viewBox.top();
+ /* Append viewbox and turn transformation */
+ Geom::Point page_center = this->viewBox.midpoint();
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (this->angle > 0.0 || this->angle < 0.0 ) { //!0
+ if (desktop) {
+ rotation = Geom::Translate(page_center).inverse() * Geom::Rotate(Geom::rad_from_deg(angle)) * Geom::Translate(page_center);
+ this->c2p = rotation * vbt * this->c2p;
+ } else {
+ this->c2p = vbt * this->c2p;
+ }
+ } else {
+ this->c2p = vbt * this->c2p;
+ }
+ if (desktop && this->rotated) {
+ Geom::Rect view = desktop->get_display_area();
+ Geom::Point view_center = desktop->doc2dt(view.midpoint());
+ Geom::Affine center_rotation = Geom::identity();
+ center_rotation *= Geom::Translate(page_center * vbt).inverse();
+ center_rotation *= Geom::Rotate(Geom::rad_from_deg(this->angle - this->previous_angle));
+ center_rotation *= Geom::Translate(page_center * vbt);
+ view_center = desktop->dt2doc(view_center * center_rotation);
+ desktop->zoom_relative(view_center[Geom::X], view_center[Geom::Y], 1.0);
+ this->rotated = false;
+ }
}
+
SPItemCtx SPViewBox::get_rctx(const SPItemCtx* ictx, double scale_none) {
/* Create copy of item context */
diff --git a/src/viewbox.h b/src/viewbox.h
index c71abb610..2f107132c 100644
--- a/src/viewbox.h
+++ b/src/viewbox.h
@@ -36,7 +36,14 @@ public:
/* Child to parent additional transform */
Geom::Affine c2p;
+ Geom::Affine vbt;
+ Geom::Affine rotation;
+ double angle;
+ double previous_angle;
+ bool rotated;
+ double get_rotation();
+ void set_rotation(double angle_val);
void set_viewBox(const gchar* value);
void set_preserveAspectRatio(const gchar* value);
diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp
index 6b8ade0fb..682e4e103 100644
--- a/src/widgets/desktop-widget.cpp
+++ b/src/widgets/desktop-widget.cpp
@@ -54,7 +54,7 @@
#include "ui/uxmanager.h"
#include "util/ege-appear-time-tracker.h"
#include "sp-root.h"
-
+#include "attributes.h"
// We're in the "widgets" directory, so no need to explicitly prefix these:
#include "button.h"
#include "gimp/ruler.h"
@@ -62,16 +62,11 @@
#include "spw-utilities.h"
#include "toolbox.h"
#include "widget-sizes.h"
-
#include "verbs.h"
#include <gtkmm/cssprovider.h>
#include <gtkmm/paned.h>
#include <gtkmm/messagedialog.h>
-
-#if defined (SOLARIS) && (SOLARIS == 8)
-#include "round.h"
-using Inkscape::round;
-#endif
+#include <iomanip>
using Inkscape::UI::Widget::UnitTracker;
using Inkscape::UI::UXManager;
@@ -97,8 +92,20 @@ static void sp_update_guides_lock( GtkWidget *button, gpointer data );
static void cms_adjust_toggled( GtkWidget *button, gpointer data );
#endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2)
static void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled );
+static void sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw);
static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw);
+static gint sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer data);
+static bool sp_dtw_rotation_output (GtkSpinButton *spin, gpointer data);
+static void sp_dtw_rotation_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data);
+static void sp_dtw_rotate_minus_180 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_minus_135 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_minus_90 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_minus_45 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_0 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_45 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_90 (GtkMenuItem *item, SPDesktopWidget * data);
+static void sp_dtw_rotate_135 (GtkMenuItem *item, SPDesktopWidget * data);
static gdouble sp_dtw_zoom_value_to_display (gdouble value);
static gdouble sp_dtw_zoom_display_to_value (gdouble value);
static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data);
@@ -585,6 +592,34 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw )
g_signal_connect (G_OBJECT (dtw->zoom_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->zoom_status);
dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
+ auto css_provider_spinbutton = Gtk::CssProvider::create();
+ css_provider_spinbutton->load_from_data("* { padding-left: 2px; padding-right: 2px; padding-top: 0px; padding-bottom: 0px;}");
+ auto zoomstat = Glib::wrap(dtw->zoom_status);
+ zoomstat->set_name("ZoomStatus");
+ auto context_zoom = zoomstat->get_style_context();
+ context_zoom->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+ // Rotate status spinbutton
+ dtw->rotation_status = gtk_spin_button_new_with_range (-360.0,360.0, 1.0);
+ gtk_widget_set_tooltip_text (dtw->rotation_status, _("Rotation. Can be interactive with CTRL+MMB"));
+ gtk_widget_set_size_request (dtw->rotation_status, STATUS_ROTATION_WIDTH, -1);
+ gtk_entry_set_width_chars (GTK_ENTRY (dtw->rotation_status), 7);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dtw->rotation_status), FALSE);
+ gtk_spin_button_set_digits (GTK_SPIN_BUTTON (dtw->rotation_status), 2);
+ gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dtw->rotation_status), GTK_UPDATE_ALWAYS);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "input", G_CALLBACK (sp_dtw_rotation_input), dtw);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "output", G_CALLBACK (sp_dtw_rotation_output), dtw);
+ g_object_set_data (G_OBJECT (dtw->rotation_status), "dtw", dtw->canvas);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "focus-in-event", G_CALLBACK (spinbutton_focus_in), dtw->rotation_status);
+ g_signal_connect (G_OBJECT (dtw->rotation_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->rotation_status);
+ dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "value_changed", G_CALLBACK (sp_desktop_widget_rotate_document), dtw);
+ dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "populate_popup", G_CALLBACK (sp_dtw_rotation_populate_popup), dtw);
+
+ auto rotstat = Glib::wrap(dtw->rotation_status);
+ rotstat->set_name("RotationStatus");
+ auto context_rotation = rotstat->get_style_context();
+ context_rotation->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
// Cursor coordinates
dtw->coord_status = gtk_grid_new();
@@ -611,12 +646,16 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw )
auto label_z = gtk_label_new(_("Z:"));
gtk_widget_set_name(label_z, "ZLabel");
+ auto label_r = gtk_label_new(_("R:"));
+ gtk_widget_set_name(label_r, "RLabel");
gtk_widget_set_halign(dtw->coord_status_x, GTK_ALIGN_END);
gtk_widget_set_halign(dtw->coord_status_y, GTK_ALIGN_END);
gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_x, 2, 0, 1, 1);
gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_y, 2, 1, 1, 1);
gtk_grid_attach(GTK_GRID(dtw->coord_status), label_z, 3, 0, 1, 2);
+ gtk_grid_attach(GTK_GRID(dtw->coord_status), label_r, 5, 0, 1, 2);
gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->zoom_status, 4, 0, 1, 2);
+ gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->rotation_status, 6, 0, 1, 2);
sp_set_font_size_smaller (dtw->coord_status);
@@ -684,6 +723,11 @@ static void sp_desktop_widget_dispose(GObject *object)
g_signal_handlers_disconnect_matched (G_OBJECT (dtw->zoom_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->zoom_status);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_value_changed), dtw);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_populate_popup), dtw);
+ g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_input), dtw);
+ g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_output), dtw);
+ g_signal_handlers_disconnect_matched (G_OBJECT (dtw->rotation_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->rotation_status);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_desktop_widget_rotate_document), dtw);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_dtw_rotation_populate_popup), dtw);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas), (gpointer) G_CALLBACK (sp_desktop_widget_event), dtw);
g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas_tbl), (gpointer) G_CALLBACK (canvas_tbl_size_allocate), dtw);
@@ -1415,7 +1459,7 @@ void SPDesktopWidget::layoutWidgets()
} else {
gtk_widget_show_all (dtw->menubar);
}
-
+
if (!prefs->getBool(pref_root + "commands/state", true)) {
gtk_widget_hide (dtw->commands_toolbox);
} else {
@@ -1621,10 +1665,11 @@ SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview)
dtw->menubar = sp_ui_main_menubar (dtw->desktop);
gtk_widget_set_name(dtw->menubar, "MenuBar");
gtk_widget_show_all (dtw->menubar);
+ SPNamedView *nv = dtw->desktop->namedview;
gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0);
-
dtw->layoutWidgets();
-
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON (dtw->rotation_status), namedview->document_rotation);
+ sp_namedview_set_document_rotation(namedview);
std::vector<GtkWidget *> toolboxes;
toolboxes.push_back(dtw->tool_toolbox);
toolboxes.push_back(dtw->aux_toolbox);
@@ -1664,6 +1709,8 @@ sp_desktop_widget_update_rulers (SPDesktopWidget *dtw)
void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags)
{
SPNamedView *nv=SP_NAMEDVIEW(obj);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(this->rotation_status), desktop->namedview->document_rotation);
+ sp_namedview_set_document_rotation(nv);
if (flags & SP_OBJECT_MODIFIED_FLAG) {
this->dt2r = 1. / nv->display_units->factor;
@@ -1689,9 +1736,16 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags)
if (GTK_IS_CONTAINER(i->data)) {
GList *grch = gtk_container_get_children (GTK_CONTAINER(i->data));
for (GList *j = grch; j != NULL; j = j->next) {
+
if (!GTK_IS_WIDGET(j->data)) // wasn't a widget
continue;
+ // Don't apply to text toolbar. We want to be able to
+ // use different units for text. (Bug 1562217)
+ const gchar* name = gtk_widget_get_name( (GTK_WIDGET(j->data)) );
+ if (strcmp( name, "TextToolbar") == 0)
+ continue;
+
gpointer t = sp_search_by_data_recursive(GTK_WIDGET(j->data), (gpointer) "tracker");
if (t == NULL) // didn't find any tracker data
continue;
@@ -1715,6 +1769,18 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags)
}
static void
+sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw)
+{
+ SPNamedView *nv = dtw->desktop->namedview;
+ double value = gtk_spin_button_get_value (spin);
+ if (!dtw->desktop->getDocument()->getRoot()->rotated && value != nv->document_rotation) {
+ sp_repr_set_svg_double(nv->getRepr(), "inkscape:document-rotation", value);
+ }
+ spinbutton_defocus (GTK_WIDGET(spin));
+}
+
+
+static void
sp_desktop_widget_adjustment_value_changed (GtkAdjustment */*adj*/, SPDesktopWidget *dtw)
{
if (dtw->update)
@@ -1794,6 +1860,34 @@ sp_dtw_zoom_output (GtkSpinButton *spin, gpointer /*data*/)
return TRUE;
}
+static gint
+sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/)
+{
+ gdouble new_scrolled = gtk_spin_button_get_value (spin);
+ const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin));
+ gdouble new_typed = atof (b);
+
+ if (new_scrolled == new_typed) { // the new value is set by scrolling
+ *new_val = new_scrolled;
+ } else { // the new value is typed in
+ *new_val = new_typed;
+ }
+
+ return TRUE;
+}
+
+static bool
+sp_dtw_rotation_output (GtkSpinButton *spin, gpointer /*data*/)
+{
+ gchar b[64];
+ double val = gtk_spin_button_get_value (spin);
+ std::ostringstream s;
+ s.imbue(std::locale(""));;
+ s << std::fixed << std::setprecision(2) << val << "º";
+ gtk_entry_set_text (GTK_ENTRY (spin), s.str().c_str());
+ return TRUE;
+}
+
static void
sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data)
{
@@ -1932,6 +2026,110 @@ sp_dtw_zoom_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data)
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
}
+
+static void
+sp_dtw_rotation_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data)
+{
+ GList *children, *iter;
+ GtkWidget *item;
+ SPDesktopWidget *dtw = static_cast<SPDesktopWidget*>(data);
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ for ( iter = children ; iter ; iter = g_list_next (iter)) {
+ gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (iter->data));
+ }
+ g_list_free (children);
+
+ item = gtk_menu_item_new_with_label ("-180º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_180), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("-135º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_135), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("-90º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_90), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("-45º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_45), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("0º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_0), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+ item = gtk_menu_item_new_with_label ("45º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_45), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+
+ item = gtk_menu_item_new_with_label ("90º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_90), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+
+
+ item = gtk_menu_item_new_with_label ("135º");
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_135), dtw);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+}
+
+static void
+sp_dtw_rotate_minus_180 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status),-180);
+}
+
+static void
+sp_dtw_rotate_minus_135 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -135);
+}
+
+static void
+sp_dtw_rotate_minus_90 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -90);
+}
+
+static void
+sp_dtw_rotate_minus_45 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -45);
+}
+
+static void
+sp_dtw_rotate_0 (GtkMenuItem */*item*/,SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 0);
+}
+
+static void
+sp_dtw_rotate_45 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 45);
+}
+
+static void
+sp_dtw_rotate_90 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 90);
+}
+
+static void
+sp_dtw_rotate_135 (GtkMenuItem */*item*/, SPDesktopWidget * data)
+{
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 135);
+}
+
static void
sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor)
{
diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h
index 08966ad5f..61c3b8b37 100644
--- a/src/widgets/desktop-widget.h
+++ b/src/widgets/desktop-widget.h
@@ -78,7 +78,7 @@ struct SPDesktopWidget {
GtkWidget *hbox;
- GtkWidget *menubar, *statusbar;
+ GtkWidget *menubar, *statusbar, *rotatebar;
Inkscape::UI::Dialogs::SwatchesPanel *panels;
@@ -97,7 +97,9 @@ struct SPDesktopWidget {
GtkWidget *select_status;
GtkWidget *select_status_eventbox;
GtkWidget *zoom_status;
+ GtkWidget *rotation_status;
gulong zoom_update;
+ gulong rotation_update;
Inkscape::UI::Widget::Dock *dock;
diff --git a/src/widgets/gimp/ruler.cpp b/src/widgets/gimp/ruler.cpp
index 2a71b5c08..6a1f7f903 100644
--- a/src/widgets/gimp/ruler.cpp
+++ b/src/widgets/gimp/ruler.cpp
@@ -34,11 +34,10 @@
#include <cstdio>
#include "ruler.h"
-#include "round.h"
#include <glibmm/i18n.h>
#include "util/units.h"
-#define ROUND(x) ((int) ((x) + 0.5))
+#define ROUND(x) ((int) round(x))
#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
@@ -1217,7 +1216,7 @@ sp_ruler_draw_ticks (SPRuler *ruler)
// be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
// and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
// by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-))
- pos = gint(Inkscape::round((cur - lower) * increment + 1e-12));
+ pos = gint(round((cur - lower) * increment + 1e-12));
if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
{
diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp
index 1efa8f06b..885deaab7 100644
--- a/src/widgets/icon.cpp
+++ b/src/widgets/icon.cpp
@@ -35,6 +35,7 @@
#include "display/drawing.h"
#include "io/sys.h"
#include "sp-root.h"
+#include "sp-namedview.h"
#include "util/units.h"
#include "icon.h"
@@ -1163,6 +1164,19 @@ sp_icon_doc_icon( SPDocument *doc, Inkscape::Drawing &drawing,
CAIRO_FORMAT_ARGB32, psize, psize, stride);
Inkscape::DrawingContext dc(s, ua.min());
+ SPNamedView *nv = sp_document_namedview(doc, NULL);
+ float bg_r = SP_RGBA32_R_F(nv->pagecolor);
+ float bg_g = SP_RGBA32_G_F(nv->pagecolor);
+ float bg_b = SP_RGBA32_B_F(nv->pagecolor);
+ float bg_a = SP_RGBA32_A_F(nv->pagecolor);
+
+ cairo_t *cr = cairo_create(s);
+ cairo_set_source_rgba(cr, bg_r, bg_g, bg_b, bg_a);
+ cairo_rectangle(cr, 0, 0, psize, psize);
+ cairo_fill(cr);
+ cairo_save(cr);
+ cairo_destroy(cr);
+
drawing.render(dc, ua);
cairo_surface_destroy(s);
diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp
index ba79517d2..24665be37 100644
--- a/src/widgets/text-toolbar.cpp
+++ b/src/widgets/text-toolbar.cpp
@@ -301,10 +301,23 @@ static void sp_text_fontstyle_value_changed( Ink_ComboBoxEntry_Action *act, GObj
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
sp_desktop_set_style (desktop, css, true, true);
+
+
+ // If no selected objects, set default.
+ SPStyle query(SP_ACTIVE_DOCUMENT);
+ int result_style =
+ sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTSTYLE);
+ if (result_style == QUERY_STYLE_NOTHING) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->mergeStyle("/tools/text/style", css);
+ } else {
+ // Save for undo
+ DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT,
+ _("Text: Change font style"));
+ }
+
sp_repr_css_attr_unref (css);
- DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT,
- _("Text: Change font style"));
}
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
@@ -588,17 +601,14 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl )
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- // Only save if not relative unit
- if ( !is_relative(unit) ) {
- // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit so
- // we can save it (allows us to adjust line height value when unit changes).
- SPILength temp_length;
- Inkscape::CSSOStringStream temp_stream;
- temp_stream << 1 << unit->abbr;
- temp_length.read(temp_stream.str().c_str());
- prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit);
- g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit));
- }
+ // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit so
+ // we can save it (allows us to adjust line height value when unit changes).
+ SPILength temp_length;
+ Inkscape::CSSOStringStream temp_stream;
+ temp_stream << 1 << unit->abbr;
+ temp_length.read(temp_stream.str().c_str());
+ prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit);
+ g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit));
// Set css line height.
SPCSSAttr *css = sp_repr_css_attr_new ();
@@ -701,16 +711,13 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl )
g_return_if_fail(unit != NULL);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- // Only save if not relative unit
- if ( !is_relative(unit) ) {
- // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit.
- SPILength temp_length;
- Inkscape::CSSOStringStream temp_stream;
- temp_stream << 1 << unit->abbr;
- temp_length.read(temp_stream.str().c_str());
- prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit);
- g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit));
- }
+ // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit.
+ SPILength temp_length;
+ Inkscape::CSSOStringStream temp_stream;
+ temp_stream << 1 << unit->abbr;
+ temp_length.read(temp_stream.str().c_str());
+ prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit);
+ g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit));
// Read current line height value
EgeAdjustmentAction *line_height_act =
@@ -723,7 +730,10 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl )
auto itemlist = selection->items();
// Convert between units
- if ((unit->abbr == "" || unit->abbr == "em") && old_unit == SP_CSS_UNIT_EX) {
+ if ((unit->abbr == "" || unit->abbr == "em") &&
+ (old_unit == SP_CSS_UNIT_NONE || old_unit == SP_CSS_UNIT_EM)) {
+ // Do nothing
+ } else if ((unit->abbr == "" || unit->abbr == "em") && old_unit == SP_CSS_UNIT_EX) {
line_height *= 0.5;
} else if ((unit->abbr) == "ex" && (old_unit == SP_CSS_UNIT_EM || old_unit == SP_CSS_UNIT_NONE) ) {
line_height *= 2.0;
@@ -1130,6 +1140,54 @@ static void sp_text_orientation_changed( EgeSelectOneAction *act, GObject *tbl )
g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
}
+static void sp_text_direction_changed( EgeSelectOneAction *act, GObject *tbl )
+{
+ // quit if run by the _changed callbacks
+ if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
+ return;
+ }
+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
+
+ int mode = ege_select_one_action_get_active( act );
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ switch (mode)
+ {
+ case 0:
+ {
+ sp_repr_css_set_property (css, "direction", "ltr");
+ break;
+ }
+
+ case 1:
+ {
+ sp_repr_css_set_property (css, "direction", "rtl");
+ break;
+ }
+ }
+
+ SPStyle query(SP_ACTIVE_DOCUMENT);
+ int result_numbers =
+ sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
+
+ // If querying returned nothing, update default style.
+ if (result_numbers == QUERY_STYLE_NOTHING)
+ {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->mergeStyle("/tools/text/style", css);
+ }
+
+ sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
+ if(result_numbers != QUERY_STYLE_NOTHING)
+ {
+ DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_TEXT,
+ _("Text: Change direction"));
+ }
+ sp_repr_css_attr_unref (css);
+
+ g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
+}
+
/*
* Set the default list of font sizes, scaled to the users preferred unit
*/
@@ -1170,8 +1228,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/
std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
std::cout << "sp_text_toolbox_selection_changed: start " << count << std::endl;
- SPDesktop *desktop = SP_ACTIVE_DESKTOP;
- Inkscape::Selection *selection = desktop->getSelection();
+ Inkscape::Selection *selection = (SP_ACTIVE_DESKTOP)->getSelection();
auto itemlist0= selection->items();
for(auto i=itemlist0.begin();i!=itemlist0.end(); ++i) {
const gchar* id = (*i)->getId();
@@ -1494,6 +1551,15 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/
ege_select_one_action_update_sensitive( textOrientationAction );
+ // Direction
+ int activeButton4 = 0;
+ if (query.direction.computed == SP_CSS_DIRECTION_LTR ) activeButton4 = 0;
+ if (query.direction.computed == SP_CSS_DIRECTION_RTL ) activeButton4 = 1;
+
+ EgeSelectOneAction* textDirectionAction =
+ EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextDirectionAction" ) );
+ ege_select_one_action_set_active( textDirectionAction, activeButton4 );
+
}
#ifdef DEBUG_TEXT
@@ -1926,6 +1992,52 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje
g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder );
}
+
+ // Text direction (predominant direction of horizontal text).
+ {
+ GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
+
+ GtkTreeIter iter;
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("LTR"),
+ 1, _("Left to right text"),
+ 2, INKSCAPE_ICON("format-text-direction-horizontal"),
+ 3, true,
+ -1 );
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("RTL"),
+ 1, _("Right to left text"),
+ 2, INKSCAPE_ICON("format-text-direction-r2l"),
+ 3, true,
+ -1 );
+
+ EgeSelectOneAction* act = ege_select_one_action_new( "TextDirectionAction", // Name
+ _("Text direction"), // Label
+ _("Text direction for normally horizontal text."), // Tooltip
+ NULL, // Icon name
+ GTK_TREE_MODEL(model) ); // Model
+
+ g_object_set( act, "short_label", "NotUsed", NULL );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
+ g_object_set_data( holder, "TextDirectAction", act );
+
+ ege_select_one_action_set_appearance( act, "full" );
+ ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
+ g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
+ ege_select_one_action_set_icon_column( act, 2 );
+ ege_select_one_action_set_icon_size( act, secondarySize );
+ ege_select_one_action_set_tooltip_column( act, 1 );
+ ege_select_one_action_set_sensitive_column( act, 3 );
+
+ gint mode = prefs->getInt("/tools/text/text_direction", 0);
+ ege_select_one_action_set_active( act, mode );
+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_direction_changed), holder );
+ }
+
/* Line height unit tracker */
UnitTracker* tracker = new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR);
tracker->prependUnit(unit_table.getUnit("")); // No unit
diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp
index 126eac9cd..016eafdaa 100644
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
@@ -204,6 +204,7 @@ static struct {
SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
{ "/tools/lpetool", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
+ // If you change TextToolbar here, change it also in desktop-widget.cpp
{ "/tools/text", "text_toolbox", 0, sp_text_toolbox_prep, "TextToolbar",
SP_VERB_INVALID, 0, 0},
{ "/tools/dropper", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
@@ -529,6 +530,8 @@ static gchar const * ui_descr =
" <toolitem action='TextWritingModeAction' />"
" <separator />"
" <toolitem action='TextOrientationAction' />"
+ " <separator />"
+ " <toolitem action='TextDirectionAction' />"
" </toolbar>"
" <toolbar name='LPEToolToolbar'>"
@@ -1428,7 +1431,7 @@ void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
auto kludge = dataHolders[aux_toolboxes[i].type_name];
auto holder = gtk_grid_new();
- gtk_widget_set_name( holder, "ToolbarHolder" );
+ gtk_widget_set_name( holder, aux_toolboxes[i].ui_name );
gtk_grid_attach( GTK_GRID(holder), kludge, 2, 0, 1, 1);
gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
diff --git a/src/widgets/widget-sizes.h b/src/widgets/widget-sizes.h
index 87c7ca2e0..186cf4730 100644
--- a/src/widgets/widget-sizes.h
+++ b/src/widgets/widget-sizes.h
@@ -28,6 +28,7 @@
#define STATUS_BAR_FONT_SIZE 10000
#define STATUS_ZOOM_WIDTH 57
+#define STATUS_ROTATION_WIDTH 57
#define SELECTED_STYLE_SB_WIDTH 48
#define SELECTED_STYLE_WIDTH 190
diff --git a/src/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp
index 7e3d4fa7e..a34df09a5 100644
--- a/src/xml/rebase-hrefs.cpp
+++ b/src/xml/rebase-hrefs.cpp
@@ -276,7 +276,7 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b
#ifdef WIN32
/* Native Windows path separators are replaced with / so that the href
* also works on Gnu/Linux and OSX */
- std::replace(href.begin(), href.end(), '\\', '/');
+ std::replace(new_href.begin(), new_href.end(), '\\', '/');
#endif
ir->setAttribute("xlink:href", new_href.c_str());
} else {