summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2017-01-25 09:46:27 +0000
committerjabiertxof <info@marker.es>2017-01-25 09:46:27 +0000
commit59ed5cf5fae750ea136fe8f5a37df23b541f80d5 (patch)
tree08fd8c0ac7d70c22fe8e2c1edc960235e2710010 /src
parentUpdate to trunk (diff)
parentComented one of the shortcuts for doc rotate because bad response now interac... (diff)
downloadinkscape-59ed5cf5fae750ea136fe8f5a37df23b541f80d5.tar.gz
inkscape-59ed5cf5fae750ea136fe8f5a37df23b541f80d5.zip
update to trunk
(bzr r13645.1.168)
Diffstat (limited to 'src')
-rw-r--r--src/2geom/intersection-graph.cpp6
-rw-r--r--src/attributes.cpp1
-rw-r--r--src/attributes.h1
-rw-r--r--src/desktop-events.cpp4
-rw-r--r--src/display/canvas-temporary-item-list.h1
-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.cpp6
-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-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.cpp6
-rw-r--r--src/helper/geom.cpp7
-rw-r--r--src/helper/geom.h2
-rw-r--r--src/live_effects/CMakeLists.txt6
-rw-r--r--src/live_effects/effect.cpp95
-rw-r--r--src/live_effects/effect.h20
-rw-r--r--src/live_effects/lpe-clone-original.cpp331
-rw-r--r--src/live_effects/lpe-clone-original.h34
-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-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/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.cpp246
-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/text.cpp3
-rw-r--r--src/print.cpp2
-rw-r--r--src/sp-item-group.cpp43
-rw-r--r--src/sp-lpe-item.cpp35
-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.cpp15
-rw-r--r--src/sp-object.h3
-rw-r--r--src/ui/dialog/export.cpp9
-rw-r--r--src/ui/dialog/polar-arrange-tab.cpp4
-rw-r--r--src/ui/tool/transform-handle-set.cpp5
-rw-r--r--src/ui/tools/tool-base.cpp862
-rw-r--r--src/ui/tools/tool-base.h1
-rw-r--r--src/viewbox.cpp62
-rw-r--r--src/viewbox.h7
-rw-r--r--src/widgets/desktop-widget.cpp208
-rw-r--r--src/widgets/desktop-widget.h4
-rw-r--r--src/widgets/widget-sizes.h1
58 files changed, 3010 insertions, 655 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/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/desktop-events.cpp b/src/desktop-events.cpp
index 5fef8cbfc..cb3e1f7c8 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-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/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..8072ec53d 100644
--- a/src/document.cpp
+++ b/src/document.cpp
@@ -386,6 +386,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 +408,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
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-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.cpp b/src/file.cpp
index e8248bb8e..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.
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/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 59ff06a4e..f67c43db0 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -59,6 +59,9 @@ set(live_effects_SRC
parameter/array.cpp
parameter/bool.cpp
+ parameter/item-reference.cpp
+ parameter/item.cpp
+ parameter/originalitem.cpp
parameter/originalpath.cpp
parameter/originalpatharray.cpp
parameter/parameter.cpp
@@ -141,6 +144,9 @@ set(live_effects_SRC
parameter/array.h
parameter/bool.h
parameter/enum.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 19e80c611..a02f62ac9 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"
@@ -115,7 +117,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,12 +395,19 @@ Effect::doOnApply (SPLPEItem const*/*lpeitem*/)
}
void
+Effect::setCurrentZoom(double cZ)
+{
+ current_zoom = cZ;
+}
+
+void
Effect::setSelectedNodePos(std::vector<Geom::Point> selected_nodes_pos_data)
{
selected_nodes_pos = selected_nodes_pos_data;
}
void
+
Effect::setSelectedNodeIndex(Geom::PathVector pv)
{
selected_nodes_index.clear();
@@ -435,12 +445,6 @@ Effect::setSelectedNodeIndex(Geom::PathVector pv)
}
}
-void
-Effect::setCurrentZoom(double cZ)
-{
- current_zoom = cZ;
-}
-
bool
Effect::isNodeSelected(Geom::Point const &nodePoint) const
{
@@ -461,6 +465,74 @@ Effect::isNodeSelected(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.
*/
@@ -484,8 +556,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);
}
@@ -495,6 +571,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 0190325c0..6c11e0b67 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);
@@ -74,6 +80,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,
@@ -126,7 +135,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);
@@ -151,7 +162,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
@@ -165,11 +175,11 @@ 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> selected_nodes_pos;
std::vector<size_t> selected_nodes_index;
- 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..31d5bab65 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,310 @@ 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)
{
- registerParameter( dynamic_cast<Parameter *>(&linked_path) );
+ 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)
+{
+ 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(","));
+ if (inverse) {
+ cloneAttrbutes(SP_OBJECT(sp_lpe_item), linked_item.getObject(), true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true);
+ } else {
+ cloneAttrbutes(linked_item.getObject(), SP_OBJECT(sp_lpe_item), true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true);
+ }
+ }
+}
+
+
+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 +331,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..0ff5eb01d 100644
--- a/src/live_effects/lpe-clone-original.h
+++ b/src/live_effects/lpe-clone-original.h
@@ -8,24 +8,48 @@
*
* 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;
+ 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-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/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..8caea4e26
--- /dev/null
+++ b/src/live_effects/parameter/item.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "ui/widget/point.h"
+#include <glibmm/i18n.h>
+
+#include "live_effects/parameter/item.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+
+#include "widgets/icon.h"
+#include <gtk/gtk.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 <gtkmm/button.h>
+#include <gtkmm/label.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/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/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/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-lpe-item.cpp b/src/sp-lpe-item.cpp
index e2f61bfb5..00671b936 100644
--- a/src/sp-lpe-item.cpp
+++ b/src/sp-lpe-item.cpp
@@ -24,6 +24,8 @@
#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 +128,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 +214,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 +246,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 +265,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 +614,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 +626,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 +707,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 +721,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 +737,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..c9e71955f 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..0dc301c49 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);
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/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/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/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/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 21bd31299..f0d0541c2 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,11 +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>
+#include <iomanip>
#if defined (SOLARIS) && (SOLARIS == 8)
#include "round.h"
@@ -105,8 +105,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);
@@ -593,6 +605,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: 2; padding-right: 2; padding-top: 0; padding-bottom: 0;}");
+ 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();
@@ -619,12 +659,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);
@@ -692,6 +736,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);
@@ -1423,7 +1472,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 {
@@ -1629,10 +1678,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);
- gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0);
-
+ SPNamedView *nv = dtw->desktop->namedview;
+ gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, TRUE, TRUE, 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);
@@ -1672,6 +1722,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;
@@ -1723,6 +1775,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)
@@ -1802,6 +1866,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)
{
@@ -1940,6 +2032,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/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