diff options
| author | Shlomi Fish <shlomif@shlomifish.org> | 2017-02-06 16:50:07 +0000 |
|---|---|---|
| committer | Shlomi Fish <shlomif@shlomifish.org> | 2017-02-06 16:50:07 +0000 |
| commit | 1232596134bcba8d19f2809ffdc84e3b5c33d3b3 (patch) | |
| tree | 2fcb91d6fe9ef47a85ba3f73be10dc5dc7ee10a4 /src | |
| parent | Merged. (diff) | |
| parent | Remove some unneeded < C++11 fallback code (diff) | |
| download | inkscape-1232596134bcba8d19f2809ffdc84e3b5c33d3b3.tar.gz inkscape-1232596134bcba8d19f2809ffdc84e3b5c33d3b3.zip | |
Merged.
(bzr r15369.1.18)
Diffstat (limited to 'src')
140 files changed, 5552 insertions, 1211 deletions
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp index d469d3ffc..cac010942 100644 --- a/src/2geom/intersection-graph.cpp +++ b/src/2geom/intersection-graph.cpp @@ -410,10 +410,10 @@ PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b) assert(!result.back().empty()); } - /*if (n_processed != size() * 2) { + if (n_processed != size() * 2) { std::cerr << "Processed " << n_processed << " intersections, expected " << (size() * 2) << std::endl; - }*/ - assert(n_processed == size() * 2); + } + //assert(n_processed == size() * 2); return result; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47aa02ef1..6df71e17e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -200,6 +200,7 @@ set(inkscape_SRC event-log.cpp extract-uri.cpp file.cpp + file-update.cpp filter-chemistry.cpp filter-enums.cpp gc-anchored.cpp @@ -325,7 +326,6 @@ set(inkscape_SRC id-clash.h inkscape-version.h inkscape.h - isinf.h knot-enums.h knot-holder-entity.h knot-ptr.h @@ -366,7 +366,6 @@ set(inkscape_SRC removeoverlap.h require-config.h resource-manager.h - round.h rubberband.h satisfied-guide-cns.h selcue.h diff --git a/src/attributes.cpp b/src/attributes.cpp index 02b9450b0..8154bfbbe 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -89,6 +89,7 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_ZOOM, "inkscape:zoom"}, {SP_ATTR_INKSCAPE_CX, "inkscape:cx"}, {SP_ATTR_INKSCAPE_CY, "inkscape:cy"}, + {SP_ATTR_INKSCAPE_DOCUMENT_ROTATION, "inkscape:document-rotation"}, {SP_ATTR_INKSCAPE_WINDOW_WIDTH, "inkscape:window-width"}, {SP_ATTR_INKSCAPE_WINDOW_HEIGHT, "inkscape:window-height"}, {SP_ATTR_INKSCAPE_WINDOW_X, "inkscape:window-x"}, diff --git a/src/attributes.h b/src/attributes.h index 12adcf8e2..e21851bcf 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -97,6 +97,7 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_ZOOM, SP_ATTR_INKSCAPE_CX, SP_ATTR_INKSCAPE_CY, + SP_ATTR_INKSCAPE_DOCUMENT_ROTATION, SP_ATTR_INKSCAPE_WINDOW_WIDTH, SP_ATTR_INKSCAPE_WINDOW_HEIGHT, SP_ATTR_INKSCAPE_WINDOW_X, diff --git a/src/box3d.cpp b/src/box3d.cpp index dfc4d06e7..23766895a 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -15,12 +15,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "box3d.h" + #include <glibmm/i18n.h> #include "attributes.h" #include "xml/document.h" #include "xml/repr.h" -#include "box3d.h" +#include "bad-uri-exception.h" #include "box3d-side.h" #include "ui/tools/box3d-tool.h" #include "perspective-line.h" diff --git a/src/decimal-round.h b/src/decimal-round.h index 6b412685e..b90958cae 100644 --- a/src/decimal-round.h +++ b/src/decimal-round.h @@ -3,14 +3,11 @@ #include <cmath> -#include "round.h" - - namespace Inkscape { /** Returns x rounded to the nearest \a nplaces decimal places. - Implemented in terms of Inkscape::round, i.e. we make no guarantees as to what happens if x is + Implemented in terms of std::round, i.e. we make no guarantees as to what happens if x is half way between two rounded numbers. Add a note to the documentation if you care which candidate is chosen for such case (round towards -infinity, +infinity, 0, or "even"). @@ -25,7 +22,7 @@ inline double decimal_round(double const x, int const nplaces) { double const multiplier = std::pow(10.0, nplaces); - return Inkscape::round( x * multiplier ) / multiplier; + return round( x * multiplier ) / multiplier; } } diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index 5fef8cbfc..9e5f1c20b 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -155,6 +155,10 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge } } + SPNamedView *namedview = desktop->namedview; + //if (namedview && namedview->document_rotation) { + // normal *= Geom::Rotate(Geom::rad_from_deg(namedview->document_rotation * -1)); + //} guide = sp_guideline_new(desktop->guides, NULL, event_dt, normal); sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor); diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 94ee4ccfd..589cc849d 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -42,7 +42,6 @@ #include "2geom/line.h" #include "2geom/angle.h" #include "helper/mathfns.h" -#include "round.h" #include "util/units.h" using Inkscape::Util::unit_table; diff --git a/src/display/canvas-temporary-item-list.h b/src/display/canvas-temporary-item-list.h index 471bb99b9..6b0e5796b 100644 --- a/src/display/canvas-temporary-item-list.h +++ b/src/display/canvas-temporary-item-list.h @@ -14,6 +14,7 @@ struct SPCanvasItem; class SPDesktop; +class SPViewBox; namespace Inkscape { namespace Display { diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index 19dedb67c..1397c0f34 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -286,15 +286,7 @@ private: static int const BSize = 0x100; static int const BMask = 0xff; -#ifdef CPP11 // GCC 4.6.1 (currently used on Windows) does not correctly set __cplusplus in C++11 mode, so configure with -DCPP11 to make this work in C++11 mode static double constexpr PerlinOffset = 4096.0; -#else -#if (__cplusplus < 201103L) - static double const PerlinOffset; -#else - static double constexpr PerlinOffset = 4096.0; -#endif -#endif Geom::Rect _tile; Geom::Point _baseFreq; @@ -311,10 +303,6 @@ private: bool _fractalnoise; }; -#if !defined(CPP11) && __cplusplus < 201103L - double const TurbulenceGenerator::PerlinOffset = 4096.0; -#endif - FilterTurbulence::FilterTurbulence() : gen(new TurbulenceGenerator()) , XbaseFrequency(0) diff --git a/src/display/nr-filter-utils.h b/src/display/nr-filter-utils.h index 35a74d7c1..52b6bd11a 100644 --- a/src/display/nr-filter-utils.h +++ b/src/display/nr-filter-utils.h @@ -14,8 +14,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "round.h" - namespace Inkscape { namespace Filters { diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 789758d9c..d430553d4 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -48,11 +48,6 @@ #include "sp-filter-units.h" #include "preferences.h" -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif - namespace Inkscape { namespace Filters { diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 416c2c804..68eae0a65 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -29,18 +29,24 @@ #include "helper/sp-marshal.h" #include <2geom/rect.h> #include <2geom/affine.h> -#include "display/cairo-utils.h" #include "display/sp-canvas.h" #include "display/sp-canvas-group.h" +#include "display/rendermode.h" +#include "display/cairo-utils.h" +#include "display/cairo-templates.h" +#include "display/drawing-context.h" +#include "display/drawing-item.h" +#include "display/nr-filter-colormatrix.h" +#include "display/canvas-arena.h" #include "preferences.h" #include "inkscape.h" #include "sodipodi-ctrlrect.h" #include "cms-system.h" -#include "display/rendermode.h" -#include "display/cairo-utils.h" #include "debug/gdk-event-latency-tracker.h" #include "desktop.h" #include "color.h" +#include <iomanip> +#include <glibmm/i18n.h> using Inkscape::Debug::GdkEventLatencyTracker; @@ -1888,6 +1894,19 @@ SPCanvasGroup *SPCanvas::getRoot() return SP_CANVAS_GROUP(_root); } +gdouble grayscale_value_matrix[20] = { + 0.21, 0.72, 0.072, 0, 0, + 0.21, 0.72, 0.072, 0, 0, + 0.21, 0.72, 0.072, 0, 0, + 0 , 0 , 0 , 1, 0 + }; +cairo_surface_t *surface_rotated; +cairo_surface_t *surface_origin; +cairo_surface_t *surface_measure; +double start_angle = 0; +bool started = false; +bool rotated = false; + void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling) { GtkAllocation allocation; @@ -1910,9 +1929,14 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // Paint the background cairo_translate(cr, -ix, -iy); + if (rotated) { + cairo_translate(cr, dx, dy); + rotated = false; + } cairo_set_source(cr, _background); cairo_paint(cr); // Copy the old backing store contents + cairo_set_source_surface(cr, _backing_store, _x0, _y0); cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height); cairo_clip(cr); @@ -1949,6 +1973,271 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll addIdle(); } +void SPCanvas::startRotateTo(double angle) +{ + if (!_backing_store || started) { + return; + } + start_angle = angle; + started = true; + GtkAllocation allocation; + gtk_widget_get_allocation(&_widget, &allocation); + int half_w = allocation.width/2; + int half_h = allocation.height/2; + int half_min = std::min(half_w,half_h); + + cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cairo_t *cr = cairo_create(new_backing_store); + cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI); + cairo_fill(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_IN); + cairo_set_source_surface(cr, _backing_store, 0, 0); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_arc(cr, half_w, half_h, half_min-16, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 0.5); + cairo_stroke(cr); + cairo_destroy(cr); + surface_rotated = new_backing_store; + + cairo_surface_t *new_backing_store_measure = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cr = cairo_create(new_backing_store_measure); + cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 0.2); + cairo_fill(cr); + cairo_translate(cr, half_w, half_h); + cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 10.0); + for (gint x = 0; x < 360 ; x++){ + gint ang = 360 - x ;//+ 90; + if (ang > 180) { + ang -= 360; + } + double rot = (-180.0 + x)*(M_PI/180.); + double dist = half_min-9; + gint inverse = 1; + if((x) < 91 || (x) > 270) { + inverse = -1; + } + if(x%10 == 0) { + cairo_rotate(cr, -rot); + cairo_text_extents_t extents; + std::string s = std::to_string(ang) + "º"; + cairo_text_extents(cr, s.c_str(), &extents); + //std::cout << extents.width/2 << "extents.x_bearing\n"; + cairo_translate(cr, (extents.width/2) * inverse * -1, (dist + ((extents.height/2)* inverse))); + if((x) < 91 || (x) > 270) { + cairo_rotate(cr, 180*(M_PI/180.0)); + } + cairo_text_path(cr, s.c_str()); + if((x) < 91 || (x) > 270) { + cairo_rotate(cr, -180*(M_PI/180.0)); + } + cairo_translate(cr, (extents.width/2) * inverse , (dist + ((extents.height/2)* inverse)) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_fill(cr); + cairo_rotate(cr, rot); + } + cairo_rotate(cr, x*(M_PI/180.)); + if(x%5 == 0) { + cairo_move_to(cr, 0, half_min-30); + cairo_line_to(cr, 0, half_min-17); + } else { + cairo_move_to(cr, 0, half_min-20); + cairo_line_to(cr, 0, half_min-15); + } + cairo_line_to(cr, 0, half_min-15); + cairo_set_source_rgba (cr, 0, 0, 0, 0.4); + cairo_set_line_width (cr,1); + cairo_stroke(cr); + cairo_rotate(cr, -x*(M_PI/180.)); + } + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_translate(cr, -half_w, -half_h); + cairo_arc(cr, half_w, half_h, half_min-30, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_fill(cr); + cairo_translate(cr, half_w, half_h); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_rotate(cr, start_angle*(M_PI/180.)); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.25); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr,5); + cairo_stroke(cr); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 0, 0, 0.9); + cairo_set_line_width (cr,1); + cairo_stroke(cr); + cairo_rotate(cr, -start_angle*(M_PI/180.)); + cairo_destroy(cr); + surface_measure = new_backing_store_measure; + + cairo_surface_t *new_backing_store_grey = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cr = cairo_create(new_backing_store_grey); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, _backing_store, 0, 0); + cairo_paint(cr); + Inkscape::Filters::FilterColorMatrix::ColorMatrixMatrix _grayscale_colormatrix = std::vector<gdouble> (grayscale_value_matrix, grayscale_value_matrix + 20); + cairo_surface_t *out = ink_cairo_surface_create_identical(new_backing_store_grey); + ink_cairo_surface_filter(new_backing_store_grey, out, _grayscale_colormatrix); + cairo_set_source_surface(cr, out, 0, 0); + cairo_surface_destroy(out); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_destroy(cr); + surface_origin = new_backing_store_grey; +} + +bool SPCanvas::endRotateTo() +{ + if (!_backing_store || !started) { + return false; + } + started = false; + surface_rotated = NULL; + surface_origin = NULL; + gtk_widget_queue_draw(GTK_WIDGET(this)); + dirtyAll(); + addIdle(); + rotated = true; + return true; +} + +void SPCanvas::clearRotateTo() +{ + if (!started) { + return; + } + gtk_widget_queue_draw(GTK_WIDGET(this)); + dirtyAll(); + addIdle(); +} + +void SPCanvas::rotateTo(double angle) +{ + if (!_backing_store || !started) { + return; + } + GtkAllocation allocation; + gtk_widget_get_allocation(&_widget, &allocation); + int half_w = allocation.width/2; + int half_h = allocation.height/2; + int half_min = std::min(half_w,half_h); + cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cairo_t *cr = cairo_create(new_backing_store); + cairo_set_source_surface(cr, surface_origin, 0, 0); + cairo_paint(cr); + cairo_set_source_rgba (cr, 0, 0, 0, 0.5); + cairo_paint(cr); + cairo_pattern_t *source_pattern; + cairo_matrix_t matrix; + source_pattern = cairo_pattern_create_for_surface (surface_rotated); + cairo_matrix_init_identity (&matrix); + cairo_matrix_translate (&matrix, allocation.width/2.0, allocation.height/2.0); + cairo_matrix_rotate (&matrix, Geom::rad_from_deg(angle - start_angle) * -1); + cairo_matrix_translate (&matrix, -allocation.width/2.0, -allocation.height/2.0); + cairo_pattern_set_matrix (source_pattern, &matrix); + cairo_set_source(cr, source_pattern); + cairo_paint(cr); + cairo_set_source_surface(cr, surface_measure, 0, 0); + cairo_paint(cr); + cairo_translate(cr, half_w, half_h); + cairo_rotate(cr, angle*(M_PI/180.)); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.25); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr,5); + cairo_stroke(cr); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.9); + cairo_set_line_width (cr,1); + cairo_stroke(cr); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 0, 0, 0.9); + const double dashed[] = {6.0, 3.0}; + int len = sizeof(dashed) / sizeof(dashed[0]); + cairo_set_dash(cr, dashed, len, 1); + cairo_stroke(cr); + cairo_translate(cr, -half_w, -half_h); + cairo_set_source_rgba (cr, 1, 1, 1, 0.25); + cairo_arc(cr, half_w, half_h, 7, 0, 2*M_PI); + cairo_fill(cr); + cairo_set_source_rgba (cr, 1, 0, 0, 0.7); + cairo_arc(cr, half_w, half_h, 5, 0, 2*M_PI); + cairo_fill(cr); + cairo_translate(cr, half_w, half_h); + cairo_rotate(cr, -angle*(M_PI/180.)); + cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 15.0); + cairo_text_extents_t extents; + std::ostringstream s; + s << _("Original angle ") << std::fixed << std::setprecision(2) << start_angle << "º"; + cairo_text_extents(cr, s.str().c_str(), &extents); + cairo_translate(cr, half_w - extents.width -15 ,-half_h + 25); + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 25) *-1); + s.str(""); + s << _("New angle ") << std::fixed << std::setprecision(2) << angle << "º"; + cairo_text_extents(cr, s.str().c_str(), &extents); + cairo_translate(cr, half_w - extents.width -15 ,-half_h + 45); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 45) *-1); + s.str(""); + s << _("Gap ") << std::fixed << std::setprecision(2) << std::abs(start_angle-angle) << "º"; + cairo_text_extents(cr, s.str().c_str(), &extents); + cairo_translate(cr, half_w - extents.width -15 ,-half_h + 65); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 65) *-1); + cairo_translate(cr, -half_w + 10 ,-half_h + 25); + s.str(""); + cairo_set_font_size(cr, 12.0); + s << _("Normal mode, 1º round step"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w +10) * -1 ,(-half_h + 25) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 40); + s.str(""); + s << _("+ALT, Fractional degrees"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 40) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 55); + s.str(""); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + s << _("+CTRL, ") << 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12) << _("º round step"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 55) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 70); + s.str(""); + s << _("+SHIFT, Reset"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 70) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 85); + s.str(""); + s << _("+CTRL+SHIFT, 0º"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + //cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 60) * -1); + cairo_destroy(cr); + cairo_surface_destroy(_backing_store); + _backing_store = new_backing_store; + cairo_pattern_destroy (source_pattern); + gtk_widget_queue_draw(GTK_WIDGET(this)); + addIdle(); +} + void SPCanvas::updateNow() { if (_need_update) { diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 78d96d728..21b6760f2 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -72,7 +72,10 @@ GType sp_canvas_get_type() G_GNUC_CONST; struct SPCanvas { /// Scrolls canvas to specific position (cx and cy are measured in screen pixels). void scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling = false); - + void startRotateTo(double angle); + void rotateTo(double angle); + bool endRotateTo(); + void clearRotateTo(); /// Synchronously updates the canvas if necessary. void updateNow(); diff --git a/src/document-undo.cpp b/src/document-undo.cpp index 113d09d66..ae90c916e 100644 --- a/src/document-undo.cpp +++ b/src/document-undo.cpp @@ -240,49 +240,52 @@ static void perform_document_update(SPDocument &doc) { gboolean Inkscape::DocumentUndo::undo(SPDocument *doc) { - using Inkscape::Debug::EventTracker; - using Inkscape::Debug::SimpleEvent; + using Inkscape::Debug::EventTracker; + using Inkscape::Debug::SimpleEvent; - gboolean ret; + gboolean ret; - EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo"); + EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo"); - g_assert (doc != NULL); - g_assert (doc->priv != NULL); - g_assert (doc->priv->sensitive); + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + g_assert (doc->priv->sensitive); - doc->priv->sensitive = FALSE; + doc->priv->sensitive = FALSE; doc->priv->seeking = true; - doc->actionkey.clear(); + doc->actionkey.clear(); - finish_incomplete_transaction(*doc); + finish_incomplete_transaction(*doc); - if (! doc->priv->undo.empty()) { - Inkscape::Event *log = doc->priv->undo.back(); - doc->priv->undo.pop_back(); - sp_repr_undo_log (log->event); - perform_document_update(*doc); + if (! doc->priv->undo.empty()) { + Inkscape::Event *log = doc->priv->undo.back(); + doc->priv->undo.pop_back(); + sp_repr_undo_log (log->event); + perform_document_update(*doc); - doc->priv->redo.push_back(log); + doc->priv->redo.push_back(log); doc->setModifiedSinceSave(); doc->priv->undoStackObservers.notifyUndoEvent(log); - ret = TRUE; - } else { - ret = FALSE; - } + ret = TRUE; + } else { + ret = FALSE; + } - sp_repr_begin_transaction (doc->rdoc); + sp_repr_begin_transaction (doc->rdoc); - doc->priv->sensitive = TRUE; + doc->priv->sensitive = TRUE; doc->priv->seeking = false; - if (ret) - INKSCAPE.external_change(); + if (ret) INKSCAPE.external_change(); - return ret; + SPObject *updated = doc->getRoot(); + if (updated) { + updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + return ret; } gboolean Inkscape::DocumentUndo::redo(SPDocument *doc) diff --git a/src/document.cpp b/src/document.cpp index 0c77a8f48..b69508751 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -49,6 +49,7 @@ #include "display/drawing.h" #include "document-private.h" #include "document-undo.h" +#include "file.h" #include "id-clash.h" #include "inkscape.h" #include "inkscape-version.h" @@ -71,7 +72,7 @@ using Inkscape::Util::unit_table; // since we want it to happen when there are no more updates. #define SP_DOCUMENT_REROUTING_PRIORITY (G_PRIORITY_HIGH_IDLE - 1) - +bool sp_no_convert_text_baseline_spacing = false; static gint sp_document_idle_handler(gpointer data); static gint sp_document_rerouting_handler(gpointer data); @@ -386,6 +387,7 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, if (!bordercolor.empty()) { rnew->setAttribute("bordercolor", bordercolor.data()); } + sp_repr_set_svg_double(rnew, "inkscape:document-rotation", 0.); sp_repr_set_svg_double(rnew, "borderopacity", prefs->getDouble("/template/base/borderopacity", 1.0)); sp_repr_set_svg_double(rnew, "objecttolerance", @@ -407,6 +409,11 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, rroot->addChild(rnew, NULL); // clean up Inkscape::GC::release(rnew); + } else { + Inkscape::XML::Node *nv_repr = sp_item_group_get_child_by_name(document->root, NULL, "sodipodi:namedview")->getRepr(); + if (!nv_repr->attribute("inkscape:document-rotation")) { + sp_repr_set_svg_double(nv_repr, "inkscape:document-rotation", 0.); + } } // Defs @@ -446,6 +453,17 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, )); document->oldSignalsConnected = true; + /** Fix baseline spacing (pre-92 files) **/ + if ( (!sp_no_convert_text_baseline_spacing) + && sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) { + sp_file_convert_text_baseline_spacing(document); + } + + /** Fix font names in legacy documents (pre-92 files) **/ + if ( sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) { + sp_file_convert_font_name(document); + } + return document; } @@ -1047,6 +1065,60 @@ sigc::connection SPDocument::connectIdChanged(gchar const *id, return priv->id_changed_signals[g_quark_from_string(id)].connect(slot); } +void _getObjectsByClassRecursive(Glib::ustring const &klass, SPObject *parent, std::vector<SPObject *> &objects) +{ + if (parent) { + Glib::ustring class_attribute; + char const *temp = parent->getAttribute("class"); + if (temp) { + class_attribute = temp; + } + + if (class_attribute.find( klass ) != std::string::npos) { + objects.push_back( parent ); + } + + // Check children + for (auto& child : parent->children) { + _getObjectsByClassRecursive( klass, &child, objects ); + } + } +} + +std::vector<SPObject *> SPDocument::getObjectsByClass(Glib::ustring const &klass) const +{ + std::vector<SPObject *> objects; + g_return_val_if_fail(!klass.empty(), objects); + + _getObjectsByClassRecursive(klass, root, objects); + return objects; +} + +void _getObjectsByElementRecursive(Glib::ustring const &element, SPObject *parent, + std::vector<SPObject *> &objects) +{ + if (parent) { + Glib::ustring prefixed = "svg:" + element; + if (parent->getRepr()->name() == prefixed) { + objects.push_back(parent); + } + + // Check children + for (auto& child : parent->children) { + _getObjectsByElementRecursive(element, &child, objects); + } + } +} + +std::vector<SPObject *> SPDocument::getObjectsByElement(Glib::ustring const &element) const +{ + std::vector<SPObject *> objects; + g_return_val_if_fail(!element.empty(), objects); + + _getObjectsByElementRecursive(element, root, objects); + return objects; +} + void SPDocument::bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object) { if (object) { diff --git a/src/document.h b/src/document.h index 813d4ae49..142eb5000 100644 --- a/src/document.h +++ b/src/document.h @@ -30,6 +30,24 @@ #include <set> #include <deque> +// This variable is introduced with 0.92.1 +// with the introduction of automatic fix +// for files detected to have been created +// with previous versions to have a similar +// look in 0.92+. +extern bool sp_no_convert_text_baseline_spacing; + + + +// This variable is introduced with 0.92.1 +// with the introduction of automatic fix +// for files detected to have been created +// with previous versions to have a similar +// look in 0.92+. +extern bool sp_do_not_fix_pre_92; + + + namespace Avoid { class Router; } @@ -179,6 +197,9 @@ public: SPObject *getObjectById(char const *id) const; sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot); + std::vector<SPObject *> getObjectsByClass(Glib::ustring const &klass) const; + std::vector<SPObject *> getObjectsByElement(Glib::ustring const &element) const; + void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object); SPObject *getObjectByRepr(Inkscape::XML::Node *repr) const; diff --git a/src/extension/internal/cairo-png-out.cpp b/src/extension/internal/cairo-png-out.cpp index 956fcce9a..5859a82da 100644 --- a/src/extension/internal/cairo-png-out.cpp +++ b/src/extension/internal/cairo-png-out.cpp @@ -53,11 +53,10 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename) { CairoRenderer *renderer; CairoRenderContext *ctx; - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); /* Start */ - SPItem *base = doc->getRoot(); Inkscape::Drawing drawing; unsigned dkey = SPItem::display_key_new(1); @@ -77,6 +76,7 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename) renderer->destroyContext(ctx); base->invoke_hide(dkey); + doc->getRoot()->c2p *= doc->getRoot()->rotation; /* end */ delete renderer; diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp index e8f47e79e..809125266 100644 --- a/src/extension/internal/cairo-ps-out.cpp +++ b/src/extension/internal/cairo-ps-out.cpp @@ -68,6 +68,7 @@ static bool ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool omittext, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px, bool eps = false) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); SPItem *base = NULL; @@ -84,9 +85,10 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l pageBoundingBox = !exportDrawing; } - if (!base) + if (!base) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; - + } Inkscape::Drawing drawing; unsigned dkey = SPItem::display_key_new(1); base->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY); @@ -115,6 +117,7 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l renderer->destroyContext(ctx); delete renderer; + doc->getRoot()->c2p *= doc->getRoot()->rotation; return ret; } diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index e65752c84..06711bca4 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -865,7 +865,7 @@ CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t cairo_scale(_cr, Inkscape::Util::Quantity::convert(1, "px", "pt"), Inkscape::Util::Quantity::convert(1, "px", "pt")); } else if (cairo_surface_get_content(_surface) != CAIRO_CONTENT_ALPHA) { // set background color on non-alpha surfaces - // TODO: bgcolor should be derived from SPDocument + // TODO: bgcolor should be derived from SPDocument (see IconImpl) cairo_set_source_rgb(_cr, 1.0, 1.0, 1.0); cairo_rectangle(_cr, 0, 0, _width, _height); cairo_fill(_cr); diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp index 5576676b2..5558fe1ad 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -61,6 +61,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int bool texttopath, bool omittext, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); /* Start */ @@ -80,6 +81,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int } if (!base) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } @@ -112,7 +114,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int renderer->destroyContext(ctx); delete renderer; - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return ret; } diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 12751c5ec..198c18ff2 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -94,7 +94,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename) const gchar *oldconst; gchar *oldoutput; unsigned int ret; - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); mod = Inkscape::Extension::get_print(PRINT_EMF); @@ -114,6 +114,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename) /* Print document */ ret = mod->begin(doc); if (ret) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; g_free(oldoutput); throw Inkscape::Extension::Output::save_failed(); } @@ -127,7 +128,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename) mod->set_param_string("destination", oldoutput); g_free(oldoutput); - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp index d7ad7e6f7..c95434939 100644 --- a/src/extension/internal/javafx-out.cpp +++ b/src/extension/internal/javafx-out.cpp @@ -843,7 +843,8 @@ void JavaFXOutput::reset() bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) { reset(); - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); name = Glib::path_get_basename(filename_utf8); int pos = name.find('.'); @@ -856,12 +857,14 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) //# Lets do the curves first, to get the stats if (!doTree(doc)) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } String curveBuf = outbuf; outbuf.clear(); if (!doHeader()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } @@ -875,6 +878,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) doBody(doc, doc->getRoot()); if (!doTail()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } @@ -884,6 +888,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w"); if (!f) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could open JavaFX file '%s' for writing", filename_utf8); return false; } @@ -894,7 +899,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) } fclose(f); - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return true; } diff --git a/src/extension/internal/latex-pstricks-out.cpp b/src/extension/internal/latex-pstricks-out.cpp index 3ce2c5531..aa6ea6963 100644 --- a/src/extension/internal/latex-pstricks-out.cpp +++ b/src/extension/internal/latex-pstricks-out.cpp @@ -49,6 +49,7 @@ bool LatexOutput::check(Inkscape::Extension::Extension * /*module*/) void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc, gchar const *filename) { SPPrintContext context; + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); Inkscape::Extension::Print *mod = Inkscape::Extension::get_print(SP_MODULE_KEY_PRINT_LATEX); @@ -76,6 +77,7 @@ void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc, mod->set_param_string("destination", oldoutput); g_free(oldoutput); + doc->getRoot()->c2p *= doc->getRoot()->rotation; } #include "clear-n_.h" diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp index f885ef5e5..66d370357 100644 --- a/src/extension/internal/odf.cpp +++ b/src/extension/internal/odf.cpp @@ -72,6 +72,7 @@ #include "sp-path.h" #include "sp-text.h" #include "sp-flowtext.h" +#include "sp-root.h" #include "svg/svg.h" #include "text-editing.h" #include "util/units.h" @@ -2095,7 +2096,8 @@ void OdfOutput::reset() void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename) { reset(); - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); documentUri = Inkscape::URI(filename); ZipFile zf; @@ -2104,25 +2106,30 @@ void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gcha if (!writeManifest(zf)) { g_warning("Failed to write manifest"); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } if (!writeContent(zf, doc->rroot)) { g_warning("Failed to write content"); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } if (!writeMeta(zf)) { g_warning("Failed to write metafile"); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } if (!zf.writeFile(filename)) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } + doc->getRoot()->c2p *= doc->getRoot()->rotation; } diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp index 8df883069..03a2ecd62 100644 --- a/src/extension/internal/pov-out.cpp +++ b/src/extension/internal/pov-out.cpp @@ -616,11 +616,13 @@ void PovOutput::reset() void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) { reset(); - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); //###### SAVE IN POV FORMAT TO BUFFER //# Lets do the curves first, to get the stats if (!doTree(doc)) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could not output curves for %s", filename_utf8); return; } @@ -630,6 +632,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) if (!doHeader()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could not write header for %s", filename_utf8); return; } @@ -638,6 +641,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) if (!doTail()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could not write footer for %s", filename_utf8); return; } @@ -648,9 +652,11 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) //###### WRITE TO FILE Inkscape::IO::dump_fopen_call(filename_utf8, "L"); FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w"); - if (!f) + if (!f){ + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; - + } + for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); ++iter) { int ch = *iter; @@ -658,6 +664,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) } fclose(f); + doc->getRoot()->c2p *= doc->getRoot()->rotation; } diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index c7226a58a..a79af5ec1 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -95,7 +95,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) SPPrintContext context; const gchar *oldconst; gchar *oldoutput; - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); mod = Inkscape::Extension::get_print(PRINT_WMF); @@ -115,6 +115,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) /* Print document */ if (mod->begin(doc)) { g_free(oldoutput); + doc->getRoot()->c2p *= doc->getRoot()->rotation; throw Inkscape::Extension::Output::save_failed(); } mod->base->invoke_print(&context); @@ -127,7 +128,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) mod->set_param_string("destination", oldoutput); g_free(oldoutput); - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -135,6 +136,8 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) void Wmf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); Inkscape::Extension::Extension * ext; ext = Inkscape::Extension::db.get(PRINT_WMF); diff --git a/src/file-update.cpp b/src/file-update.cpp new file mode 100644 index 000000000..40a8bc834 --- /dev/null +++ b/src/file-update.cpp @@ -0,0 +1,173 @@ +/** + * @file-update + * Operations to bump files from the pre-0.92 era into the 0.92+ era + * (90dpi vs 96dpi, line height problems, and related bugs) + */ +/* Authors: + * Tavmjong Bah + * Marc Jeanmougin + * su_v + */ +#include "file.h" +#include "sp-root.h" +#include "sp-text.h" +#include "sp-tspan.h" +#include "sp-flowdiv.h" +#include "sp-flowtext.h" +#include "sp-object.h" +#include "sp-item.h" +#include "style.h" +#include "document.h" +#include <string> +#include "text-editing.h" + +using namespace std; + +bool is_line(SPObject *i) +{ + if (!(i->getAttribute("sodipodi:role"))) + return false; + return !strcmp(i->getAttribute("sodipodi:role"), "line"); +} + + +void fix_blank_line(SPObject *o) +{ + if (SP_IS_TEXT(o)) + ((SPText *)o)->rebuildLayout(); + else if (SP_IS_FLOWTEXT(o)) + ((SPFlowtext *)o)->rebuildLayout(); + + SPIFontSize fontsize = o->style->font_size; + SPILengthOrNormal lineheight = o->style->line_height; + vector<SPObject *> cl = o->childList(false); + bool beginning = true; + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) { + SPObject *i = *ci; + if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) { + if (sp_text_get_length((SPItem *)i) <= 1) { // empty line + Inkscape::Text::Layout::iterator pos = te_get_layout((SPItem*)(o))->charIndexToIterator( + ((SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i))?0:((ci==cl.begin())?0:1)) + sp_text_get_length_upto(o,i) ); + sp_te_insert((SPItem *)o, pos, "\u00a0"); //"\u00a0" + gchar *l = g_strdup_printf("%f", lineheight.value); + gchar *f = g_strdup_printf("%f", fontsize.value); + i->style->line_height.readIfUnset(l); + if (!beginning) + i->style->font_size.read(f); + else + i->style->font_size.readIfUnset(f); + g_free(l); + g_free(f); + } else { + beginning = false; + fontsize = i->style->font_size; + lineheight = o->style->line_height; + } + } + } +} + +void fix_line_spacing(SPObject *o) +{ + SPILengthOrNormal lineheight = o->style->line_height; + bool inner = false; + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) { + SPObject *i = *ci; + if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) { + // if no line-height attribute, set it + gchar *l = g_strdup_printf("%f", lineheight.value); + i->style->line_height.readIfUnset(l); + g_free(l); + } + inner = true; + } + if (inner) { + if (SP_IS_TEXT(o)) { + o->style->line_height.read("0.00%"); + } else { + o->style->line_height.read("0.01%"); + } + } +} + +void fix_font_name(SPObject *o) +{ + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) + fix_font_name(*ci); + string prev = o->style->font_family.value ? o->style->font_family.value : o->style->font_family.value_default; + if (prev == "Sans") + o->style->font_family.read("sans-serif"); + else if (prev == "Serif") + o->style->font_family.read("serif"); + else if (prev == "Monospace") + o->style->font_family.read("monospace"); +} + + +void fix_font_size(SPObject *o) +{ + SPIFontSize fontsize = o->style->font_size; + if (!fontsize.set) + return; + bool inner = false; + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) { + SPObject *i = *ci; + fix_font_size(i); + if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) { + inner = true; + gchar *s = g_strdup_printf("%f", fontsize.value); + if (fontsize.set) + i->style->font_size.readIfUnset(s); + g_free(s); + } + } + if (inner && (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o))) + o->style->font_size.clear(); +} + + + +// helper function +void sp_file_text_run_recursive(void (*f)(SPObject *), SPObject *o) +{ + if (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o)) + f(o); + else { + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) + sp_file_text_run_recursive(f, *ci); + } +} + +void fix_update(SPObject *o) { + o->style->write(); + o->updateRepr(); +} + +void sp_file_convert_text_baseline_spacing(SPDocument *doc) +{ + sp_file_text_run_recursive(fix_blank_line, doc->getRoot()); + sp_file_text_run_recursive(fix_line_spacing, doc->getRoot()); + sp_file_text_run_recursive(fix_font_size, doc->getRoot()); + sp_file_text_run_recursive(fix_update, doc->getRoot()); +} + +void sp_file_convert_font_name(SPDocument *doc) +{ + sp_file_text_run_recursive(fix_font_name, doc->getRoot()); + sp_file_text_run_recursive(fix_update, doc->getRoot()); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/file.cpp b/src/file.cpp index 9e96361c3..6613b1e65 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -293,10 +293,12 @@ bool sp_file_open(const Glib::ustring &uri, bool replace_empty) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Inkscape::Display::TemporaryItem *page_border_rotated = NULL; if (desktop) { desktop->setWaitingCursor(); + page_border_rotated = sp_document_namedview(desktop->getDocument(), NULL)->page_border_rotated; } - + SPDocument *doc = NULL; bool cancelled = false; try { @@ -315,7 +317,6 @@ bool sp_file_open(const Glib::ustring &uri, } if (doc) { - SPDocument *existing = desktop ? desktop->getDocument() : NULL; if (existing && existing->virgin && replace_empty) { @@ -323,6 +324,7 @@ bool sp_file_open(const Glib::ustring &uri, doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc. desktop->change_document(doc); doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px")); + desktop->remove_temporary_canvasitem(page_border_rotated); } else { // create a whole new desktop and window SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc. @@ -1631,8 +1633,12 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) for (Inkscape::XML::Node *obj = clipboard->firstChild() ; obj ; obj = obj->next()) { if(target_document->getObjectById(obj->attribute("id"))) continue; Inkscape::XML::Node *obj_copy = obj->duplicate(target_document->getReprDoc()); - target_parent->appendChild(obj_copy); + SPObject * pasted = desktop->currentLayer()->appendChildRepr(obj_copy); Inkscape::GC::release(obj_copy); + SPLPEItem * pasted_lpe_item = dynamic_cast<SPLPEItem *>(pasted); + if (pasted_lpe_item){ + pasted_lpe_item->forkPathEffectsIfNecessary(1); + } pasted_objects_not.push_back(obj_copy); } Inkscape::Selection *selection = desktop->getSelection(); diff --git a/src/file.h b/src/file.h index 4ffbc8ec0..2fc4ea910 100644 --- a/src/file.h +++ b/src/file.h @@ -203,7 +203,8 @@ void sp_file_print (Gtk::Window& parentWindow); * clean unused defs out of file */ void sp_file_vacuum (SPDocument *doc); - +void sp_file_convert_text_baseline_spacing(SPDocument *doc); +void sp_file_convert_font_name(SPDocument *doc); #endif // SEEN_SP_FILE_H diff --git a/src/filters/image.cpp b/src/filters/image.cpp index 887201eb3..3dedb28ad 100644 --- a/src/filters/image.cpp +++ b/src/filters/image.cpp @@ -14,12 +14,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "image.h" + #include <sigc++/bind.h> + +#include "bad-uri-exception.h" #include "display/nr-filter-image.h" #include "uri.h" #include "uri-references.h" #include "attributes.h" -#include "image.h" #include "xml/repr.h" #include "display/nr-filter.h" diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp index 42c494c00..e1f05c3ce 100644 --- a/src/helper/geom.cpp +++ b/src/helper/geom.cpp @@ -843,6 +843,13 @@ recursive_bezier4(const double x1, const double y1, recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1); } +void +swap(Geom::Point &A, Geom::Point &B){ + Geom::Point tmp = A; + A = B; + B = tmp; +} + /* Local Variables: mode:c++ diff --git a/src/helper/geom.h b/src/helper/geom.h index d49e2070c..b3d907e51 100644 --- a/src/helper/geom.h +++ b/src/helper/geom.h @@ -32,7 +32,7 @@ void recursive_bezier4(const double x1, const double y1, const double x2, const const double x3, const double y3, const double x4, const double y4, std::vector<Geom::Point> &pointlist, int level); - +void swap(Geom::Point &A, Geom::Point &B); #endif // INKSCAPE_HELPER_GEOM_H /* diff --git a/src/inkgc/gc-core.h b/src/inkgc/gc-core.h index d9d0bf4ff..a27510f50 100644 --- a/src/inkgc/gc-core.h +++ b/src/inkgc/gc-core.h @@ -18,7 +18,7 @@ #include <new> #include <cstdlib> -#include <cstddef> + #ifdef HAVE_GC_GC_H # include <gc/gc.h> #else @@ -137,7 +137,6 @@ inline void *operator new(std::size_t size, Inkscape::GC::CollectionPolicy collect, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { using namespace Inkscape::GC; @@ -168,7 +167,6 @@ inline void *operator new(std::size_t size, Inkscape::GC::ScanPolicy scan, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { return operator new(size, scan, Inkscape::GC::AUTO, cleanup, data); } @@ -178,7 +176,6 @@ inline void *operator new[](std::size_t size, Inkscape::GC::CollectionPolicy collect, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { return operator new(size, scan, collect, cleanup, data); } @@ -187,7 +184,6 @@ inline void *operator new[](std::size_t size, Inkscape::GC::ScanPolicy scan, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { return operator new[](size, scan, Inkscape::GC::AUTO, cleanup, data); } diff --git a/src/inkgc/gc-managed.h b/src/inkgc/gc-managed.h index f435d7802..97b2b816e 100644 --- a/src/inkgc/gc-managed.h +++ b/src/inkgc/gc-managed.h @@ -30,7 +30,6 @@ public: void *operator new(std::size_t size, ScanPolicy scan=default_scan, CollectionPolicy collect=default_collect) - throw (std::bad_alloc) { return ::operator new(size, scan, collect); } @@ -38,7 +37,6 @@ public: void *operator new[](std::size_t size, ScanPolicy scan=default_scan, CollectionPolicy collect=default_collect) - throw (std::bad_alloc) { return ::operator new[](size, scan, collect); } diff --git a/src/inkgc/gc.cpp b/src/inkgc/gc.cpp index ffa94ea2a..b1bd07af6 100644 --- a/src/inkgc/gc.cpp +++ b/src/inkgc/gc.cpp @@ -173,7 +173,7 @@ public: {} }; -Ops const &get_ops() throw (InvalidGCModeError) { +Ops const &get_ops() { char *mode_string=std::getenv("_INKSCAPE_GC"); if (mode_string) { if (!std::strcmp(mode_string, "enable")) { diff --git a/src/isinf.h b/src/isinf.h deleted file mode 100644 index 8d590b972..000000000 --- a/src/isinf.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ISINF_H__ -#define __ISINF_H__ - -/* - * Fix for missing std::isinf with SOLARIS8/GCC3.2 - */ - -#if defined (SOLARIS) - - #include <ieeefp.h> - #define isinf(x) ((fpclass(x) == FP_NINF) || (fpclass(x) == FP_PINF)) - -#elif defined(__APPLE__) && __GNUC__ == 3 -#define isinf(x) __isinf(x) -#elif __cplusplus >= 201103L -# include <cmath> -# define isinf std::isinf -#endif - -#endif /* __ISINF_H__ */ diff --git a/src/libcola/gradient_projection.cpp b/src/libcola/gradient_projection.cpp index 3e41aceac..c8488a545 100644 --- a/src/libcola/gradient_projection.cpp +++ b/src/libcola/gradient_projection.cpp @@ -18,7 +18,6 @@ #include "gradient_projection.h" #include <iostream> #include <2geom/math-utils.h> -#include "isinf.h" #include <math.h> using namespace std; diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index e6da9ba63..8e173a7c7 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -210,6 +210,7 @@ class Layout::Calculator */ static void dumpPangoItemsOut(ParagraphInfo *para){ std::cout << "Pango items: " << para->pango_items.size() << std::endl; + font_factory * factory = font_factory::Default(); for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){ std::cout << "idx: " << pidx @@ -217,6 +218,8 @@ static void dumpPangoItemsOut(ParagraphInfo *para){ << para->pango_items[pidx].item->offset << " length: " << para->pango_items[pidx].item->length + << " font: " + << factory->ConstructFontSpecification( para->pango_items[pidx].font ) << std::endl; } } @@ -1483,11 +1486,18 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, UnbrokenSpanPosition span_pos; for( ; ; ) { + + // Get regions where one can place one line of text (can be more than one, if filling a + // donut for example). std::vector<ScanlineMaker::ScanRun> scan_runs; scan_runs = _scanline_maker->makeScanline(*line_box_height); // Only one line with "InfiniteScanlineMaker + + // If scan_runs is empty, we must have reached the bottom of a shape. Go to next shape. while (scan_runs.empty()) { // Only used by ShapeScanlineMaker if (!_goToNextWrapShape()) return false; // no more shapes to wrap in to + + *line_box_height = *strut_height; scan_runs = _scanline_maker->makeScanline(*line_box_height); } @@ -1498,13 +1508,21 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, unsigned scan_run_index; span_pos = *start_span_pos; for (scan_run_index = 0 ; scan_run_index < scan_runs.size() ; scan_run_index++) { - if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height)) + + // Returns false if some text in line requires a taller line_box_height. + // (We try again with a larger line_box_height.) + if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height)) { break; + } + if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty()) span_pos = chunk_info->back().broken_spans.back().end; } + if (scan_run_index == scan_runs.size()) break; // ie when buildChunksInScanRun() succeeded - } + + } // End for loop + *start_span_pos = span_pos; TRACE((" final line_box_height: %f\n", line_box_height->emSize() )); TRACE((" end _findChunksForLine: chunks: %lu\n", chunk_info->size())); @@ -1578,6 +1596,8 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, new_span_height.descent > line_height->descent + std::numeric_limits<float>::epsilon() ) { // Take larger of each of the two ascents and two descents per CSS line_height->max(new_span_height); + + // Currently always true for flowed text and false for Inkscape multiline text. if (!_scanline_maker->canExtendCurrentScanline(*line_height)) { return false; } @@ -1752,12 +1772,20 @@ bool Layout::Calculator::calculate() span_pos.char_byte = 0; span_pos.char_index = 0; + bool keep_going = true; do { // for each line in the paragraph TRACE(("begin line\n")); + std::vector<ChunkInfo> line_chunk_info; - if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height, &strut_height )) + if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height, &strut_height )) { + keep_going = false; break; // out of shapes to wrap in to + } + if (line_box_height.emSize() < 0.001 && line_chunk_info.empty()) { + keep_going = false; + break; // No room for text and not useful to try again at same place. + } _outputLine(para, line_box_height, line_chunk_info); _scanline_maker->setLineHeight( line_box_height ); _scanline_maker->completeLine(); // Increments y by line height @@ -1765,7 +1793,7 @@ bool Layout::Calculator::calculate() } while (span_pos.iter_span != para.unbroken_spans.end()); TRACE(("para %lu end\n\n", _flow._paragraphs.size() - 1)); - if (_scanline_maker != NULL) { + if (keep_going) { bool is_empty_para = _flow._characters.empty() || _flow._characters.back().line(&_flow).in_paragraph != _flow._paragraphs.size() - 1; if ((is_empty_para && para_end_input_index + 1 >= _flow._input_stream.size()) || para_end_input_index + 1 < _flow._input_stream.size()) { diff --git a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp index 0d6112d19..a57aaa3e4 100644 --- a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp +++ b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp @@ -124,7 +124,7 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScan FloatLigne line_rasterization; FloatLigne line_decent_length_runs; float line_text_height = (float)(line_height.emSize()); - if (line_text_height == 0.0) + if (line_text_height < 0.001) line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do _current_line_height = (float)line_height.emSize(); diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 784317090..5ffccc7c0 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -60,6 +60,9 @@ set(live_effects_SRC parameter/array.cpp parameter/bool.cpp parameter/filletchamferpointarray.cpp + parameter/item-reference.cpp + parameter/item.cpp + parameter/originalitem.cpp parameter/originalpath.cpp parameter/originalpatharray.cpp parameter/parameter.cpp @@ -142,6 +145,9 @@ set(live_effects_SRC parameter/bool.h parameter/enum.h parameter/filletchamferpointarray.h + parameter/item.h + parameter/item-reference.h + parameter/originalitem.h parameter/originalpath.h parameter/originalpatharray.h parameter/parameter.h diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 5cc0d6f20..50e2aa353 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -70,6 +70,8 @@ #include "ui/tools/node-tool.h" #include "ui/tools-switch.h" #include "knotholder.h" +#include "path-chemistry.h" +#include "xml/sp-css-attr.h" #include "live_effects/lpeobject.h" #include "display/curve.h" @@ -116,7 +118,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { {RULER, N_("Ruler"), "ruler"}, /* 0.91 */ {POWERSTROKE, N_("Power stroke"), "powerstroke"}, - {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"}, + {CLONE_ORIGINAL, N_("Clone original"), "clone_original"}, /* 0.92 */ {SIMPLIFY, N_("Simplify"), "simplify"}, {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, @@ -356,6 +358,7 @@ Effect::Effect(LivePathEffectObject *lpeobject) sp_lpe_item(NULL), current_zoom(1), upd_params(true), + sp_shape(NULL), sp_curve(NULL), provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden @@ -392,15 +395,15 @@ Effect::doOnApply (SPLPEItem const*/*lpeitem*/) } void -Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP) +Effect::setCurrentZoom(double cZ) { - selectedNodesPoints = sNP; + current_zoom = cZ; } void -Effect::setCurrentZoom(double cZ) +Effect::setSelectedNodePoints(std::vector<Geom::Point> sNP) { - current_zoom = cZ; + selectedNodesPoints = sNP; } bool @@ -423,6 +426,74 @@ Effect::isNodePointSelected(Geom::Point const &nodePoint) const return false; } +void +Effect::processObjects(LpeAction lpe_action) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + for (std::vector<const char *>::iterator el_it = items.begin(); + el_it != items.end(); ++el_it) { + const char * id = *el_it; + if (!id || strlen(id) == 0) { + return; + } + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + Inkscape::XML::Node * elemnode = elemref->getRepr(); + std::vector<SPItem*> item_list; + item_list.push_back(SP_ITEM(elemref)); + std::vector<Inkscape::XML::Node*> item_to_select; + std::vector<SPItem*> item_selected; + SPCSSAttr *css; + Glib::ustring css_str; + switch (lpe_action){ + case LPE_TO_OBJECTS: + if (SP_ITEM(elemref)->isHidden()) { + elemref->deleteObject(); + } else { + if (elemnode->attribute("inkscape:path-effect")) { + sp_item_list_to_curves(item_list, item_selected, item_to_select); + } + elemnode->setAttribute("sodipodi:insensitive", NULL); + } + break; + + case LPE_ERASE: + elemref->deleteObject(); + break; + + case LPE_VISIBILITY: + css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); + if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) { + css->setAttribute("display", "none"); + } else { + css->setAttribute("display", NULL); + } + sp_repr_css_write_string(css,css_str); + elemnode->setAttribute("style", css_str.c_str()); + break; + + default: + break; + } + } + } + if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) { + items.clear(); + } +} + +void Effect::setCurrentShape(SPShape * shape){ + if(shape){ + sp_shape = shape; + if (!(sp_curve = sp_shape->getCurve())) { + // oops + return; + } + pathvector_before_effect = sp_curve->get_pathvector(); + } +} + /** * Is performed each time before the effect is updated. */ @@ -446,8 +517,12 @@ void Effect::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) void Effect::doOnApply_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); - /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); - pathvector_before_effect = sp_curve->get_pathvector();*/ + sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); + pathvector_before_effect = sp_curve->get_pathvector(); + SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); + if(shape){ + setCurrentShape(shape); + } doOnApply(lpeitem); } @@ -457,6 +532,7 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) //printf("(SPLPEITEM*) %p\n", sp_lpe_item); SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); if(shape){ + setCurrentShape(shape); sp_curve = shape->getCurve(); pathvector_before_effect = sp_curve->get_pathvector(); } diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 1997ff0ca..f5e41d50e 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -19,7 +19,7 @@ class SPDocument; class SPDesktop; class SPItem; -class LivePathEffectObject; +class LivePathEffectObject; class SPLPEItem; class KnotHolder; class KnotHolderEntity; @@ -44,6 +44,12 @@ enum LPEPathFlashType { DEFAULT }; +enum LpeAction { + LPE_ERASE = 0, + LPE_TO_OBJECTS, + LPE_VISIBILITY +}; + class Effect { public: static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj); @@ -73,6 +79,9 @@ public: static int acceptsNumClicks(EffectType type); int acceptsNumClicks() const { return acceptsNumClicks(effectType()); } void doAcceptPathPreparations(SPLPEItem *lpeitem); + SPShape * getCurrentShape(){ return sp_shape; }; + void setCurrentShape(SPShape * shape); + void processObjects(LpeAction lpe_action); /* * isReady() indicates whether all preparations which are necessary to apply the LPE are done, @@ -125,7 +134,9 @@ public: bool apply_to_clippath_and_mask; bool erase_extra_objects; // set this to false allow retain extra generated objects, see measure line LPE bool upd_params; - + BoolParam is_visible; + SPCurve * sp_curve; + Geom::PathVector pathvector_before_effect; protected: Effect(LivePathEffectObject *lpeobject); @@ -150,7 +161,6 @@ protected: bool _provides_knotholder_entities; int oncanvasedit_it; - BoolParam is_visible; bool show_orig_path; // set this to true in derived effects to automatically have the original // path displayed as helperpath @@ -164,10 +174,10 @@ protected: bool concatenate_before_pwd2; SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. + SPShape * sp_shape; // these get stored in doBeforeEffect_impl before doEffect chain, or in performPathEffects on groups, and derived classes may do as they please with them. + std::vector<const char *> items; double current_zoom; std::vector<Geom::Point> selectedNodesPoints; - SPCurve * sp_curve; - Geom::PathVector pathvector_before_effect; private: bool provides_own_flash_paths; // if true, the standard flash path is suppressed diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp index 10418a02d..04db6171e 100644 --- a/src/live_effects/lpe-clone-original.cpp +++ b/src/live_effects/lpe-clone-original.cpp @@ -6,6 +6,12 @@ #include "live_effects/lpe-clone-original.h" #include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "sp-clippath.h" +#include "sp-mask.h" +#include "xml/sp-css-attr.h" + // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -14,9 +20,319 @@ namespace LivePathEffect { LPECloneOriginal::LPECloneOriginal(LivePathEffectObject *lpeobject) : Effect(lpeobject), - linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this) + linked_path("LEGACY FALLBACK", "LEGACY FALLBACK", "linkedpath", &wr, this), + linked_item(_("Linked Item:"), _("Item from which to take the original data"), "linked_item", &wr, this), + scale(_("Scale %"), _("Scale item %"), "scale", &wr, this, 100.0), + preserve_position(_("Preserve position"), _("Preserve position"), "preserve_position", &wr, this, false), + inverse(_("Inverse clone"), _("Use LPE item as origin"), "inverse", &wr, this, false), + d(_("Clone shape -d-"), _("Clone shape -d-"), "d", &wr, this, true), + transform(_("Clone transforms"), _("Clone transforms"), "transform", &wr, this, true), + fill(_("Clone fill"), _("Clone fill"), "fill", &wr, this, false), + stroke(_("Clone stroke"), _("Clone stroke"), "stroke", &wr, this, false), + paintorder(_("Clone paint order"), _("Clone paint order"), "paintorder", &wr, this, false), + opacity(_("Clone opacity"), _("Clone opacity"), "opacity", &wr, this, false), + filter(_("Clone filter"), _("Clone filter"), "filter", &wr, this, false), + attributes("Attributes linked", "Attributes linked, comma separated atributes", "attributes", &wr, this,""), + style_attributes("Style attributes linked", "Style attributes linked, comma separated atributes", "style_attributes", &wr, this,""), + expanded(false), + origin(Geom::Point(0,0)) +{ + registerParameter(&linked_path); + registerParameter(&linked_item); + registerParameter(&scale); + registerParameter(&attributes); + registerParameter(&style_attributes); + registerParameter(&preserve_position); + registerParameter(&inverse); + registerParameter(&d); + registerParameter(&transform); + registerParameter(&fill); + registerParameter(&stroke); + registerParameter(&paintorder); + registerParameter(&opacity); + registerParameter(&filter); + scale.param_set_range(0.01, 999999.0); + scale.param_set_increments(1, 1); + scale.param_set_digits(2); + attributes.param_hide_canvas_text(); + style_attributes.param_hide_canvas_text(); + preserve_position_changed = preserve_position; + preserve_affine = Geom::identity(); +} + +void +LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root) { - registerParameter( dynamic_cast<Parameter *>(&linked_path) ); + SPDocument * document = SP_ACTIVE_DOCUMENT; + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneAttrbutes((*obj_it), dest_child, live, attributes, style_attributes, false); + index++; + } + } + //Attributes + SPShape * shape_origin = SP_SHAPE(origin); + SPPath * path_origin = SP_PATH(origin); + SPShape * shape_dest = SP_SHAPE(dest); + SPMask *mask_origin = SP_ITEM(origin)->mask_ref->getObject(); + SPMask *mask_dest = SP_ITEM(dest)->mask_ref->getObject(); + if(mask_origin && mask_dest) { + std::vector<SPObject*> mask_list = mask_origin->childList(true); + std::vector<SPObject*> mask_list_dest = mask_dest->childList(true); + if (mask_list.size() == mask_list_dest.size()) { + size_t i = 0; + for ( std::vector<SPObject*>::const_iterator iter=mask_list.begin();iter!=mask_list.end();++iter) { + SPObject * mask_data = *iter; + SPObject * mask_dest_data = mask_list_dest[i]; + cloneAttrbutes(mask_data, mask_dest_data, live, attributes, style_attributes, false); + i++; + } + } + } + SPClipPath *clippath_origin = SP_ITEM(origin)->clip_ref->getObject(); + SPClipPath *clippath_dest = SP_ITEM(dest)->clip_ref->getObject(); + if(clippath_origin && clippath_dest) { + std::vector<SPObject*> clippath_list = clippath_origin->childList(true); + std::vector<SPObject*> clippath_list_dest = clippath_dest->childList(true); + if (clippath_list.size() == clippath_list_dest.size()) { + size_t i = 0; + for ( std::vector<SPObject*>::const_iterator iter=clippath_list.begin();iter!=clippath_list.end();++iter) { + SPObject * clippath_data = *iter; + SPObject * clippath_dest_data = clippath_list_dest[i]; + cloneAttrbutes(clippath_data, clippath_dest_data, live, attributes, style_attributes, false); + i++; + } + } + } + gchar ** attarray = g_strsplit(attributes, ",", 0); + gchar ** iter = attarray; + Geom::Affine affine_dest = Geom::identity(); + Geom::Affine affine_origin = Geom::identity(); + Geom::Affine affine_previous = Geom::identity(); + sp_svg_transform_read(SP_ITEM(dest)->getAttribute("transform"), &affine_dest); + sp_svg_transform_read(SP_ITEM(origin)->getAttribute("transform"), &affine_origin); + while (*iter != NULL) { + const char* attribute = (*iter); + if ( std::strcmp(attribute, "transform") == 0 ) { + if (preserve_position) { + Geom::Affine dest_affine = Geom::identity(); + if (root) { + dest_affine *= affine_origin; + if (preserve_affine == Geom::identity()) { + dest_affine *= Geom::Translate(affine_dest.translation()); + } + dest_affine *= Geom::Translate(affine_origin.translation()).inverse(); + dest_affine *= Geom::Translate(preserve_affine.translation()); + affine_previous = preserve_affine; + preserve_affine = Geom::identity(); + SP_ITEM(dest)->getRepr()->setAttribute("transform",sp_svg_transform_write(dest_affine)); + } + } else { + SP_ITEM(dest)->getRepr()->setAttribute("transform",sp_svg_transform_write(affine_origin)); + } + } else if ( shape_dest && shape_origin && live && (std::strcmp(attribute, "d") == 0)) { + SPCurve *c = NULL; + if (inverse) { + c = shape_origin->getCurveBeforeLPE(); + } else { + c = shape_origin->getCurve(); + } + if (c) { + Geom::PathVector c_pv = c->get_pathvector(); + Geom::OptRect orig_bbox = SP_ITEM(origin)->geometricBounds(); + Geom::OptRect dest_bbox = SP_ITEM(dest)->geometricBounds(); + if (dest_bbox && orig_bbox && root) { + Geom::Point orig_point = (*orig_bbox).corner(0); + Geom::Point dest_point = (*dest_bbox).corner(0); + if (scale != 100.0) { + double scale_affine = scale/100.0; + Geom::Scale scale = Geom::Scale(scale_affine); + c_pv *= Geom::Translate(orig_point).inverse(); + c_pv *= scale; + c_pv *= Geom::Translate(orig_point); + } + if (preserve_position) { + c_pv *= Geom::Translate(dest_point - orig_point); + } + } + if (inverse) { + c_pv *= i2anc_affine(origin, sp_lpe_item); + } else { + c_pv *= i2anc_affine(dest, sp_lpe_item); + } + c->set_pathvector(c_pv); + if (!path_origin) { + shape_dest->setCurveInsync(c, TRUE); + dest->getRepr()->setAttribute(attribute, sp_svg_write_path(c_pv)); + } else { + shape_dest->setCurve(c, TRUE); + } + c->unref(); + } else { + dest->getRepr()->setAttribute(attribute, NULL); + } + } else { + dest->getRepr()->setAttribute(attribute, origin->getRepr()->attribute(attribute)); + } + iter++; + } + g_strfreev (attarray); + SPCSSAttr *css_origin = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css_origin, origin->getRepr()->attribute("style")); + SPCSSAttr *css_dest = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css_dest, dest->getRepr()->attribute("style")); + gchar ** styleattarray = g_strsplit(style_attributes, ",", 0); + gchar ** styleiter = styleattarray; + while (*styleiter != NULL) { + const char* attribute = (*styleiter); + const char* origin_attribute = sp_repr_css_property(css_origin, attribute, ""); + if (origin_attribute == "") { + sp_repr_css_set_property (css_dest, attribute, NULL); + } else { + sp_repr_css_set_property (css_dest, attribute, origin_attribute); + } + styleiter++; + } + g_strfreev (styleattarray); + Glib::ustring css_str; + sp_repr_css_write_string(css_dest,css_str); + dest->getRepr()->setAttribute("style", css_str.c_str()); +} + +void +LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){ + if (linked_path.linksToPath()) { //Legacy staff + Glib::ustring attributes_value("d"); + attributes.param_setValue(attributes_value); + attributes.write_to_SVG(); + Glib::ustring style_attributes_value(""); + style_attributes.param_setValue(style_attributes_value); + style_attributes.write_to_SVG(); + linked_item.param_readSVGValue(linked_path.param_getSVGValue()); + linked_path.param_readSVGValue(""); + } + + if (linked_item.linksToItem()) { + linked_item.setInverse(inverse); + if ( preserve_position_changed != preserve_position ) { + if (!preserve_position) { + sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &preserve_affine); + } + preserve_position_changed = preserve_position; + } + Glib::ustring attr = ""; + if (d) { + attr.append("d,"); + } + if (transform) { + attr.append("transform,"); + } + attr.append(Glib::ustring(attributes.param_getSVGValue()).append(",")); + if (attr.size() && !Glib::ustring(attributes.param_getSVGValue()).size()) { + attr.erase (attr.size()-1, 1); + } + Glib::ustring style_attr = ""; + if (fill) { + style_attr.append("fill,").append("fill-rule,"); + } + if (stroke) { + style_attr.append("stroke,").append("stroke-width,").append("stroke-linecap,").append("stroke-linejoin,"); + style_attr.append("stroke-opacity,").append("stroke-miterlimit,").append("stroke-dasharray,"); + style_attr.append("stroke-opacity,").append("stroke-dashoffset,").append("marker-start,"); + style_attr.append("marker-mid,").append("marker-end,"); + } + if (paintorder) { + style_attr.append("paint-order,"); + } + if (filter) { + style_attr.append("filter,"); + } + if (opacity) { + style_attr.append("opacity,"); + } + if (style_attr.size() && !Glib::ustring(style_attributes.param_getSVGValue()).size()) { + style_attr.erase (style_attr.size()-1, 1); + } + style_attr.append(Glib::ustring(style_attributes.param_getSVGValue()).append(",")); + + SPItem * from = inverse ? SP_ITEM(sp_lpe_item) : SP_ITEM(linked_item.getObject()); + SPItem * to = !inverse ? SP_ITEM(sp_lpe_item) : SP_ITEM(linked_item.getObject()); + cloneAttrbutes(from, to, true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true); + Geom::OptRect bbox = from->geometricBounds(); + if (bbox && preserve_position && origin != Geom::Point(0,0)) { + origin = (*bbox).corner(0) - origin; + to->transform *= Geom::Translate(origin); + } + bbox = from->geometricBounds(); + if (bbox && preserve_position) { + origin = (*bbox).corner(0); + } + } +} + + +Gtk::Widget * +LPECloneOriginal::newWidget() +{ + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); + vbox_expander->set_border_width(0); + vbox_expander->set_spacing(2); + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter * param = *it; + if (param->param_key == "linkedpath") { + ++it; + continue; + } + Gtk::Widget * widg = param->param_newWidget(); + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + if (param->param_key != "attributes" && + param->param_key != "style_attributes") { + vbox->pack_start(*widg, true, true, 2); + } else { + vbox_expander->pack_start(*widg, true, true, 2); + } + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + + ++it; + } + expander = Gtk::manage(new Gtk::Expander(Glib::ustring(_("Show attributes override")))); + expander->add(*vbox_expander); + expander->set_expanded(expanded); + expander->property_expanded().signal_changed().connect(sigc::mem_fun(*this, &LPECloneOriginal::onExpanderChanged) ); + vbox->pack_start(*expander, true, true, 2); + this->upd_params = false; + return dynamic_cast<Gtk::Widget *>(vbox); +} + +void +LPECloneOriginal::onExpanderChanged() +{ + expanded = expander->get_expanded(); + if(expanded) { + expander->set_label (Glib::ustring(_("Hide attributes override"))); + } else { + expander->set_label (Glib::ustring(_("Show attributes override"))); + } } LPECloneOriginal::~LPECloneOriginal() @@ -24,12 +340,22 @@ LPECloneOriginal::~LPECloneOriginal() } -void LPECloneOriginal::doEffect (SPCurve * curve) +void +LPECloneOriginal::transform_multiply(Geom::Affine const& postmul, bool set) +{ + if (linked_item.linksToItem()) { + bool changed = false; + linked_item.getObject()->requestModified(SP_OBJECT_MODIFIED_FLAG); + } +} + +void +LPECloneOriginal::doEffect (SPCurve * curve) { - if ( linked_path.linksToPath() ) { - Geom::PathVector linked_pathv = linked_path.get_pathvector(); - if ( !linked_pathv.empty() ) { - curve->set_pathvector(linked_pathv); + if (linked_item.linksToItem() && !inverse) { + SPShape * shape = getCurrentShape(); + if(shape){ + curve->set_pathvector(shape->getCurve()->get_pathvector()); } } } diff --git a/src/live_effects/lpe-clone-original.h b/src/live_effects/lpe-clone-original.h index abf65ded8..e4328c169 100644 --- a/src/live_effects/lpe-clone-original.h +++ b/src/live_effects/lpe-clone-original.h @@ -8,24 +8,49 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - +#include <gtkmm/expander.h> #include "live_effects/effect.h" +#include "live_effects/parameter/originalitem.h" #include "live_effects/parameter/originalpath.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/point.h" +#include "live_effects/parameter/text.h" +#include "live_effects/lpegroupbbox.h" namespace Inkscape { namespace LivePathEffect { -class LPECloneOriginal : public Effect { +class LPECloneOriginal : public Effect, GroupBBoxEffect { public: LPECloneOriginal(LivePathEffectObject *lpeobject); virtual ~LPECloneOriginal(); - virtual void doEffect (SPCurve * curve); + virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void transform_multiply(Geom::Affine const& postmul, bool set); + virtual Gtk::Widget * newWidget(); + void onExpanderChanged(); + void cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root); private: OriginalPathParam linked_path; - -private: + OriginalItemParam linked_item; + ScalarParam scale; + BoolParam preserve_position; + BoolParam inverse; + BoolParam d; + BoolParam transform; + BoolParam fill; + BoolParam stroke; + BoolParam paintorder; + BoolParam opacity; + BoolParam filter; + TextParam attributes; + TextParam style_attributes; + Geom::Point origin; + bool preserve_position_changed; + bool expanded; + Gtk::Expander * expander; + Geom::Affine preserve_affine; LPECloneOriginal(const LPECloneOriginal&); LPECloneOriginal& operator=(const LPECloneOriginal&); }; diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index 813f25d3d..42e055062 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -17,6 +17,13 @@ #include <2geom/sbasis-to-bezier.h> #include <2geom/intersection-graph.h> #include "live_effects/lpe-copy_rotate.h" +#include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "style.h" +#include "helper/geom.h" +#include "xml/sp-css-attr.h" +#include "path-chemistry.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -45,9 +52,11 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : starting_angle(_("Starting angle"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0), rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0), num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 6), + split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001), copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true), fuse_paths(_("Kaleidoskope"), _("Kaleidoskope by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false), join_paths(_("Join paths"), _("Join paths, use fill-rule: evenodd for best result"), "join_paths", &wr, this, false), + split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false), dist_angle_handle(100.0) { show_orig_path = true; @@ -56,15 +65,21 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : registerParameter(&copies_to_360); registerParameter(&fuse_paths); registerParameter(&join_paths); + registerParameter(&split_items); registerParameter(&starting_angle); registerParameter(&starting_point); registerParameter(&rotation_angle); registerParameter(&num_copies); + registerParameter(&split_gap); registerParameter(&origin); - + split_gap.param_set_range(-999999.0, 999999.0); + split_gap.param_set_increments(0.1, 0.1); + split_gap.param_set_digits(5); + num_copies.param_set_range(0, 999999); num_copies.param_make_integer(true); - num_copies.param_set_range(0, 1000); apply_to_clippath_and_mask = true; + previous_num_copies = num_copies; + reset = false; } LPECopyRotate::~LPECopyRotate() @@ -72,6 +87,207 @@ LPECopyRotate::~LPECopyRotate() } +void +LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem) +{ + if (split_items) { + SPDocument * document = SP_ACTIVE_DOCUMENT; + items.clear(); + container = dynamic_cast<SPObject *>(sp_lpe_item->parent); + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); + Inkscape::XML::Node *root_origin = doc->getReprRoot(); + if (root_origin != root) { + return; + } + if (previous_num_copies != num_copies) { + gint numcopies_gap = previous_num_copies - num_copies; + if (numcopies_gap > 0 && num_copies != 0) { + guint counter = num_copies - 1; + while (numcopies_gap > 0) { + const char * id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + if (!id || strlen(id) == 0) { + return; + } + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + SP_ITEM(elemref)->setHidden(true); + } + counter++; + numcopies_gap--; + } + } + previous_num_copies = num_copies; + } + SPObject *elemref = NULL; + char * id = g_strdup(Glib::ustring("rotated-").append("1").append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + guint counter = 0; + while(elemref = document->getObjectById(id)) { + if (SP_ITEM(elemref)->isHidden()) { + items.push_back(id); + } + id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + counter++; + } + g_free(id); + double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + double size_divider = Geom::distance(origin,bbox) + (diagonal * 2); + Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider; + Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider; + Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))); + if (fuse_paths) { + size_t rest = 0; + for (size_t i = 1; i < num_copies; ++i) { + Geom::Affine r = Geom::identity(); + Geom::Point dir = unit_vector((Geom::Point)origin - Geom::middle_point(line_start,line_end)); + if( rest%2 == 0) { + r *= Geom::Rotate(Geom::Angle(dir)).inverse(); + r *= Geom::Scale(1, -1); + r *= Geom::Rotate(Geom::Angle(dir)); + } + Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i))); + Geom::Affine t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + if( rest%2 == 0) { + t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)).inverse() * Geom::Translate(origin); + } + t *= sp_lpe_item->transform; + toItem(t, i-1, reset); + rest ++; + } + } else { + for (size_t i = 1; i < num_copies; ++i) { + Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i))); + Geom::Affine t = m * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + t *= sp_lpe_item->transform; + toItem(t, i - 1, reset); + } + } + reset = false; + } else { + processObjects(LPE_ERASE); + items.clear(); + } + + std::cout << previous_num_copies << "previous_num_copies\n"; + std::cout << num_copies << "num_copies\n"; +} + +void +LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneD(*obj_it, dest_child, false, reset); + index++; + } + } + SPShape * shape = SP_SHAPE(origin); + SPPath * path = SP_PATH(dest); + if (!path && !SP_IS_GROUP(dest)) { + Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); + dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); + path = SP_PATH(dest); + } + if (path && shape) { + SPCurve *c = NULL; + if (root) { + c = new SPCurve(); + c->set_pathvector(pathvector_before_effect); + } else { + c = shape->getCurve(); + } + if (c) { + path->setCurve(c, TRUE); + c->unref(); + } else { + dest->getRepr()->setAttribute("d", NULL); + } + if (reset) { + dest->getRepr()->setAttribute("style", shape->getRepr()->attribute("style")); + } + } +} + +void +LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + items.push_back(elemref_id); + SPObject *elemref= NULL; + Inkscape::XML::Node *phantom = NULL; + if (elemref = document->getObjectById(elemref_id)) { + phantom = elemref->getRepr(); + } else { + phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); + std::vector<const char *> attrs; + attrs.push_back("inkscape:path-effect"); + attrs.push_back("inkscape:original-d"); + attrs.push_back("sodipodi:type"); + attrs.push_back("sodipodi:rx"); + attrs.push_back("sodipodi:ry"); + attrs.push_back("sodipodi:cx"); + attrs.push_back("sodipodi:cy"); + attrs.push_back("sodipodi:end"); + attrs.push_back("sodipodi:start"); + attrs.push_back("inkscape:flatsided"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:rounded"); + attrs.push_back("sodipodi:arg1"); + attrs.push_back("sodipodi:arg2"); + attrs.push_back("sodipodi:r1"); + attrs.push_back("sodipodi:r2"); + attrs.push_back("sodipodi:sides"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("sodipodi:argument"); + attrs.push_back("sodipodi:expansion"); + attrs.push_back("sodipodi:radius"); + attrs.push_back("sodipodi:revolution"); + attrs.push_back("sodipodi:t0"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("x"); + attrs.push_back("y"); + attrs.push_back("rx"); + attrs.push_back("ry"); + attrs.push_back("width"); + attrs.push_back("height"); + phantom->setAttribute("id", elemref_id); + for(const char * attr : attrs) { + phantom->setAttribute(attr, NULL); + } + } + if (!elemref) { + elemref = container->appendChildRepr(phantom); + Inkscape::GC::release(phantom); + } + cloneD(SP_OBJECT(sp_lpe_item), elemref, true, reset); + elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); + SP_ITEM(elemref)->setHidden(false); + if (elemref->parent != container) { + Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); + copy->setAttribute("id", elemref_id); + container->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + } +} + +void +LPECopyRotate::resetStyles(){ + reset = true; + doAfterEffect(sp_lpe_item); +} + Gtk::Widget * LPECopyRotate::newWidget() { // use manage here, because after deletion of Effect object, others might @@ -81,7 +297,15 @@ Gtk::Widget * LPECopyRotate::newWidget() vbox->set_border_width(5); vbox->set_homogeneous(false); vbox->set_spacing(2); - + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); + Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); + vbox_expander->set_border_width(0); + vbox_expander->set_spacing(2); + Gtk::Button * reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles")))); + reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPECopyRotate::resetStyles)); + reset_button->set_size_request(140,30); + vbox->pack_start(*hbox, true,true,2); + hbox->pack_start(*reset_button, false, false,2); std::vector<Parameter *>::iterator it = param_vector.begin(); while (it != param_vector.end()) { if ((*it)->widget_is_visible) { @@ -125,11 +349,11 @@ void LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set) { // cycle through all parameters. Most parameters will not need transformation, but path and point params do. - for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) { Parameter * param = *it; param->param_transform_multiply(postmul, set); } + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); } void @@ -140,17 +364,17 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) if (copies_to_360) { rotation_angle.param_set_value(360.0/(double)num_copies); } - if (fuse_paths && rotation_angle * num_copies > 360 && rotation_angle > 0) { + if (fuse_paths && rotation_angle * num_copies > 360.1 && rotation_angle > 0) { num_copies.param_set_value(floor(360/rotation_angle)); } if (fuse_paths && copies_to_360) { - num_copies.param_set_increments(2,2); + num_copies.param_set_increments(2.0,10.0); if ((int)num_copies%2 !=0) { num_copies.param_set_value(num_copies+1); rotation_angle.param_set_value(360.0/(double)num_copies); } } else { - num_copies.param_set_increments(1,1); + num_copies.param_set_increments(1.0, 10.0); } if (dist_angle_handle < 1.0) { @@ -352,12 +576,56 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s tmp_path.clear(); } +Geom::PathVector +LPECopyRotate::doEffect_path (Geom::PathVector const & path_in) +{ + Geom::PathVector path_out; + if (split_items && (fuse_paths || join_paths)) { + if (num_copies == 0) { + return path_out; + } + path_out = pathv_to_linear_and_cubic_beziers(path_in); + double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + double size_divider = Geom::distance(origin,bbox) + (diagonal * 2); + Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider; + Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider; + Geom::Path divider = Geom::Path(line_start); + divider.appendNew<Geom::LineSegment>((Geom::Point)origin); + divider.appendNew<Geom::LineSegment>(line_end); + divider.close(); + Geom::PathVector triangle; + triangle.push_back(divider); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(triangle, path_out); + if (pig && ! path_out.empty() && !triangle.empty()) { + //TODO: Here can produce a crash because some knows problems in new boolops code + path_out = pig->getIntersection(); + } + Geom::Affine r = Geom::identity(); + Geom::Point dir = unit_vector(Geom::middle_point(line_start,line_end) - (Geom::Point)origin); + Geom::Point gap = dir * split_gap; + path_out *= Geom::Translate(gap); + } else { + // default behavior + for (unsigned int i=0; i < path_in.size(); i++) { + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb(); + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in); + Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE); + // add the output path vector to the already accumulated vector: + for (unsigned int j=0; j < path.size(); j++) { + path_out.push_back(path[j]); + } + } + } + return pathv_to_linear_and_cubic_beziers(path_out); +} + Geom::Piecewise<Geom::D2<Geom::SBasis> > LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) { using namespace Geom; - if (num_copies == 1 && !fuse_paths) { + if ((num_copies == 1 && !fuse_paths) || split_items) { return pwd2_in; } @@ -373,10 +641,10 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p divider.appendNew<Geom::LineSegment>(line_end); Piecewise<D2<SBasis> > output; Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle)); + PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001)); if (fuse_paths) { Geom::PathVector path_out; Geom::PathVector tmp_path; - PathVector const original_pathv = path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001); for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) { continue; @@ -403,7 +671,7 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p output = paths_to_pw(path_out); } } else { - Geom::PathVector output_pv = path_from_piecewise(output , 0.01); + Geom::PathVector output_pv; for (int i = 0; i < num_copies; ++i) { Rotate rot(-rad_from_deg(rotation_angle * i)); Affine t = pre * rot * Translate(origin); @@ -449,6 +717,23 @@ LPECopyRotate::resetDefaults(SPItem const* item) original_bbox(SP_LPE_ITEM(item)); } +void +LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPECopyRotate::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //unset "erase_extra_objects" hook on sp-lpe-item.cpp + if (!erase_extra_objects) { + processObjects(LPE_TO_OBJECTS); + return; + } + processObjects(LPE_ERASE); +} + } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-copy_rotate.h b/src/live_effects/lpe-copy_rotate.h index c2ae2daf1..dbec2e1c3 100644 --- a/src/live_effects/lpe-copy_rotate.h +++ b/src/live_effects/lpe-copy_rotate.h @@ -15,6 +15,8 @@ */ #include "live_effects/effect.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/text.h" #include "live_effects/parameter/point.h" #include "live_effects/lpegroupbbox.h" @@ -26,13 +28,20 @@ public: LPECopyRotate(LivePathEffectObject *lpeobject); virtual ~LPECopyRotate(); virtual void doOnApply (SPLPEItem const* lpeitem); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void doAfterEffect (SPLPEItem const* lpeitem); virtual void setFusion(Geom::PathVector &path_in, Geom::Path divider, double sizeDivider); virtual void split(Geom::PathVector &path_in, Geom::Path const ÷r); virtual void resetDefaults(SPItem const* item); virtual void transform_multiply(Geom::Affine const& postmul, bool set); + virtual void doOnRemove (SPLPEItem const* /*lpeitem*/); + virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Gtk::Widget * newWidget(); + void toItem(Geom::Affine transform, size_t i, bool reset); + void cloneD(SPObject *origin, SPObject *dest, bool root, bool reset); + void resetStyles(); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); @@ -42,9 +51,11 @@ private: ScalarParam starting_angle; ScalarParam rotation_angle; ScalarParam num_copies; + ScalarParam split_gap; BoolParam copies_to_360; BoolParam fuse_paths; BoolParam join_paths; + BoolParam split_items; Geom::Point A; Geom::Point B; Geom::Point dir; @@ -52,6 +63,9 @@ private: Geom::Point rot_pos; Geom::Point previous_start_point; double dist_angle_handle; + double previous_num_copies; + bool reset; + SPObject * container; LPECopyRotate(const LPECopyRotate&); LPECopyRotate& operator=(const LPECopyRotate&); }; diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp index 38cbeaac0..f2fc00aeb 100644 --- a/src/live_effects/lpe-curvestitch.cpp +++ b/src/live_effects/lpe-curvestitch.cpp @@ -54,6 +54,7 @@ LPECurveStitch::LPECurveStitch(LivePathEffectObject *lpeobject) : prop_scale.param_set_digits(3); prop_scale.param_set_increments(0.01, 0.10); + transformed = false; } LPECurveStitch::~LPECurveStitch() @@ -106,8 +107,9 @@ LPECurveStitch::doEffect_path (Geom::PathVector const & path_in) if (!Geom::are_near(start,end)) { gdouble scaling_y = 1.0; - if (scale_y_rel.get_value()) { + if (scale_y_rel.get_value() || transformed) { scaling_y = (L2(end-start)/scaling)*prop_scale; + transformed = false; } else { scaling_y = prop_scale; } @@ -193,12 +195,8 @@ LPECurveStitch::transform_multiply(Geom::Affine const& postmul, bool set) if (postmul.isTranslation()) { strokepath.param_transform_multiply(postmul, set); } else if (!scale_y_rel.get_value()) { - // this basically means that for this transformation, the result should be the same as normal scaling the result path - // don't know how to do this yet. -// Geom::Affine new_postmul; - //new_postmul.setIdentity(); -// new_postmul.setTranslation(postmul.translation()); -// Effect::transform_multiply(new_postmul, set); + transformed = true; + strokepath.param_transform_multiply(postmul, set); } } diff --git a/src/live_effects/lpe-curvestitch.h b/src/live_effects/lpe-curvestitch.h index c6ea66f6c..0a48046e0 100644 --- a/src/live_effects/lpe-curvestitch.h +++ b/src/live_effects/lpe-curvestitch.h @@ -43,6 +43,7 @@ private: RandomParam endpoint_spacing_variation; ScalarParam prop_scale; BoolParam scale_y_rel; + bool transformed; LPECurveStitch(const LPECurveStitch&); LPECurveStitch& operator=(const LPECurveStitch&); diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp index 1e2eadfdb..2690ce160 100644 --- a/src/live_effects/lpe-fill-between-many.cpp +++ b/src/live_effects/lpe-fill-between-many.cpp @@ -19,9 +19,11 @@ namespace LivePathEffect { LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) : Effect(lpeobject), - linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this) + linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this), + fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this) { registerParameter( dynamic_cast<Parameter *>(&linked_paths) ); + registerParameter( dynamic_cast<Parameter *>(&fuse) ); //perceived_path = true; } @@ -46,7 +48,11 @@ void LPEFillBetweenMany::doEffect (SPCurve * curve) if (!res_pathv.empty()) { linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj); - res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint()); + if (!are_near(res_pathv.front().finalPoint(), linked_path.initialPoint(), 0.01) || !fuse) { + res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint()); + } else { + linked_path.setInitial(res_pathv.front().finalPoint()); + } res_pathv.front().append(linked_path); } else { firstObj = SP_ITEM(obj); diff --git a/src/live_effects/lpe-fill-between-many.h b/src/live_effects/lpe-fill-between-many.h index 99ee8b15f..552c8037d 100644 --- a/src/live_effects/lpe-fill-between-many.h +++ b/src/live_effects/lpe-fill-between-many.h @@ -24,6 +24,7 @@ public: private: OriginalPathArrayParam linked_paths; + BoolParam fuse; private: LPEFillBetweenMany(const LPEFillBetweenMany&); diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp index 0dbebdf26..8dc55357f 100644 --- a/src/live_effects/lpe-fill-between-strokes.cpp +++ b/src/live_effects/lpe-fill-between-strokes.cpp @@ -18,11 +18,15 @@ LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) : Effect(lpeobject), linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this), second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this), - reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this) + reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this), + close(_("Close path"), _("Close path"), "close", &wr, this), + fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this) { registerParameter( dynamic_cast<Parameter *>(&linked_path) ); registerParameter( dynamic_cast<Parameter *>(&second_path) ); registerParameter( dynamic_cast<Parameter *>(&reverse_second) ); + registerParameter( dynamic_cast<Parameter *>(&close) ); + registerParameter( dynamic_cast<Parameter *>(&fuse) ); //perceived_path = true; } @@ -51,22 +55,22 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) } if ( !result_linked_pathv.empty() && !result_second_pathv.empty() && !result_linked_pathv.front().closed() ) { - if (reverse_second.get_value()) - { - result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint()); - result_linked_pathv.front().append(result_second_pathv.front().reversed()); + if (reverse_second.get_value()) { + result_second_pathv.front() = result_second_pathv.front().reversed(); } - else - { + if (!are_near(result_linked_pathv.front().finalPoint(), result_second_pathv.front().initialPoint(),0.01) || !fuse) { result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint()); - result_linked_pathv.front().append(result_second_pathv.front()); + } else { + result_second_pathv.front().setInitial(result_linked_pathv.front().finalPoint()); + } + result_linked_pathv.front().append(result_second_pathv.front()); + if (close) { + result_linked_pathv.front().close(); } curve->set_pathvector(result_linked_pathv); - } - else if ( !result_linked_pathv.empty() ) { + } else if ( !result_linked_pathv.empty() ) { curve->set_pathvector(result_linked_pathv); - } - else if ( !result_second_pathv.empty() ) { + } else if ( !result_second_pathv.empty() ) { curve->set_pathvector(result_second_pathv); } } diff --git a/src/live_effects/lpe-fill-between-strokes.h b/src/live_effects/lpe-fill-between-strokes.h index ec57b1852..14eb9167c 100644 --- a/src/live_effects/lpe-fill-between-strokes.h +++ b/src/live_effects/lpe-fill-between-strokes.h @@ -26,6 +26,8 @@ private: OriginalPathParam linked_path; OriginalPathParam second_path; BoolParam reverse_second; + BoolParam close; + BoolParam fuse; private: LPEFillBetweenStrokes(const LPEFillBetweenStrokes&); diff --git a/src/live_effects/lpe-measure-line.cpp b/src/live_effects/lpe-measure-line.cpp index af2a8e919..ef87be81c 100644 --- a/src/live_effects/lpe-measure-line.cpp +++ b/src/live_effects/lpe-measure-line.cpp @@ -21,7 +21,9 @@ #include "svg/svg-color.h" #include "svg/svg.h" #include "display/curve.h" +#include "helper/geom.h" #include "2geom/affine.h" +#include "path-chemistry.h" #include "style.h" #include "sp-root.h" #include "sp-defs.h" @@ -165,12 +167,6 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : LPEMeasureLine::~LPEMeasureLine() {} -void swap(Geom::Point &A, Geom::Point &B){ - Geom::Point tmp = A; - A = B; - B = tmp; -} - void LPEMeasureLine::createArrowMarker(const char * mode) { @@ -229,7 +225,7 @@ LPEMeasureLine::createArrowMarker(const char * mode) elemref = SP_OBJECT(document->getDefs()->appendChildRepr(arrow)); Inkscape::GC::release(arrow); } - elements.push_back(mode); + items.push_back(mode); } void @@ -369,7 +365,7 @@ LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angl copy->setAttribute("id", id); elemref = elemref_copy; } - elements.push_back(id); + items.push_back(id); Geom::OptRect bounds = SP_ITEM(elemref)->bounds(SPItem::GEOMETRIC_BBOX); if (bounds) { anotation_width = bounds->width() * 1.4; @@ -483,7 +479,7 @@ LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, const char * id, b elemref->deleteObject(); copy->setAttribute("id", id); } - elements.push_back(id); + items.push_back(id); } void @@ -535,7 +531,7 @@ LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem) sp_lpe_item->getCurrentLPE() != this){ return; } - elements.clear(); + items.clear(); start_stored = start; end_stored = end; Geom::Point hstart = start; @@ -680,60 +676,12 @@ LPEMeasureLine::doOnRemove (SPLPEItem const* /*lpeitem*/) //unset "erase_extra_objects" hook on sp-lpe-item.cpp if (!erase_extra_objects) { processObjects(LPE_TO_OBJECTS); - elements.clear(); + items.clear(); return; } processObjects(LPE_ERASE); } -void -LPEMeasureLine::processObjects(LpeAction lpe_action) -{ - SPDocument * document = SP_ACTIVE_DOCUMENT; - for (std::vector<const char *>::iterator el_it = elements.begin(); - el_it != elements.end(); ++el_it) { - const char * id = *el_it; - if (!id || strlen(id) == 0) { - return; - } - SPObject *elemref = NULL; - if (elemref = document->getObjectById(id)) { - SPCSSAttr *css; - Glib::ustring css_str; - switch (lpe_action){ - case LPE_TO_OBJECTS: - elemref->getRepr()->setAttribute("inkscape:path-effect", NULL); - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); - break; - - case LPE_ERASE: - if (std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) { - elemref->deleteObject(); - } - break; - - case LPE_VISIBILITY: - css = sp_repr_css_attr_new(); - sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); - if (!this->isVisible() && std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) { - css->setAttribute("display", "none"); - } else { - css->setAttribute("display", NULL); - } - sp_repr_css_write_string(css,css_str); - elemref->getRepr()->setAttribute("style", css_str.c_str()); - break; - - default: - break; - } - } - } - if (lpe_action == LPE_ERASE) { - elements.clear(); - } -} - Gtk::Widget *LPEMeasureLine::newWidget() { // use manage here, because after deletion of Effect object, others might diff --git a/src/live_effects/lpe-measure-line.h b/src/live_effects/lpe-measure-line.h index c69921a4d..724c0d924 100644 --- a/src/live_effects/lpe-measure-line.h +++ b/src/live_effects/lpe-measure-line.h @@ -35,12 +35,6 @@ enum OrientationMethod { OM_END }; -enum LpeAction { - LPE_ERASE = 0, - LPE_TO_OBJECTS, - LPE_VISIBILITY -}; - class LPEMeasureLine : public Effect { public: LPEMeasureLine(LivePathEffectObject *lpeobject); @@ -51,7 +45,6 @@ public: virtual void doEffect (SPCurve * curve){}; //stop the chain virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in); - void processObjects(LpeAction lpe_action); void createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows = false); void createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid); void onExpanderChanged(); @@ -92,7 +85,6 @@ private: double arrow_gap; Geom::Point start_stored; Geom::Point end_stored; - std::vector<const char *> elements; /* Geom::Affine affine_over;*/ LPEMeasureLine(const LPEMeasureLine &); LPEMeasureLine &operator=(const LPEMeasureLine &); diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 61b2b8b5c..7f0a93c52 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -15,11 +15,22 @@ */ #include <gtkmm.h> +#include "live_effects/lpeobject.h" +#include "live_effects/lpeobject-reference.h" #include "live_effects/lpe-mirror_symmetry.h" -#include <display/curve.h> -#include <svg/path-string.h> +#include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "sp-defs.h" #include "helper/geom.h" -#include <2geom/path-intersection.h> +#include "2geom/intersection-graph.h" +#include "2geom/path-intersection.h" +#include "2geom/affine.h" +#include "helper/geom.h" +#include "sp-lpe-item.h" +#include "path-chemistry.h" +#include "style.h" +#include "xml/sp-css-attr.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -40,73 +51,69 @@ MTConverter(ModeTypeData, MT_END); LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) : Effect(lpeobject), mode(_("Mode"), _("Symmetry move mode"), "mode", MTConverter, &wr, this, MT_FREE), + split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001), discard_orig_path(_("Discard original path"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false), fuse_paths(_("Fuse paths"), _("Fuse original and the reflection into a single path"), "fuse_paths", &wr, this, false), oposite_fuse(_("Opposite fuse"), _("Picks the other side of the mirror as the original"), "oposite_fuse", &wr, this, false), + split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false), start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, _("Adjust start of mirroring")), end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, _("Adjust end of mirroring")), - center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")) + center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")), + id_origin("id origin", "store the id of the first LPEItem", "id_origin", &wr, this,"") { show_orig_path = true; registerParameter(&mode); - registerParameter( &discard_orig_path); - registerParameter( &fuse_paths); - registerParameter( &oposite_fuse); - registerParameter( &start_point); - registerParameter( &end_point); - registerParameter( ¢er_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(¢er_point); + registerParameter(&id_origin); + id_origin.param_hide_canvas_text(); + split_gap.param_set_range(-999999.0, 999999.0); + split_gap.param_set_increments(0.1, 0.1); + split_gap.param_set_digits(5); apply_to_clippath_and_mask = true; + previous_center = Geom::Point(0,0); } LPEMirrorSymmetry::~LPEMirrorSymmetry() { } -Gtk::Widget * LPEMirrorSymmetry::newWidget() +void +LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem) { - // use manage here, because after deletion of Effect object, others might - // still be pointing to this widget. - Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); - - vbox->set_border_width(5); - vbox->set_homogeneous(false); - vbox->set_spacing(2); - - std::vector<Parameter *>::iterator it = param_vector.begin(); - while (it != param_vector.end()) { - if ((*it)->widget_is_visible) { - Parameter *param = *it; - Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); - Glib::ustring *tip = param->param_getTooltip(); - if (widg) { - if (param->param_key != "center_point") { - vbox->pack_start(*widg, true, true, 2); - if (tip) { - widg->set_tooltip_text(*tip); - } else { - widg->set_tooltip_text(""); - widg->set_has_tooltip(false); - } - } - } + if (split_items && !discard_orig_path) { + container = dynamic_cast<SPObject *>(sp_lpe_item->parent); + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); + Inkscape::XML::Node *root_origin = doc->getReprRoot(); + if (root_origin != root) { + return; } - - ++it; + Geom::Line ls((Geom::Point)start_point, (Geom::Point)end_point); + Geom::Affine m = Geom::reflection (ls.vector(), (Geom::Point)start_point); + m = m * sp_lpe_item->transform; + toMirror(m); + } else { + processObjects(LPE_ERASE); + items.clear(); } - return dynamic_cast<Gtk::Widget *>(vbox); } - void LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) { + using namespace Geom; original_bbox(lpeitem); //center_point->param_set_liveupdate(false); Point point_a(boundingbox_X.max(), boundingbox_Y.min()); Point point_b(boundingbox_X.max(), boundingbox_Y.max()); - Point point_c(boundingbox_X.max(), boundingbox_Y.middle()); if (mode == MT_Y) { point_a = Geom::Point(boundingbox_X.min(),center_point[Y]); point_b = Geom::Point(boundingbox_X.max(),center_point[Y]); @@ -173,6 +180,172 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) } void +LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneD(*obj_it, dest_child, live, false); + index++; + } + } + SPShape * shape = SP_SHAPE(origin); + SPPath * path = SP_PATH(dest); + if (!path && !SP_IS_GROUP(dest)) { + Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); + dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); + path = SP_PATH(dest); + } + if (path && shape) { + if ( live) { + SPCurve *c = NULL; + if (root) { + c = new SPCurve(); + c->set_pathvector(pathvector_before_effect); + } else { + c = shape->getCurve(); + } + if (c) { + path->setCurve(c, TRUE); + c->unref(); + } else { + dest->getRepr()->setAttribute("d", NULL); + } + } else { + dest->getRepr()->setAttribute("d", origin->getRepr()->attribute("d")); + } + } +} + +void +LPEMirrorSymmetry::toMirror(Geom::Affine transform) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + const char * id_origin_char = id_origin.param_getSVGValue(); + const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str()); + items.clear(); + items.push_back(elemref_id); + SPObject *elemref= NULL; + Inkscape::XML::Node *phantom = NULL; + if (elemref = document->getObjectById(elemref_id)) { + phantom = elemref->getRepr(); + } else { + phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); + std::vector<const char *> attrs; + attrs.push_back("inkscape:path-effect"); + attrs.push_back("inkscape:original-d"); + attrs.push_back("sodipodi:type"); + attrs.push_back("sodipodi:rx"); + attrs.push_back("sodipodi:ry"); + attrs.push_back("sodipodi:cx"); + attrs.push_back("sodipodi:cy"); + attrs.push_back("sodipodi:end"); + attrs.push_back("sodipodi:start"); + attrs.push_back("inkscape:flatsided"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:rounded"); + attrs.push_back("sodipodi:arg1"); + attrs.push_back("sodipodi:arg2"); + attrs.push_back("sodipodi:r1"); + attrs.push_back("sodipodi:r2"); + attrs.push_back("sodipodi:sides"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("sodipodi:argument"); + attrs.push_back("sodipodi:expansion"); + attrs.push_back("sodipodi:radius"); + attrs.push_back("sodipodi:revolution"); + attrs.push_back("sodipodi:t0"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("x"); + attrs.push_back("y"); + attrs.push_back("rx"); + attrs.push_back("ry"); + attrs.push_back("width"); + attrs.push_back("height"); + for(const char * attr : attrs) { + phantom->setAttribute(attr, NULL); + } + } + phantom->setAttribute("id", elemref_id); + if (!elemref) { + elemref = container->appendChildRepr(phantom); + Inkscape::GC::release(phantom); + } + cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true); + elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); + if (elemref->parent != container) { + Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); + copy->setAttribute("id", elemref_id); + container->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + } +} + +Gtk::Widget * +LPEMirrorSymmetry::newWidget() +{ + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter * param = *it; + if (param->param_key == "id_origin" || param->param_key == "center_point") { + ++it; + continue; + } + Gtk::Widget * widg = param->param_newWidget(); + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + vbox->pack_start(*widg, true, true, 2); + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + + ++it; + } + this->upd_params = false; + return dynamic_cast<Gtk::Widget *>(vbox); +} + +//TODO: Migrate the tree next function to effect.cpp/h to avoid duplication +void +LPEMirrorSymmetry::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPEMirrorSymmetry::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //unset "erase_extra_objects" hook on sp-lpe-item.cpp + if (!erase_extra_objects) { + processObjects(LPE_TO_OBJECTS); + return; + } + processObjects(LPE_ERASE); +} + +void LPEMirrorSymmetry::transform_multiply(Geom::Affine const& postmul, bool set) { // cycle through all parameters. Most parameters will not need transformation, but path and point params do. @@ -193,18 +366,26 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem) Point point_a(boundingbox_X.max(), boundingbox_Y.min()); Point point_b(boundingbox_X.max(), boundingbox_Y.max()); Point point_c(boundingbox_X.max(), boundingbox_Y.middle()); - start_point.param_setValue(point_a, true); + start_point.param_setValue(point_a); start_point.param_update_default(point_a); - end_point.param_setValue(point_b, true); + end_point.param_setValue(point_b); end_point.param_update_default(point_b); center_point.param_setValue(point_c, true); previous_center = center_point; + SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem); + if (!lpeitem->hasPathEffectOfType(this->effectType(), false) ){ //first applied not ready yet + id_origin.param_setValue(lpeitem->getRepr()->attribute("id")); + id_origin.write_to_SVG(); + } } Geom::PathVector LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) { + if (split_items && !fuse_paths) { + return path_in; + } Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); Geom::PathVector path_out; @@ -214,15 +395,51 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point); Geom::Affine m = Geom::reflection (line_separation.vector(), (Geom::Point)start_point); - - if (fuse_paths && !discard_orig_path) { + if (split_items && fuse_paths) { + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + Geom::Path p(Geom::Point(bbox->left(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top())); + p.close(); + p *= Geom::Translate(bbox->midpoint()).inverse(); + p *= Geom::Scale(1.2); + p *= Geom::Rotate(line_separation.angle()); + p *= Geom::Translate(bbox->midpoint()); + bbox = p.boundsFast(); + p.clear(); + p.start(Geom::Point(bbox->left(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom())); + p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top())); + p.close(); + p *= Geom::Translate(bbox->midpoint()).inverse(); + p *= Geom::Rotate(line_separation.angle()); + p *= Geom::Translate(bbox->midpoint()); + Geom::Point base(p.pointAt(3)); + if (oposite_fuse) { + base = p.pointAt(0); + } + p *= Geom::Translate(line_separation.pointAt(line_separation.nearestTime(base)) - base); + Geom::PathVector pv_bbox; + pv_bbox.push_back(p); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(pv_bbox, original_pathv); + if (pig && !original_pathv.empty() && !pv_bbox.empty()) { + path_out = pig->getBminusA(); + } + Geom::Point dir = rot90(unit_vector((Geom::Point)end_point - (Geom::Point)start_point)); + Geom::Point gap = dir * split_gap; + path_out *= Geom::Translate(gap); + } else if (fuse_paths && !discard_orig_path) { for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) { continue; } - Geom::PathVector tmp_path; + Geom::PathVector tmp_pathvector; double time_start = 0.0; int position = 0; bool end_open = false; @@ -268,11 +485,11 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) Geom::Path mirror = portion.reversed() * m; mirror.setInitial(portion.finalPoint()); portion.append(mirror); - if(i!=0) { + if(i != 0) { portion.setFinal(portion.initialPoint()); portion.close(); } - tmp_path.push_back(portion); + tmp_pathvector.push_back(portion); } portion.clear(); } @@ -293,36 +510,33 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) portion.append(mirror); portion = portion.reversed(); if (!original.closed()) { - tmp_path.push_back(portion); + tmp_pathvector.push_back(portion); } else { - if (cs.size() > 1 && tmp_path.size() > 0 && tmp_path[0].size() > 0 ) { - portion.setFinal(tmp_path[0].initialPoint()); - portion.setInitial(tmp_path[0].finalPoint()); - tmp_path[0].append(portion); + if (cs.size() > 1 && tmp_pathvector.size() > 0 && tmp_pathvector[0].size() > 0 ) { + portion.setFinal(tmp_pathvector[0].initialPoint()); + portion.setInitial(tmp_pathvector[0].finalPoint()); + tmp_pathvector[0].append(portion); } else { - tmp_path.push_back(portion); + tmp_pathvector.push_back(portion); } - tmp_path[0].close(); + tmp_pathvector[0].close(); } portion.clear(); } } } if (cs.size() == 0 && position == 1) { - tmp_path.push_back(original); - tmp_path.push_back(original * m); + tmp_pathvector.push_back(original); + tmp_pathvector.push_back(original * m); } - path_out.insert(path_out.end(), tmp_path.begin(), tmp_path.end()); - tmp_path.clear(); + path_out.insert(path_out.end(), tmp_pathvector.begin(), tmp_pathvector.end()); + tmp_pathvector.clear(); } - } - - if (!fuse_paths || discard_orig_path) { + } else if (!fuse_paths || discard_orig_path) { for (size_t i = 0; i < original_pathv.size(); ++i) { path_out.push_back(original_pathv[i] * m); } } - return path_out; } diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h index b4967173a..592a11894 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -18,8 +18,8 @@ #include "live_effects/effect.h" #include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/text.h" #include "live_effects/parameter/point.h" -#include "live_effects/parameter/path.h" #include "live_effects/parameter/enum.h" #include "live_effects/lpegroupbbox.h" @@ -41,23 +41,32 @@ public: virtual ~LPEMirrorSymmetry(); virtual void doOnApply (SPLPEItem const* lpeitem); virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void doAfterEffect (SPLPEItem const* lpeitem); virtual void transform_multiply(Geom::Affine const& postmul, bool set); virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); + virtual void doOnRemove (SPLPEItem const* /*lpeitem*/); + virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Gtk::Widget * newWidget(); + void toMirror(Geom::Affine transform); + // void cloneAttrbutes(Inkscape::XML::Node * origin, Inkscape::XML::Node * dest, const char * first_attribute, ...); + void cloneD(SPObject *origin, SPObject *dest, bool live, bool root); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); private: EnumParam<ModeType> mode; + ScalarParam split_gap; BoolParam discard_orig_path; BoolParam fuse_paths; BoolParam oposite_fuse; + BoolParam split_items; PointParam start_point; PointParam end_point; PointParam center_point; + TextParam id_origin; Geom::Point previous_center; - + SPObject * container; LPEMirrorSymmetry(const LPEMirrorSymmetry&); LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&); }; diff --git a/src/live_effects/lpe-patternalongpath.cpp b/src/live_effects/lpe-patternalongpath.cpp index c1853ef22..966e9020e 100644 --- a/src/live_effects/lpe-patternalongpath.cpp +++ b/src/live_effects/lpe-patternalongpath.cpp @@ -11,6 +11,7 @@ #include <2geom/bezier-to-sbasis.h> #include "knotholder.h" +#include <cmath> #include <algorithm> // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -161,7 +162,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con // spacing.param_set_range(-pattBndsX.extent()*.9, Geom::infinity()); // } - y0+=noffset; + y0 += noffset; std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in; paths_in = split_at_discontinuities(pwd2_in); @@ -197,7 +198,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con case PAPCT_REPEATED_STRETCHED: // if uskeleton is closed: - if(path_i.segs.front().at0() == path_i.segs.back().at1()){ + if (are_near(path_i.segs.front().at0(), path_i.segs.back().at1())){ nbCopies = std::max(1, static_cast<int>(std::floor((uskeleton.domain().extent() - toffset)/(pattBndsX->extent()+xspace)))); pattBndsX = Interval(pattBndsX->min(),pattBndsX->max()+xspace); scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX->extent()); @@ -213,11 +214,13 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con return pwd2_in; }; + //Ceil to 6 decimals + scaling = ceil(scaling * 1000000) / 1000000; double pattWidth = pattBndsX->extent() * scaling; - x*=scaling; + x *= scaling; if ( scale_y_rel.get_value() ) { - y*=(scaling * prop_scale); + y *= prop_scale * scaling; } else { y *= prop_scale; } @@ -235,7 +238,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con offs+=pattWidth; } } - if (fuse_tolerance > 0){ + if (fuse_tolerance > 0){ pre_output = fuse_nearby_ends(pre_output, fuse_tolerance); for (unsigned i=0; i<pre_output.size(); i++){ output.concat(pre_output[i]); @@ -264,7 +267,6 @@ LPEPatternAlongPath::transform_multiply(Geom::Affine const& postmul, bool set) pattern.param_transform_multiply(postmul, set); pattern.write_to_SVG(); } - sp_lpe_item_update_patheffect (sp_lpe_item, false, true); } void diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp index e0dac0687..18b5b724d 100644 --- a/src/live_effects/lpe-perspective-envelope.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -379,7 +379,7 @@ LPEPerspectiveEnvelope::newWidget() hbox_down_handles->pack_start(*widg, true, true, 2); } if (tip) { - widg->set_tooltip_text(*tip); + widg->set_tooltip_markup(*tip); } else { widg->set_tooltip_text(""); widg->set_has_tooltip(false); diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp index 1940806bd..83cd6623c 100644 --- a/src/live_effects/lpeobject-reference.cpp +++ b/src/live_effects/lpeobject-reference.cpp @@ -6,9 +6,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include "live_effects/lpeobject-reference.h" + #include <string.h> -#include "live_effects/lpeobject-reference.h" +#include "bad-uri-exception.h" #include "live_effects/lpeobject.h" #include "uri.h" diff --git a/src/live_effects/parameter/bool.cpp b/src/live_effects/parameter/bool.cpp index af99ef362..813c06b4e 100644 --- a/src/live_effects/parameter/bool.cpp +++ b/src/live_effects/parameter/bool.cpp @@ -72,7 +72,7 @@ BoolParam::param_newWidget() checkwdg->setActive(value); checkwdg->setProgrammatically = false; checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter")); - + param_effect->upd_params = false; return dynamic_cast<Gtk::Widget *> (checkwdg); } else { return NULL; @@ -82,6 +82,7 @@ BoolParam::param_newWidget() void BoolParam::param_setValue(bool newvalue) { + param_effect->upd_params = true; value = newvalue; } diff --git a/src/live_effects/parameter/item-reference.cpp b/src/live_effects/parameter/item-reference.cpp new file mode 100644 index 000000000..a775d93b7 --- /dev/null +++ b/src/live_effects/parameter/item-reference.cpp @@ -0,0 +1,44 @@ +/* + * The reference corresponding to href of LPE Item parameter. + * + * Copyright (C) 2008 Johan Engelen + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include "live_effects/parameter/item-reference.h" + +#include "sp-shape.h" +#include "sp-text.h" +#include "sp-item-group.h" + +namespace Inkscape { +namespace LivePathEffect { + +bool ItemReference::_acceptObject(SPObject * const obj) const +{ + if (SP_IS_SHAPE(obj) || SP_IS_TEXT(obj) || SP_IS_GROUP(obj)) { + /* Refuse references to lpeobject */ + if (obj == getOwner()) { + return false; + } + // TODO: check whether the referred item has this LPE applied, if so: deny deny deny! + return URIReference::_acceptObject(obj); + } else { + return false; + } +} + +} // namespace LivePathEffect +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/item-reference.h b/src/live_effects/parameter/item-reference.h new file mode 100644 index 000000000..91231455a --- /dev/null +++ b/src/live_effects/parameter/item-reference.h @@ -0,0 +1,56 @@ +#ifndef SEEN_LPE_ITEM_REFERENCE_H +#define SEEN_LPE_ITEM_REFERENCE_H + +/* + * Copyright (C) 2008-2012 Authors + * Authors: Johan Engelen + * Abhishek Sharma + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include <uri-references.h> + +class SPItem; +namespace Inkscape { +namespace XML { class Node; } + +namespace LivePathEffect { + +/** + * The reference corresponding to href of LPE ItemParam. + */ +class ItemReference : public Inkscape::URIReference { +public: + ItemReference(SPObject *owner) : URIReference(owner) {} + + SPItem *getObject() const { + return (SPItem *)URIReference::getObject(); + } + +protected: + virtual bool _acceptObject(SPObject * const obj) const; + +private: + ItemReference(const ItemReference&); + ItemReference& operator=(const ItemReference&); +}; + +} // namespace LivePathEffect + +} // namespace Inkscape + + + +#endif /* !SEEN_LPE_PATH_REFERENCE_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/item.cpp b/src/live_effects/parameter/item.cpp new file mode 100644 index 000000000..93cf2b15f --- /dev/null +++ b/src/live_effects/parameter/item.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> + * Abhishek Sharma + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/parameter/item.h" + +#include <glibmm/i18n.h> + +#include <gtkmm/button.h> +#include <gtkmm/label.h> + +#include "bad-uri-exception.h" +#include "ui/widget/point.h" + +#include "live_effects/effect.h" +#include "svg/svg.h" + +#include "widgets/icon.h" +#include "selection-chemistry.h" +#include "xml/repr.h" +#include "desktop.h" +#include "inkscape.h" +#include "message-stack.h" + +// clipboard support +#include "ui/clipboard.h" +// required for linking to other paths +#include "uri.h" + +#include "ui/icon-names.h" + +namespace Inkscape { + +namespace LivePathEffect { + +ItemParam::ItemParam( const Glib::ustring& label, const Glib::ustring& tip, + const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, + Effect* effect, const gchar * default_value) + : Parameter(label, tip, key, wr, effect), + changed(true), + href(NULL), + ref( (SPObject*)effect->getLPEObj() ) +{ + defvalue = g_strdup(default_value); + ref_changed_connection = ref.changedSignal().connect(sigc::mem_fun(*this, &ItemParam::ref_changed)); +} + +ItemParam::~ItemParam() +{ + remove_link(); + g_free(defvalue); +} + +void +ItemParam::param_set_default() +{ + param_readSVGValue(defvalue); +} + + +void +ItemParam::param_set_and_write_default() +{ + param_write_to_repr(defvalue); +} + +bool +ItemParam::param_readSVGValue(const gchar * strvalue) +{ + if (strvalue) { + remove_link(); + if (strvalue[0] == '#') { + if (href) + g_free(href); + href = g_strdup(strvalue); + try { + ref.attach(Inkscape::URI(href)); + //lp:1299948 + SPItem* i = ref.getObject(); + if (i) { + linked_modified_callback(i, SP_OBJECT_MODIFIED_FLAG); + } // else: document still processing new events. Repr of the linked object not created yet. + } catch (Inkscape::BadURIException &e) { + g_warning("%s", e.what()); + ref.detach(); + } + } + emit_changed(); + return true; + } + + return false; +} + +gchar * +ItemParam::param_getSVGValue() const +{ + return g_strdup(href); +} + +Gtk::Widget * +ItemParam::param_newWidget() +{ + Gtk::HBox * _widget = Gtk::manage(new Gtk::HBox()); + Gtk::Widget* pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-clone"), Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Button * pButton = Gtk::manage(new Gtk::Button()); + Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true); + pLabel->set_tooltip_text(param_tooltip); + pButton->set_relief(Gtk::RELIEF_NONE); + pIcon->show(); + pButton->add(*pIcon); + pButton->show(); + pButton->signal_clicked().connect(sigc::mem_fun(*this, &ItemParam::on_link_button_click)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); + pButton->set_tooltip_text(_("Link to item on clipboard")); + + static_cast<Gtk::HBox*>(_widget)->show_all_children(); + + return dynamic_cast<Gtk::Widget *> (_widget); +} + +void +ItemParam::emit_changed() +{ + changed = true; + signal_item_changed.emit(); +} + + +void +ItemParam::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) +{ +} + + +void +ItemParam::start_listening(SPObject * to) +{ + if ( to == NULL ) { + return; + } + linked_delete_connection = to->connectDelete(sigc::mem_fun(*this, &ItemParam::linked_delete)); + linked_modified_connection = to->connectModified(sigc::mem_fun(*this, &ItemParam::linked_modified)); + if (SP_IS_ITEM(to)) { + linked_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::mem_fun(*this, &ItemParam::linked_transformed)); + } + linked_modified(to, SP_OBJECT_MODIFIED_FLAG); // simulate linked_modified signal, so that path data is updated +} + +void +ItemParam::quit_listening(void) +{ + linked_modified_connection.disconnect(); + linked_delete_connection.disconnect(); + linked_transformed_connection.disconnect(); +} + +void +ItemParam::ref_changed(SPObject */*old_ref*/, SPObject *new_ref) +{ + quit_listening(); + if ( new_ref ) { + start_listening(new_ref); + } +} + +void +ItemParam::remove_link() +{ + if (href) { + ref.detach(); + g_free(href); + href = NULL; + } +} + +void +ItemParam::linked_delete(SPObject */*deleted*/) +{ + quit_listening(); + remove_link(); +} + +void ItemParam::linked_modified(SPObject *linked_obj, guint flags) +{ + linked_modified_callback(linked_obj, flags); +} + +void ItemParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item) +{ + linked_transformed_callback(rel_transf, moved_item); +} + +void +ItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/) +{ + emit_changed(); + SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); +} + +void +ItemParam::on_link_button_click() +{ + Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); + const gchar * iid = cm->getFirstObjectID(); + if (!iid) { + return; + } + + Glib::ustring itemid(iid); + + if (itemid.empty()) { + return; + } + + // add '#' at start to make it an uri. + itemid.insert(itemid.begin(), '#'); + if ( href && strcmp(itemid.c_str(), href) == 0 ) { + // no change, do nothing + return; + } else { + // TODO: + // check if id really exists in document, or only in clipboard document: if only in clipboard then invalid + // check if linking to object to which LPE is applied (maybe delegated to PathReference + + param_write_to_repr(itemid.c_str()); + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, + _("Link item parameter to path")); + } +} + +} /* namespace LivePathEffect */ + +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/item.h b/src/live_effects/parameter/item.h new file mode 100644 index 000000000..6c719d451 --- /dev/null +++ b/src/live_effects/parameter/item.h @@ -0,0 +1,79 @@ +#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H +#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H + +/* + * Inkscape::LivePathEffectParameters + * +* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> + + +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/item-reference.h" +#include <stddef.h> +#include <sigc++/sigc++.h> + +namespace Inkscape { + +namespace LivePathEffect { + +class ItemParam : public Parameter { +public: + ItemParam ( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Inkscape::UI::Widget::Registry* wr, + Effect* effect, + const gchar * default_value = ""); + virtual ~ItemParam(); + virtual Gtk::Widget * param_newWidget(); + + virtual bool param_readSVGValue(const gchar * strvalue); + virtual gchar * param_getSVGValue() const; + + virtual void param_set_default(); + void param_set_and_write_default(); + virtual void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector<Geom::PathVector> &hp_vec); + + sigc::signal <void> signal_item_pasted; + sigc::signal <void> signal_item_changed; + bool changed; /* this gets set whenever the path is changed (this is set to true, and then the signal_item_changed signal is emitted). + * the user must set it back to false if she wants to use it sensibly */ +protected: + + gchar * href; // contains link to other object, e.g. "#path2428", NULL if ItemParam contains pathdata itself + ItemReference ref; + sigc::connection ref_changed_connection; + sigc::connection linked_delete_connection; + sigc::connection linked_modified_connection; + sigc::connection linked_transformed_connection; + void ref_changed(SPObject *old_ref, SPObject *new_ref); + void remove_link(); + void start_listening(SPObject * to); + void quit_listening(void); + void linked_delete(SPObject *deleted); + void linked_modified(SPObject *linked_obj, guint flags); + void linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item); + virtual void linked_modified_callback(SPObject *linked_obj, guint flags); + virtual void linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/) {}; + void on_link_button_click(); + + void emit_changed(); + + gchar * defvalue; + +private: + ItemParam(const ItemParam&); + ItemParam& operator=(const ItemParam&); +}; + + +} //namespace LivePathEffect + +} //namespace Inkscape + +#endif diff --git a/src/live_effects/parameter/originalitem.cpp b/src/live_effects/parameter/originalitem.cpp new file mode 100644 index 000000000..053062128 --- /dev/null +++ b/src/live_effects/parameter/originalitem.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gtkmm/box.h> +#include "live_effects/parameter/originalitem.h" + +#include "widgets/icon.h" +#include <glibmm/i18n.h> +#include <gtkmm/button.h> +#include <gtkmm/label.h> + +#include "uri.h" +#include "sp-shape.h" +#include "sp-text.h" +#include "display/curve.h" +#include "live_effects/effect.h" + +#include "inkscape.h" +#include "desktop.h" +#include "selection.h" +#include "ui/icon-names.h" + +namespace Inkscape { + +namespace LivePathEffect { + +OriginalItemParam::OriginalItemParam( const Glib::ustring& label, const Glib::ustring& tip, + const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, + Effect* effect) + : ItemParam(label, tip, key, wr, effect, "") +{ +} + +OriginalItemParam::~OriginalItemParam() +{ + +} + +Gtk::Widget * +OriginalItemParam::param_newWidget() +{ + Gtk::HBox *_widget = Gtk::manage(new Gtk::HBox()); + + { // Label + Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true); + pLabel->set_tooltip_text(param_tooltip); + } + + { // Paste item to link button + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-paste"), Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); + pButton->set_relief(Gtk::RELIEF_NONE); + pIcon->show(); + pButton->add(*pIcon); + pButton->show(); + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_link_button_click)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); + pButton->set_tooltip_text(_("Link to item")); + } + + { // Select original button + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon("edit-select-original", Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); + pButton->set_relief(Gtk::RELIEF_NONE); + pIcon->show(); + pButton->add(*pIcon); + pButton->show(); + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_select_original_button_click)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); + pButton->set_tooltip_text(_("Select original")); + } + + static_cast<Gtk::HBox*>(_widget)->show_all_children(); + + return dynamic_cast<Gtk::Widget *> (_widget); +} + +void +OriginalItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/) +{ + if (!inverse) { + emit_changed(); + SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); + } +} + +void +OriginalItemParam::linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/) +{ +/** \todo find good way to compensate for referenced item transform, like done for normal clones. + * See sp-use.cpp: sp_use_move_compensate */ +} + + +void +OriginalItemParam::on_select_original_button_click() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPItem *original = ref.getObject(); + if (desktop == NULL || original == NULL) { + return; + } + Inkscape::Selection *selection = desktop->getSelection(); + selection->clear(); + selection->set(original); +} + +} /* namespace LivePathEffect */ + +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/originalitem.h b/src/live_effects/parameter/originalitem.h new file mode 100644 index 000000000..58d04e05a --- /dev/null +++ b/src/live_effects/parameter/originalitem.h @@ -0,0 +1,49 @@ +#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H +#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H + +/* + * Inkscape::LiveItemEffectParameters + * +* Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/parameter/item.h" + +namespace Inkscape { + +namespace LivePathEffect { + +class OriginalItemParam: public ItemParam { +public: + OriginalItemParam ( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Inkscape::UI::Widget::Registry* wr, + Effect* effect); + virtual ~OriginalItemParam(); + void setInverse(bool inversed) { inverse = inversed; } + bool linksToItem() const { return (href != NULL); } + SPItem * getObject() const { return ref.getObject(); } + + virtual Gtk::Widget * param_newWidget(); + +protected: + virtual void linked_modified_callback(SPObject *linked_obj, guint flags); + virtual void linked_transformed_callback(Geom::Affine const *rel_transf, SPItem *moved_item); + + void on_select_original_button_click(); + +private: + bool inverse; + OriginalItemParam(const OriginalItemParam&); + OriginalItemParam& operator=(const OriginalItemParam&); +}; + + +} //namespace LivePathEffect + +} //namespace Inkscape + +#endif diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp index f0c494267..dafc6d406 100644 --- a/src/live_effects/parameter/path.cpp +++ b/src/live_effects/parameter/path.cpp @@ -5,10 +5,16 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "ui/widget/point.h" +#include "live_effects/parameter/path.h" + #include <glibmm/i18n.h> -#include "live_effects/parameter/path.h" +#include <gtkmm/button.h> +#include <gtkmm/label.h> + +#include "bad-uri-exception.h" +#include "ui/widget/point.h" + #include "live_effects/effect.h" #include "svg/svg.h" #include <2geom/svg-path-parser.h> @@ -17,7 +23,6 @@ #include <2geom/d2.h> #include "widgets/icon.h" -#include <gtk/gtk.h> #include "selection-chemistry.h" #include "xml/repr.h" #include "desktop.h" @@ -44,8 +49,6 @@ #include "ui/tool/multi-path-manipulator.h" #include "ui/tool/shape-record.h" -#include <gtkmm/button.h> -#include <gtkmm/label.h> #include "ui/icon-names.h" namespace Inkscape { diff --git a/src/live_effects/parameter/text.cpp b/src/live_effects/parameter/text.cpp index 8cab68ad0..5c4cdf4c6 100644 --- a/src/live_effects/parameter/text.cpp +++ b/src/live_effects/parameter/text.cpp @@ -125,13 +125,14 @@ TextParam::param_newWidget() rsu->setProgrammatically = false; rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change text parameter")); - + param_effect->upd_params = false; return dynamic_cast<Gtk::Widget *> (rsu); } void TextParam::param_setValue(const Glib::ustring newvalue) { + param_effect->upd_params = true; value = newvalue; if (!_hide_canvas_text) { sp_canvastext_set_text (canvas_text, newvalue.c_str()); diff --git a/src/live_effects/parameter/vector.cpp b/src/live_effects/parameter/vector.cpp index aa16a2b98..55b4d4b32 100644 --- a/src/live_effects/parameter/vector.cpp +++ b/src/live_effects/parameter/vector.cpp @@ -116,7 +116,7 @@ VectorParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point void VectorParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/) { - set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() ); + set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() ); } diff --git a/src/main.cpp b/src/main.cpp index 605c1207e..47cf43456 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -166,6 +166,7 @@ enum { SP_ARG_SHELL, SP_ARG_VERSION, SP_ARG_VACUUM_DEFS, + SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING, #ifdef WITH_DBUS SP_ARG_DBUS_LISTEN, SP_ARG_DBUS_NAME, @@ -235,7 +236,6 @@ static gchar *sp_xverbs_yaml_utf8 = NULL; static gchar *sp_xverbs_yaml = NULL; #endif // WITH_YAML - /** * Reset variables to default values. */ @@ -274,6 +274,7 @@ static void resetCommandlineGlobals() { sp_query_all = FALSE; sp_query_id = NULL; sp_vacuum_defs = FALSE; + sp_no_convert_text_baseline_spacing = FALSE; #ifdef WITH_DBUS sp_dbus_listen = FALSE; sp_dbus_name = NULL; @@ -525,6 +526,11 @@ struct poptOption options[] = { N_("Start Inkscape in interactive shell mode."), NULL}, + {"no-convert-text-baseline-spacing", 0, + POPT_ARG_NONE, &sp_no_convert_text_baseline_spacing, SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING, + N_("Do not fix legacy (pre-0.92) files' text baseline spacing on opening."), + NULL}, + POPT_AUTOHELP POPT_TABLEEND }; @@ -1636,7 +1642,6 @@ static int sp_do_export_png(SPDocument *doc) return retcode; } - /** * Perform a PDF/PS/EPS export * diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index 75302806b..39bcf0c20 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -184,6 +184,7 @@ static char const menus_skeleton[] = " <submenu name=\"" N_("_Object") "\">\n" " <verb verb-id=\"DialogObjects\" />\n" " <verb verb-id=\"DialogTags\" />\n" +" <verb verb-id=\"DialogStyle\" />\n" " <separator/>\n" " <verb verb-id=\"DialogFillStroke\" />\n" " <verb verb-id=\"DialogObjectProperties\" />\n" diff --git a/src/object-set.h b/src/object-set.h index a89a299bd..7c224f640 100644 --- a/src/object-set.h +++ b/src/object-set.h @@ -447,7 +447,7 @@ public: // various void getExportHints(Glib::ustring &filename, float *xdpi, float *ydpi); bool fitCanvas(bool with_margins, bool skip_undo = false); - + void swapFillStroke(); protected: virtual void _connectSignals(SPObject* object) {}; diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 741f433f2..b66bcf368 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -498,7 +498,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) /* Whole text's style */ Glib::ustring style_str = - item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility + item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility g_repr->setAttribute("style", style_str.c_str()); Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin(); @@ -519,7 +519,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) pos_obj = pos_obj->parent; // SPStrings don't have style } Glib::ustring style_str = - pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility + pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility // get path from iter to iter_next: SPCurve *curve = te_get_layout(item)->convertToCurves(iter, iter_next); @@ -579,7 +579,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) /* Style */ Glib::ustring style_str = - item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility + item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility repr->setAttribute("style", style_str.c_str()); /* Mask */ diff --git a/src/print.cpp b/src/print.cpp index 7cd05ac93..479401995 100644 --- a/src/print.cpp +++ b/src/print.cpp @@ -79,6 +79,7 @@ unsigned int sp_print_text(SPPrintContext *ctx, char const *text, Geom::Point p, void sp_print_document(Gtk::Window& parentWindow, SPDocument *doc) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); // Build arena @@ -88,6 +89,7 @@ sp_print_document(Gtk::Window& parentWindow, SPDocument *doc) Inkscape::UI::Dialog::Print printop(doc,base); Gtk::PrintOperationResult res = printop.run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, parentWindow); (void)res; // TODO handle this + doc->getRoot()->c2p *= doc->getRoot()->rotation; } void sp_print_document_to_file(SPDocument *doc, gchar const *filename) diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp index 188b7d9c8..4901cf424 100644 --- a/src/resource-manager.cpp +++ b/src/resource-manager.cpp @@ -126,8 +126,22 @@ public: */ std::map<Glib::ustring, Glib::ustring> locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks); + + /** + * Try to parse href into a local filename using standard methods. + * + * @return true if successfull. + */ bool extractFilepath( Glib::ustring const &href, std::string &uri ); + /** + * Try to parse href into a local filename using some non-standard methods. + * This means the href is likely invalid and should be rewritten. + * + * @return true if successfull. + */ + bool reconstructFilepath( Glib::ustring const &href, std::string &uri ); + bool searchUpwards( std::string const &base, std::string const &subpath, std::string &dest ); protected: @@ -156,9 +170,13 @@ bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::strin // TODO debug g_message("--- is a file URI [%s]", href.c_str()); // throws Glib::ConvertError: - uri = Glib::filename_from_uri(href); // TODO see if we can get this to throw - // TODO debug g_message(" [%s]", uri.c_str()); - isFile = true; + try { + uri = Glib::filename_from_uri(href); + // TODO debug g_message(" [%s]", uri.c_str()); + isFile = true; + } catch(Glib::ConvertError e) { + g_warning("%s", e.what().c_str()); + } } } else { // No scheme. Assuming it is a file path (absolute or relative). @@ -170,6 +188,26 @@ bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::strin return isFile; } +bool ResourceManagerImpl::reconstructFilepath( Glib::ustring const &href, std::string &uri ) +{ + bool isFile = false; + + uri.clear(); + + std::string scheme = Glib::uri_parse_scheme(href); + if ( !scheme.empty() ) { + if ( scheme == "file" ) { + // try to build a relative filename for URIs like "file:image.png" + // they're not standard conformant but not uncommon + Glib::ustring href_new = Glib::ustring(href, 5); + uri = Glib::filename_from_utf8(href_new); + // TODO debug g_message("reconstructed path for '%s' into '%s'", href.c_str(), uri.c_str()); + isFile = true; + } + } + return isFile; +} + std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc ) { @@ -192,11 +230,14 @@ std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc } } else { std::string combined = Glib::build_filename(doc->getBase(), uri); - if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { + if ( !Glib::file_test(combined, Glib::FILE_TEST_EXISTS) ) { result.push_back(href); uniques.insert(href); } } + } else if ( reconstructFilepath( href, uri ) ) { + result.push_back(href); + uniques.insert(href); } } } @@ -228,7 +269,7 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us priorLocations.push_back(path); } } catch (Glib::ConvertError e) { - g_warning("Bad URL ignored [%s]", uri.c_str()); + g_warning("%s", e.what().c_str()); } } } @@ -238,7 +279,7 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us // TODO debug g_message("========{%s}", it->c_str()); std::string uri; - if ( extractFilepath( *it, uri ) ) { + if ( extractFilepath( *it, uri ) || reconstructFilepath( *it, uri ) ) { // We were able to get some path. Check it std::string origPath = uri; @@ -247,32 +288,31 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us // TODO debug g_message(" not absolute. Fixing up as [%s]", uri.c_str()); } - if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { - // TODO debug g_message(" DOES NOT EXIST."); - std::string remainder; - bool exists = searchUpwards( docbase, origPath, remainder ); + bool exists = Glib::file_test(uri, Glib::FILE_TEST_EXISTS); - if ( !exists ) { - // TODO debug g_message("Expanding the search..."); + // search in parent folders + if (!exists) { + exists = searchUpwards( docbase, origPath, uri ); + } - // Check if the MRU bases point us to it. - if ( !Glib::path_is_absolute(origPath) ) { - for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) { - exists = searchUpwards( *it, origPath, remainder ); - } + // Check if the MRU bases point us to it. + if ( !exists ) { + if ( !Glib::path_is_absolute(origPath) ) { + for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) { + exists = searchUpwards( *it, origPath, uri ); } } + } - if ( exists ) { - if ( Glib::path_is_absolute( remainder ) ) { - // TODO debug g_message("Need to convert to relative if possible [%s]", remainder.c_str()); - remainder = convertPathToRelative( remainder, docbase ); - } - - bool isAbsolute = Glib::path_is_absolute( remainder ); - Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( remainder ) : Glib::filename_to_utf8( remainder ); - result[*it] = replacement; + if ( exists ) { + if ( Glib::path_is_absolute( uri ) ) { + // TODO debug g_message("Need to convert to relative if possible [%s]", uri.c_str()); + uri = convertPathToRelative( uri, docbase ); } + + bool isAbsolute = Glib::path_is_absolute( uri ); + Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( uri ) : Glib::filename_to_utf8( uri ); + result[*it] = replacement; } } } diff --git a/src/round.h b/src/round.h deleted file mode 100644 index 16d280566..000000000 --- a/src/round.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SEEN_ROUND_H -#define SEEN_ROUND_H - -#include <cmath> - -namespace Inkscape { - -/** Returns x rounded to the nearest integer. It is unspecified what happens - if x is half way between two integers: we may in future use rint/round - on platforms that have them. If you depend on a particular rounding - behaviour, then please change this documentation accordingly. -**/ -inline double -round(double const x) -{ - return std::floor( x + .5 ); -} - -} - -#endif /* !SEEN_ROUND_H */ - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index f8ab33ac4..70307579f 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -97,6 +97,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "live_effects/parameter/originalpath.h" #include "layer-manager.h" #include "object-set.h" +#include "svg/svg-color.h" // For clippath editing #include "ui/tools/node-tool.h" @@ -1242,7 +1243,6 @@ void ObjectSet::pasteStyle() } } - void ObjectSet::pastePathEffect() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); @@ -3905,6 +3905,7 @@ void ObjectSet::setClipGroup() || apply_to_layer){ Geom::Affine oldtr=(*i)->transform; + oldtr *= SP_ITEM((*i)->parent)->i2doc_affine().inverse(); (*i)->doWriteTransform((*i)->getRepr(), (*i)->i2doc_affine()); Inkscape::XML::Node *dup = (*i)->getRepr()->duplicate(xml_doc); (*i)->doWriteTransform((*i)->getRepr(), oldtr); @@ -4169,6 +4170,75 @@ bool ObjectSet::fitCanvas(bool with_margins, bool skip_undo) } } +void ObjectSet::swapFillStroke() +{ + if (desktop() == NULL) { + return; + } + + SPIPaint *paint; + SPPaintServer *server; + Glib::ustring _paintserver_id; + + auto list= items(); + for (auto itemlist=list.begin();itemlist!=list.end();++itemlist) { + SPItem *item = *itemlist; + + SPCSSAttr *css = sp_repr_css_attr_new (); + + _paintserver_id.clear(); + paint = &(item->style->fill); + if (paint->set && paint->isNone()) + sp_repr_css_set_property (css, "stroke", "none"); + else if (paint->set && paint->isColor()) { + guint32 color = paint->value.color.toRGBA32(SP_SCALE24_TO_FLOAT (item->style->fill_opacity.value)); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), color); + sp_repr_css_set_property (css, "stroke", c); + } + else if (!paint->set) + sp_repr_css_unset_property (css, "stroke"); + else if (paint->set && paint->isPaintserver()) { + server = SP_STYLE_FILL_SERVER(item->style); + if (server) { + Inkscape::XML::Node *srepr = server->getRepr(); + _paintserver_id += "url(#"; + _paintserver_id += srepr->attribute("id"); + _paintserver_id += ")"; + sp_repr_css_set_property (css, "stroke", _paintserver_id.c_str()); + } + } + + _paintserver_id.clear(); + paint = &(item->style->stroke); + if (paint->set && paint->isNone()) + sp_repr_css_set_property (css, "fill", "none"); + else if (paint->set && paint->isColor()) { + guint32 color = paint->value.color.toRGBA32(SP_SCALE24_TO_FLOAT (item->style->stroke_opacity.value)); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), color); + sp_repr_css_set_property (css, "fill", c); + } + else if (!paint->set) + sp_repr_css_unset_property (css, "fill"); + else if (paint->set && paint->isPaintserver()) { + server = SP_STYLE_STROKE_SERVER(item->style); + if (server) { + Inkscape::XML::Node *srepr = server->getRepr(); + _paintserver_id += "url(#"; + _paintserver_id += srepr->attribute("id"); + _paintserver_id += ")"; + sp_repr_css_set_property (css, "fill", _paintserver_id.c_str()); + } + } + + sp_desktop_apply_css_recursive(item, css, true); + sp_repr_css_attr_unref (css); + } + + DocumentUndo::done(document(), SP_VERB_EDIT_SWAP_FILL_STROKE, + _("Swap fill and stroke of an object")); +} /** * \param with_margins margins defined in the xml under <sodipodi:namedview> diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 9ce1a3b56..3c3b8abb0 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -1,11 +1,12 @@ +#include "sp-conn-end.h" #include <cstring> #include <string> #include <limits> +#include "bad-uri-exception.h" #include "display/curve.h" #include "xml/repr.h" -#include "sp-conn-end.h" #include "sp-path.h" #include "uri.h" #include "document.h" diff --git a/src/sp-filter.cpp b/src/sp-filter.cpp index 26ea03c6c..052fa7fe2 100644 --- a/src/sp-filter.cpp +++ b/src/sp-filter.cpp @@ -17,21 +17,25 @@ #include <config.h> #endif +#include "sp-filter.h" + #include <map> #include <string.h> -using std::map; -using std::pair; #include <glibmm.h> + +#include "bad-uri-exception.h" #include "attributes.h" #include "display/nr-filter.h" #include "document.h" -#include "sp-filter.h" #include "sp-filter-reference.h" #include "sp-filter-primitive.h" #include "uri.h" #include "xml/repr.h" +using std::map; +using std::pair; + #define SP_MACROS_SILENT static void filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter); diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index 264558014..d79652bf4 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -535,7 +535,7 @@ Inkscape::XML::Node *SPFlowtext::getAsText() this->layout.getSourceOfCharacter(it, &rawptr, &span_text_start_iter); SPObject *source_obj = reinterpret_cast<SPObject *>(rawptr); - Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, this->style); + Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, this->style); if (!style_text.empty()) { span_tspan->setAttribute("style", style_text.c_str()); } diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index f02149cbb..c1934dd92 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -24,6 +24,8 @@ #define noSP_GRADIENT_VERBOSE //#define OBJECT_TRACE +#include "sp-gradient.h" + #include <cstring> #include <string> @@ -34,12 +36,12 @@ #include <sigc++/functors/ptr_fun.h> #include <sigc++/adaptors/bind.h> +#include "bad-uri-exception.h" #include "display/cairo-utils.h" #include "svg/svg.h" #include "svg/css-ostringstream.h" #include "attributes.h" #include "document-private.h" -#include "sp-gradient.h" #include "gradient-chemistry.h" #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp index 02d95c75c..f1958a53b 100644 --- a/src/sp-hatch.cpp +++ b/src/sp-hatch.cpp @@ -12,11 +12,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "sp-hatch.h" + #include <cstring> #include <string> + #include <2geom/transforms.h> #include <sigc++/functors/mem_fun.h> +#include "bad-uri-exception.h" #include "svg/svg.h" #include "display/cairo-utils.h" #include "display/drawing-context.h" @@ -25,7 +29,6 @@ #include "display/drawing-pattern.h" #include "attributes.h" #include "document-private.h" -#include "sp-hatch.h" #include "sp-hatch-path.h" SPHatch::SPHatch() diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 7b2507b5e..f2c0d2f2c 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -925,6 +925,15 @@ void SPGroup::update_patheffect(bool write) { } sp_group_perform_patheffect(this, this, write); + + for (PathEffectList::iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + + if (lpeobj && lpeobj->get_lpe()) { + lpeobj->get_lpe()->doAfterEffect(this); + } + } } } @@ -950,25 +959,35 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write) } else { c = subShape->getCurve(); } - + bool success = false; // only run LPEs when the shape has a curve defined if (c) { c->transform(i2anc_affine(subitem, topgroup)); - topgroup->performPathEffect(c); + success = topgroup->performPathEffect(c, subShape); c->transform(i2anc_affine(subitem, topgroup).inverse()); - subShape->setCurve(c, TRUE); - - if (write) { - Inkscape::XML::Node *repr = subitem->getRepr(); - gchar *str = sp_svg_write_path(c->get_pathvector()); - repr->setAttribute("d", str); + Inkscape::XML::Node *repr = subitem->getRepr(); + if (c && success) { + subShape->setCurve(c, TRUE); + if (write) { + gchar *str = sp_svg_write_path(c->get_pathvector()); + repr->setAttribute("d", str); #ifdef GROUP_VERBOSE - g_message("sp_group_perform_patheffect writes 'd' attribute"); + g_message("sp_group_perform_patheffect writes 'd' attribute"); #endif - g_free(str); + g_free(str); + } + c->unref(); + } else { + // LPE was unsuccesfull or doeffect stack return null. Read the old 'd'-attribute. + if (gchar const * value = repr->attribute("d")) { + Geom::PathVector pv = sp_svg_read_pathv(value); + SPCurve *oldcurve = new (std::nothrow) SPCurve(pv); + if (oldcurve) { + subShape->setCurve(oldcurve, TRUE); + oldcurve->unref(); + } + } } - - c->unref(); } } } diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 5d02020c6..e2f678957 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -17,6 +17,10 @@ #endif #include "sp-item.h" + +#include <glibmm/i18n.h> + +#include "bad-uri-exception.h" #include "svg/svg.h" #include "print.h" #include "display/drawing-item.h" @@ -25,10 +29,7 @@ #include "uri.h" #include "inkscape.h" #include "desktop.h" - - #include "style.h" -#include <glibmm/i18n.h> #include "sp-root.h" #include "sp-clippath.h" #include "sp-mask.h" diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index e2f61bfb5..ca0f78a00 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -15,15 +15,18 @@ #ifdef HAVE_CONFIG_H #endif -#include "ui/tool/multi-path-manipulator.h" - #include <glibmm/i18n.h> +#include "bad-uri-exception.h" +#include "ui/tool/multi-path-manipulator.h" + #include "live_effects/effect.h" #include "live_effects/lpe-path_length.h" #include "live_effects/lpeobject.h" #include "live_effects/lpeobject-reference.h" #include "live_effects/lpe-measure-line.h" +#include "live_effects/lpe-mirror_symmetry.h" +#include "live_effects/lpe-copy_rotate.h" #include "sp-path.h" #include "sp-item-group.h" @@ -126,7 +129,10 @@ void SPLPEItem::set(unsigned int key, gchar const* value) { if (!value) { LivePathEffectObject *lpeobj = (*it)->lpeobject; Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe(); - if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe)){ + if (dynamic_cast<Inkscape::LivePathEffect::LPEMirrorSymmetry *>(lpe) || + dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe) || + dynamic_cast<Inkscape::LivePathEffect::LPECopyRotate *>(lpe) ) + { lpe->doOnRemove(this); } } @@ -209,7 +215,7 @@ Inkscape::XML::Node* SPLPEItem::write(Inkscape::XML::Document *xml_doc, Inkscape /** * returns true when LPE was successful. */ -bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { +bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask) { if (!curve) { return false; @@ -241,12 +247,15 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { } if (!is_clip_or_mask || (is_clip_or_mask && lpe->apply_to_clippath_and_mask)) { // Groups have their doBeforeEffect called elsewhere + if (current) { + lpe->setCurrentShape(current); + } if (!SP_IS_GROUP(this)) { lpe->doBeforeEffect_impl(this); } try { - lpe->doEffect(curve); + lpe->doEffect(curve); } catch (std::exception & e) { g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what()); @@ -257,6 +266,8 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { return false; } if (!SP_IS_GROUP(this)) { + lpe->pathvector_before_effect = curve->get_pathvector(); + lpe->sp_curve->set_pathvector(lpe->pathvector_before_effect); lpe->doAfterEffect(this); } } @@ -604,7 +615,7 @@ bool SPLPEItem::hasPathEffect() const return true; } -bool SPLPEItem::hasPathEffectOfType(int const type) const +bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const { if (path_effect_list->empty()) { return false; @@ -616,7 +627,9 @@ bool SPLPEItem::hasPathEffectOfType(int const type) const if (lpeobj) { Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe(); if (lpe && (lpe->effectType() == type)) { - return true; + if (is_ready || lpe->isReady()) { + return true; + } } } } @@ -695,10 +708,10 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) try { if(SP_IS_GROUP(this)){ c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this))); - success = this->performPathEffect(c, true); + success = this->performPathEffect(c, SP_SHAPE(clip_mask), true); c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)).inverse()); } else { - success = this->performPathEffect(c, true); + success = this->performPathEffect(c, SP_SHAPE(clip_mask), true); } } catch (std::exception & e) { g_warning("Exception during LPE execution. \n %s", e.what()); @@ -709,12 +722,13 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) success = false; } Inkscape::XML::Node *repr = clip_mask->getRepr(); - if (success) { + // This c check allow to not apply LPE if curve is NULL after performPathEffect used in clone.obgets LPE + if (success && c) { gchar *str = sp_svg_write_path(c->get_pathvector()); repr->setAttribute("d", str); g_free(str); } else { - // LPE was unsuccesfull. Read the old 'd'-attribute. + // LPE was unsuccesfull or doeffect stack return null.. Read the old 'd'-attribute. if (gchar const * value = repr->attribute("d")) { Geom::PathVector pv = sp_svg_read_pathv(value); SPCurve *oldcurve = new (std::nothrow) SPCurve(pv); @@ -724,7 +738,9 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) } } } - c->unref(); + if (c) { + c->unref(); + } } } } diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h index 9e5cb3329..0f198c49c 100644 --- a/src/sp-lpe-item.h +++ b/src/sp-lpe-item.h @@ -23,6 +23,7 @@ class LivePathEffectObject; class SPCurve; +class SPShape; class SPDesktop; namespace Inkscape{ @@ -69,11 +70,11 @@ public: virtual void update_patheffect(bool write); - bool performPathEffect(SPCurve *curve, bool is_clip_or_mask = false); + bool performPathEffect(SPCurve *curve, SPShape *current = NULL, bool is_clip_or_mask = false); bool pathEffectsEnabled() const; bool hasPathEffect() const; - bool hasPathEffectOfType(int const type) const; + bool hasPathEffectOfType(int const type, bool is_ready = true) const; bool hasPathEffectRecursive() const; Inkscape::LivePathEffect::Effect* getPathEffectOfType(int type); Inkscape::LivePathEffect::Effect const* getPathEffectOfType(int type) const; diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index b9526433f..d430f6321 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -19,7 +19,12 @@ #include "event-log.h" #include <2geom/transforms.h> +#include "display/sp-canvas-group.h" +#include "display/canvas-bpath.h" +#include "display/canvas-temporary-item.h" +#include "display/canvas-temporary-item-list.h" #include "display/canvas-grid.h" +#include "display/curve.h" #include "util/units.h" #include "svg/svg-color.h" #include "xml/repr.h" @@ -29,12 +34,15 @@ #include "desktop-events.h" #include "sp-guide.h" +#include "sp-root.h" #include "sp-item-group.h" #include "sp-namedview.h" #include "preferences.h" #include "desktop.h" +#include "selection.h" +#include "object-set.h" +#include "inkscape.h" #include "conn-avoid-ref.h" // for defaultConnSpacing. -#include "sp-root.h" #include <gtkmm/window.h> using Inkscape::DocumentUndo; @@ -72,6 +80,7 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) { this->pagecolor = 0; this->cx = 0; this->pageshadow = 0; + this->document_rotation = 0; this->window_width = 0; this->window_height = 0; this->window_maximized = 0; @@ -92,9 +101,13 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) { this->default_layer_id = 0; this->connector_spacing = defaultConnSpacing; + this->page_border_rotated = NULL; } SPNamedView::~SPNamedView() { + if(!this->getViewList().empty()) { // >0 Desktops + this->getViewList()[0]->remove_temporary_canvasitem(this->page_border_rotated); + } } static void sp_namedview_generate_old_grid(SPNamedView * /*nv*/, SPDocument *document, Inkscape::XML::Node *repr) { @@ -212,6 +225,7 @@ void SPNamedView::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr( "inkscape:zoom" ); this->readAttr( "inkscape:cx" ); this->readAttr( "inkscape:cy" ); + this->readAttr( "inkscape:document-rotation" ); this->readAttr( "inkscape:window-width" ); this->readAttr( "inkscape:window-height" ); this->readAttr( "inkscape:window-x" ); @@ -409,6 +423,11 @@ void SPNamedView::set(unsigned int key, const gchar* value) { this->cy = value ? g_ascii_strtod(value, NULL) : HUGE_VAL; // HUGE_VAL means not set this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_INKSCAPE_DOCUMENT_ROTATION: + this->document_rotation = value ? g_ascii_strtod(value, NULL) : 0; + sp_namedview_set_document_rotation(this); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SP_ATTR_INKSCAPE_WINDOW_WIDTH: this->window_width = value? atoi(value) : -1; // -1 means not set this->requestModified(SP_OBJECT_MODIFIED_FLAG); @@ -939,6 +958,81 @@ static void sp_namedview_lock_guides(SPNamedView *nv) } } +//void sp_namedview_doc_rotate_guides(SPNamedView *nv) +//{ +// bool saved = DocumentUndo::getUndoSensitive(nv->document); +// DocumentUndo::setUndoSensitive(nv->document, false); +// SPRoot * root = nv->document->getRoot(); +// Geom::Point page_center = root->viewBox.midpoint() * root->vbt; +// Geom::Affine rot = Geom::identity(); +// rot *= Geom::Translate(page_center).inverse(); +// rot *= Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); +// rot *= Geom::Translate(page_center); +// for(std::vector<SPGuide *>::iterator it=nv->guides.begin();it!=nv->guides.end();++it ) { +// Geom::Point const on_line = (*it)->getPoint() * rot ; +// (*it)->moveto(on_line, true); +// Geom::Affine rot_normal_affine = Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); +// Geom::Point const rot_normal = (*it)->getNormal() * rot_normal_affine; +// (*it)->set_normal(rot_normal, true); +// } +// DocumentUndo::setUndoSensitive(nv->document, saved); +// nv->document->setModifiedSinceSave(); +//} + +void sp_namedview_set_document_rotation(SPNamedView *nv) +{ + if ( nv->document->getRoot()->get_rotation() == nv->document_rotation) return; + if(!nv->getViewList().empty()) { // >0 Desktops + SPDesktop *desktop = nv->getViewList()[0]; + desktop->remove_temporary_canvasitem(nv->page_border_rotated); + SPRoot * root = nv->document->getRoot(); + SPCurve *c = new SPCurve(); + c->moveto(root->viewBox.min()); + c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.min()[Geom::Y])); + c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.max()[Geom::Y])); + c->lineto(Geom::Point(root->viewBox.min()[Geom::X],root->viewBox.max()[Geom::Y])); + c->closepath(); + Geom::Point page_center = root->viewBox.midpoint(); + Geom::PathVector const box = c->get_pathvector(); + Geom::Affine rot = Geom::identity(); + rot *= Geom::Translate(page_center).inverse(); + rot *= Geom::Rotate(Geom::rad_from_deg(nv->document_rotation * -1)); + rot *= Geom::Translate(page_center); + if (nv->document_rotation) { + SPCanvasItem *canvas_border = sp_canvas_bpath_new(desktop->getTempGroup(), c, true); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvas_border), 0xFF00009A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvas_border), 0, SP_WIND_RULE_NONZERO); + sp_canvas_item_affine_absolute(canvas_border, rot * root->vbt); + nv->page_border_rotated = desktop->add_temporary_canvasitem(canvas_border, 0); + } + //sp_namedview_doc_rotate_guides(nv); + nv->document->getRoot()->set_rotation(nv->document_rotation); + c->unref(); + } + if (nv->document_rotation) { + nv->showborder = FALSE; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + nv->showborder = prefs->getBool("/template/base/showborder", 1.0); + } + + SPDesktop * desktop = SP_ACTIVE_DESKTOP; + if (desktop) { +//TODO: Remove knots of shapes on selected items +// Inkscape::Selection * sel = desktop->getSelection(); +// std::vector<SPItem*> il(sel->items().begin(), sel->items().end()); +// for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ +// SPItem *item = *l; +// sel->remove(item->getRepr()); +// sel->add(item->getRepr()); +// } + SPObject *updated = desktop->getDocument()->getRoot(); + if (updated) { + updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } +} + static void sp_namedview_show_single_guide(SPGuide* guide, bool show) { if (show) { @@ -953,6 +1047,8 @@ static void sp_namedview_lock_single_guide(SPGuide* guide, bool locked) guide->set_locked(locked, true); } + + void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr) { unsigned int v; diff --git a/src/sp-namedview.h b/src/sp-namedview.h index d8ac1a77e..9d11c13b7 100644 --- a/src/sp-namedview.h +++ b/src/sp-namedview.h @@ -21,6 +21,7 @@ #include "snap.h" #include "document.h" #include "util/units.h" +#include "display/sp-canvas.h" #include <vector> namespace Inkscape { @@ -28,6 +29,9 @@ namespace Inkscape { namespace Util { class Unit; } + namespace Display { + class TemporaryItem; + } } typedef unsigned int guint32; @@ -38,7 +42,7 @@ enum { SP_BORDER_LAYER_TOP }; -class SPNamedView : public SPObjectGroup { +class SPNamedView : public SPObjectGroup{ public: SPNamedView(); virtual ~SPNamedView(); @@ -54,6 +58,7 @@ public: double zoom; double cx; double cy; + double document_rotation; int window_width; int window_height; int window_x; @@ -66,7 +71,7 @@ public: Inkscape::Util::Unit const *display_units; // Units used for the UI (*not* the same as units of SVG coordinates) Inkscape::Util::Unit const *page_size_units; // Only used in "Custom size" part of Document Properties dialog - + Inkscape::Display::TemporaryItem *page_border_rotated; GQuark default_layer_id; double connector_spacing; @@ -121,7 +126,7 @@ SPNamedView const *sp_document_namedview(SPDocument const *document, char const void sp_namedview_window_from_document(SPDesktop *desktop); void sp_namedview_document_from_window(SPDesktop *desktop); void sp_namedview_update_layers_from_document (SPDesktop *desktop); - +void sp_namedview_set_document_rotation(SPNamedView *nv); void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr); void sp_namedview_guides_toggle_lock(SPDocument *doc, Inkscape::XML::Node *repr); void sp_namedview_show_grids(SPNamedView *namedview, bool show, bool dirty_document); diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 75f4657ef..7807703f6 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -775,6 +775,21 @@ void SPObject::appendChild(Inkscape::XML::Node *child) { repr->appendChild(child); } +SPObject* SPObject::nthChild(unsigned index) { + g_assert(this->repr); + if (hasChildren()) { + std::vector<SPObject*> l; + unsigned counter = 0; + for (auto& child: children) { + if (counter == index) { + return &child; + } + counter++; + } + } + return NULL; +} + void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev) { g_assert(this->repr); @@ -1046,7 +1061,9 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML } if (style) { - Glib::ustring s = style->write(SP_STYLE_FLAG_IFSET); + // Write if property set by style attribute in this object + Glib::ustring s = + style->write(SP_STYLE_FLAG_IFSET | SP_STYLE_FLAG_IFSRC, SP_STYLE_SRC_STYLE_PROP); // Check for valid attributes. This may be time consuming. // It is useful, though, for debugging Inkscape code. diff --git a/src/sp-object.h b/src/sp-object.h index 9abbd324b..d145e966b 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -318,6 +318,9 @@ public: SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); } SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); } + SPObject *nthChild(unsigned index); + SPObject const *nthChild(unsigned index) const; + enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; /** diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp index 9e2264d76..e5b1b5d69 100644 --- a/src/sp-offset.cpp +++ b/src/sp-offset.cpp @@ -18,13 +18,17 @@ #include <config.h> #endif +#include "sp-offset.h" + #include <cstring> #include <string> +#include <glibmm/i18n.h> + +#include "bad-uri-exception.h" #include "svg/svg.h" #include "attributes.h" #include "display/curve.h" -#include <glibmm/i18n.h> #include "livarot/Path.h" #include "livarot/Shape.h" @@ -32,7 +36,6 @@ #include "enums.h" #include "preferences.h" #include "sp-text.h" -#include "sp-offset.h" #include "sp-use-reference.h" #include "uri.h" diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp index 9d6296a0d..377c035fc 100644 --- a/src/sp-pattern.cpp +++ b/src/sp-pattern.cpp @@ -16,11 +16,14 @@ #include <config.h> #endif +#include "sp-pattern.h" + #include <cstring> #include <string> #include <glibmm.h> #include <2geom/transforms.h> +#include "bad-uri-exception.h" #include "svg/svg.h" #include "display/cairo-utils.h" #include "display/drawing-context.h" @@ -29,7 +32,6 @@ #include "display/drawing-group.h" #include "attributes.h" #include "document-private.h" -#include "sp-pattern.h" #include "sp-factory.h" diff --git a/src/sp-style-elem.cpp b/src/sp-style-elem.cpp index 80e45677c..c15f810e9 100644 --- a/src/sp-style-elem.cpp +++ b/src/sp-style-elem.cpp @@ -3,6 +3,7 @@ #include "xml/repr.h" #include "document.h" #include "sp-style-elem.h" +#include "sp-root.h" #include "attributes.h" #include "style.h" using Inkscape::XML::TEXT_NODE; @@ -64,6 +65,7 @@ content_changed_cb(Inkscape::XML::Node *, gchar const *, gchar const *, SPObject *obj = reinterpret_cast<SPObject *>(data); g_assert(data != NULL); obj->read_content(); + obj->document->getRoot()->emitModified( SP_OBJECT_MODIFIED_CASCADE ); } static void @@ -249,6 +251,19 @@ property_cb(CRDocHandler *const a_handler, g_return_if_fail(append_status == CR_OK); } +void update_style_recursively( SPObject *object ) { + if (object) { + // std::cout << "update_style_recursively: " + // << (object->getId()?object->getId():"null") << std::endl; + if (object->style) { + object->style->readFromObject( object ); + } + for (auto& child : object->children) { + update_style_recursively( &child ); + } + } +} + void SPStyleElem::read_content() { /* fixme: If there's more than one <style> element in a document, then the document stylesheet * will be set to a random one of them, even switching between them. @@ -315,9 +330,13 @@ void SPStyleElem::read_content() { // the <style> is a child of the object that uses a style from it. It just forces the parent of // <style> to reread its style as soon as the stylesheet is fully loaded. Naturally, this won't // work if the user of the stylesheet is its grandparent or precedent. - if ( parent ) { - parent->style->readFromObject( parent ); - } + // if ( parent ) { + // parent->style->readFromObject( parent ); + // } + + // If style sheet has changed, we need to cascade the entire object tree, top down + // Get root, read style, loop through children + update_style_recursively( (SPObject *)document->getRoot() ); } /** diff --git a/src/sp-tag-use-reference.cpp b/src/sp-tag-use-reference.cpp index cca24ed85..bb03c120a 100644 --- a/src/sp-tag-use-reference.cpp +++ b/src/sp-tag-use-reference.cpp @@ -6,11 +6,12 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include "sp-tag-use-reference.h" + #include <cstring> #include <string> -#include "sp-tag-use-reference.h" - +#include "bad-uri-exception.h" #include "livarot/Path.h" #include "preferences.h" #include "sp-shape.h" diff --git a/src/sp-tag-use.cpp b/src/sp-tag-use.cpp index 935f7429e..1312b923f 100644 --- a/src/sp-tag-use.cpp +++ b/src/sp-tag-use.cpp @@ -10,10 +10,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "sp-tag-use.h" + #include <cstring> #include <string> #include <glibmm/i18n.h> + +#include "bad-uri-exception.h" #include "display/drawing-group.h" #include "attributes.h" #include "document.h" @@ -23,7 +27,6 @@ #include "style.h" #include "sp-factory.h" #include "sp-symbol.h" -#include "sp-tag-use.h" #include "sp-tag-use-reference.h" SPTagUse::SPTagUse() diff --git a/src/sp-tag-use.h b/src/sp-tag-use.h index 3f238d654..651c8f045 100644 --- a/src/sp-tag-use.h +++ b/src/sp-tag-use.h @@ -22,6 +22,7 @@ #define SP_TAG_USE(obj) (dynamic_cast<SPTagUse*> (obj)) #define SP_IS_TAG_USE(obj) (dynamic_cast<SPTagUse*> (obj) != NULL) +class SPItem; class SPTagUse; class SPTagUseReference; diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp index e25ddb5a4..014876478 100644 --- a/src/sp-tref.cpp +++ b/src/sp-tref.cpp @@ -16,13 +16,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "sp-tref.h" + #include <glibmm/i18n.h> +#include "bad-uri-exception.h" #include "attributes.h" #include "document.h" #include "sp-factory.h" #include "sp-text.h" -#include "sp-tref.h" #include "style.h" #include "text-editing.h" diff --git a/src/sp-use-reference.cpp b/src/sp-use-reference.cpp index 3dd63df40..ea8bc5e86 100644 --- a/src/sp-use-reference.cpp +++ b/src/sp-use-reference.cpp @@ -7,11 +7,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include "sp-use-reference.h" + #include <cstring> #include <string> +#include "bad-uri-exception.h" #include "enums.h" -#include "sp-use-reference.h" #include "display/curve.h" #include "livarot/Path.h" diff --git a/src/sp-use.cpp b/src/sp-use.cpp index 6ba03dad9..648852961 100644 --- a/src/sp-use.cpp +++ b/src/sp-use.cpp @@ -19,6 +19,8 @@ #include <2geom/transforms.h> #include <glibmm/i18n.h> #include <glibmm/markup.h> + +#include "bad-uri-exception.h" #include "display/drawing-group.h" #include "attributes.h" #include "document.h" diff --git a/src/style-internal.cpp b/src/style-internal.cpp index 5a2a2f913..54d1a0867 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -27,6 +27,10 @@ #endif #include "style-internal.h" + +#include <glibmm/regex.h> + +#include "bad-uri-exception.h" #include "style.h" #include "svg/svg.h" @@ -40,8 +44,6 @@ #include "svg/css-ostringstream.h" #include "util/units.h" -#include <glibmm/regex.h> - // TODO REMOVE OR MAKE MEMBER FUNCTIONS void sp_style_fill_paint_server_ref_changed( SPObject *old_ref, SPObject *ref, SPStyle *style); void sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style); @@ -53,6 +55,20 @@ using Inkscape::CSSOStringStream; // SPIBase -------------------------------------------------------------- +// Standard criteria for writing a property +// dfp == different from parent +inline bool should_write( guint const flags, bool set, bool dfp, bool src) { + + bool should_write = false; + if ( ((flags & SP_STYLE_FLAG_ALWAYS)) || + ((flags & SP_STYLE_FLAG_IFSET) && set && src) || + ((flags & SP_STYLE_FLAG_IFDIFF) && set && src && dfp)) { + should_write = true; + } + return should_write; +} + + // SPIFloat ------------------------------------------------------------- @@ -75,14 +91,12 @@ SPIFloat::read( gchar const *str ) { } const Glib::ustring -SPIFloat::write( guint const flags, SPIBase const *const base) const { +SPIFloat::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIFloat const *const my_base = dynamic_cast<const SPIFloat*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -151,14 +165,12 @@ SPIScale24::read( gchar const *str ) { } const Glib::ustring -SPIScale24::write( guint const flags, SPIBase const *const base) const { +SPIScale24::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIScale24 const *const my_base = dynamic_cast<const SPIScale24*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -303,14 +315,12 @@ SPILength::read( gchar const *str ) { } const Glib::ustring -SPILength::write( guint const flags, SPIBase const *const base) const { +SPILength::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPILength const *const my_base = dynamic_cast<const SPILength*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -460,18 +470,16 @@ SPILengthOrNormal::read( gchar const *str ) { }; const Glib::ustring -SPILengthOrNormal::write( guint const flags, SPIBase const *const base) const { +SPILengthOrNormal::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPILength const *const my_base = dynamic_cast<const SPILength*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->normal) { return (name + ":normal;"); } else { - return SPILength::write(flags, base); + return SPILength::write(flags, style_src_req, base); } } return Glib::ustring(""); @@ -547,14 +555,12 @@ SPIEnum::read( gchar const *str ) { } const Glib::ustring -SPIEnum::write( guint const flags, SPIBase const *const base) const { +SPIEnum::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -686,14 +692,12 @@ SPIEnumBits::read( gchar const *str ) { } const Glib::ustring -SPIEnumBits::write( guint const flags, SPIBase const *const base) const { +SPIEnumBits::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -757,14 +761,12 @@ SPILigatures::read( gchar const *str ) { } const Glib::ustring -SPILigatures::write( guint const flags, SPIBase const *const base) const { +SPILigatures::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -858,14 +860,12 @@ SPINumeric::read( gchar const *str ) { } const Glib::ustring -SPINumeric::write( guint const flags, SPIBase const *const base) const { +SPINumeric::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -929,14 +929,12 @@ SPIString::read( gchar const *str ) { // This routine is actually rarely used. Writing is done usually // in sp_repr_css_write_string... const Glib::ustring -SPIString::write( guint const flags, SPIBase const *const base) const { +SPIString::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIString const *const my_base = dynamic_cast<const SPIString*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this) ); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -1040,14 +1038,12 @@ void SPIColor::read( gchar const *str ) { } const Glib::ustring -SPIColor::write( guint const flags, SPIBase const *const base) const { +SPIColor::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIColor const *const my_base = dynamic_cast<const SPIColor*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->currentcolor) { @@ -1269,14 +1265,12 @@ SPIPaint::read( gchar const *str, SPStyle &style_in, SPDocument *document_in ) { } const Glib::ustring -SPIPaint::write( guint const flags, SPIBase const *const base) const { +SPIPaint::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIPaint const *const my_base = dynamic_cast<const SPIPaint*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -1541,14 +1535,12 @@ SPIPaintOrder::read( gchar const *str ) { } const Glib::ustring -SPIPaintOrder::write( guint const flags, SPIBase const *const base) const { +SPIPaintOrder::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIPaintOrder const *const my_base = dynamic_cast<const SPIPaintOrder*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -1689,14 +1681,14 @@ SPIFilter::read( gchar const *str ) { } } -const Glib::ustring SPIFilter::write( guint const flags, SPIBase const *const /*base*/) const +const Glib::ustring SPIFilter::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const /*base*/) const { // TODO: fix base //SPILength const *const my_base = dynamic_cast<const SPILength*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set)) - { + // bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool dfp = true; + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else if(this->href && this->href->getURI()) { @@ -1828,14 +1820,12 @@ SPIDashArray::read( gchar const *str ) { } const Glib::ustring -SPIDashArray::write( guint const flags, SPIBase const *const base) const { +SPIDashArray::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIDashArray const *const my_base = dynamic_cast<const SPIDashArray*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else if (this->values.empty() ) { @@ -1945,14 +1935,12 @@ SPIFontSize::read( gchar const *str ) { } const Glib::ustring -SPIFontSize::write( guint const flags, SPIBase const *const base) const { +SPIFontSize::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIFontSize const *const my_base = dynamic_cast<const SPIFontSize*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -2239,7 +2227,7 @@ SPIFont::read( gchar const *str ) { } } -const Glib::ustring SPIFont::write( guint const /*flags*/, SPIBase const *const /*base*/) const +const Glib::ustring SPIFont::write( guint const /*flags*/, SPStyleSrc const & /*style_src_req*/, SPIBase const *const /*base*/) const { // At the moment, do nothing. We could add a preference to write out // 'font' shorthand rather than longhand properties. @@ -2316,14 +2304,12 @@ SPIBaselineShift::read( gchar const *str ) { } const Glib::ustring -SPIBaselineShift::write( guint const flags, SPIBase const *const base) const { +SPIBaselineShift::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIBaselineShift const *const my_base = dynamic_cast<const SPIBaselineShift*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || !this->isZero() ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -2507,13 +2493,11 @@ SPITextDecorationLine::read( gchar const *str ) { } const Glib::ustring -SPITextDecorationLine::write( guint const flags, SPIBase const *const base) const { +SPITextDecorationLine::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPITextDecorationLine const *const my_base = dynamic_cast<const SPITextDecorationLine*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { Inkscape::CSSOStringStream os; os << name << ":"; if( inherit ) { @@ -2639,13 +2623,11 @@ SPITextDecorationStyle::read( gchar const *str ) { } const Glib::ustring -SPITextDecorationStyle::write( guint const flags, SPIBase const *const base) const { +SPITextDecorationStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPITextDecorationStyle const *const my_base = dynamic_cast<const SPITextDecorationStyle*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { Inkscape::CSSOStringStream os; os << name << ":"; if( inherit ) { @@ -2795,7 +2777,7 @@ SPITextDecoration::read( gchar const *str ) { // Returns CSS2 'text-decoration' (using settings in SPTextDecorationLine) // This is required until all SVG renderers support CSS3 'text-decoration' const Glib::ustring -SPITextDecoration::write( guint const flags, SPIBase const *const base) const { +SPITextDecoration::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPITextDecoration const *const my_base = dynamic_cast<const SPITextDecoration*>(base); if ( (flags & SP_STYLE_FLAG_ALWAYS) || ((flags & SP_STYLE_FLAG_IFSET) && style->text_decoration_line.set) || diff --git a/src/style-internal.h b/src/style-internal.h index 6cc8c6c4e..69b03f503 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -35,12 +35,13 @@ struct SPStyleEnum; static const unsigned SP_STYLE_FLAG_ALWAYS (1 << 2); static const unsigned SP_STYLE_FLAG_IFSET (1 << 0); static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1); +static const unsigned SP_STYLE_FLAG_IFSRC (1 << 3); // If source matches enum SPStyleSrc { SP_STYLE_SRC_UNSET, - SP_STYLE_SRC_STYLE_PROP, - SP_STYLE_SRC_STYLE_SHEET, - SP_STYLE_SRC_ATTRIBUTE + SP_STYLE_SRC_ATTRIBUTE, // fill="red" + SP_STYLE_SRC_STYLE_PROP, // style="fill:red" + SP_STYLE_SRC_STYLE_SHEET, // .red { fill:red; } }; /* General comments: @@ -77,6 +78,7 @@ enum SPStyleSrc { * IFSET: Write a property if 'set' flag is true, otherwise return empty string. * IFDIFF: Write a property if computed values are different, otherwise return empty string, * This is only used for text!! + * IFSRC Write a property if the source matches the requested source (style sheet, etc.). * * read(): Set a property value from a string. * clear(): Set a property to its default value and set the 'set' flag to false. @@ -127,17 +129,19 @@ public: {} virtual void read( gchar const *str ) = 0; - virtual void readIfUnset( gchar const *str ) { + virtual void readIfUnset( gchar const *str, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ) { if ( !set ) { read( str ); + style_src = source; } } virtual void readAttribute( Inkscape::XML::Node *repr ) { - readIfUnset( repr->attribute( name.c_str() ) ); + readIfUnset( repr->attribute( name.c_str() ), SP_STYLE_SRC_ATTRIBUTE ); } virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const = 0; virtual void clear() { set = false, inherit = false; @@ -176,7 +180,7 @@ public: unsigned inherits : 1; // Property inherits by default from parent. unsigned set : 1; // Property has been explicitly set (vs. inherited). unsigned inherit : 1; // Property value set to 'inherit'. - SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME + SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet). // To do: make private after g_asserts removed public: @@ -202,6 +206,7 @@ public: virtual ~SPIFloat() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -283,6 +288,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -355,6 +361,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -411,6 +418,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPILength::clear(); @@ -474,6 +482,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -528,6 +537,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; }; @@ -554,6 +564,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; }; @@ -577,6 +588,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; }; @@ -606,6 +618,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear(); // TODO check about value and value_default virtual void cascade( const SPIBase* const parent ); @@ -653,6 +666,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -734,6 +748,7 @@ public: virtual void read( gchar const *str ); virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear(); virtual void reset( bool init ); // Used internally when reading or cascading @@ -833,6 +848,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -885,6 +901,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -924,6 +941,7 @@ public: virtual ~SPIFilter(); virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear(); virtual void cascade( const SPIBase* const parent ); @@ -968,6 +986,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1024,6 +1043,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1068,6 +1088,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1124,6 +1145,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1171,6 +1193,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1227,6 +1250,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); diff --git a/src/style.cpp b/src/style.cpp index c513b735d..0b0358bb2 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -23,10 +23,15 @@ #include <config.h> #endif +#include "style.h" + #include <cstring> #include <string> #include <algorithm> +#include <glibmm/regex.h> + +#include "bad-uri-exception.h" #include "libcroco/cr-sel-eng.h" #include "xml/croco-node-iface.h" @@ -38,14 +43,11 @@ #include "uri-references.h" #include "uri.h" #include "sp-paint-server.h" -#include "style.h" #include "svg/css-ostringstream.h" #include "xml/simple-document.h" #include "util/units.h" #include "preferences.h" -#include <glibmm/regex.h> - using Inkscape::CSSOStringStream; using std::vector; @@ -678,22 +680,22 @@ SPStyle::readFromPrefs(Glib::ustring const &path) { // Matches sp_style_merge_property(SPStyle *style, gint id, gchar const *val) void -SPStyle::readIfUnset( gint id, gchar const *val ) { +SPStyle::readIfUnset( gint id, gchar const *val, SPStyleSrc const &source ) { - // std::cout << "SPStyle::readIfUnset: Entrance: " << (val?val:"null") << std::endl; + // std::cout << "SPStyle::readIfUnset: Entrance: " << id << ": " << (val?val:"null") << std::endl; // To Do: If it is not too slow, use std::map instead of std::vector inorder to remove switch() // (looking up SP_PROP_xxxx already uses a hash). g_return_if_fail(val != NULL); switch (id) { case SP_PROP_INKSCAPE_FONT_SPEC: - font_specification.readIfUnset( val ); + font_specification.readIfUnset( val, source ); break; case SP_PROP_FONT_FAMILY: - font_family.readIfUnset( val ); + font_family.readIfUnset( val, source ); break; case SP_PROP_FONT_SIZE: - font_size.readIfUnset( val ); + font_size.readIfUnset( val, source ); break; case SP_PROP_FONT_SIZE_ADJUST: if (strcmp(val, "none") != 0) { @@ -701,105 +703,105 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { } break; case SP_PROP_FONT_STYLE: - font_style.readIfUnset( val ); + font_style.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT: - font_variant.readIfUnset( val ); + font_variant.readIfUnset( val, source ); break; case SP_PROP_FONT_WEIGHT: - font_weight.readIfUnset( val ); + font_weight.readIfUnset( val, source ); break; case SP_PROP_FONT_STRETCH: - font_stretch.readIfUnset( val ); + font_stretch.readIfUnset( val, source ); break; case SP_PROP_FONT: - font.readIfUnset( val ); + font.readIfUnset( val, source ); break; /* Font Variants CSS 3 */ case SP_PROP_FONT_VARIANT_LIGATURES: - font_variant_ligatures.readIfUnset( val ); + font_variant_ligatures.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_POSITION: - font_variant_position.readIfUnset( val ); + font_variant_position.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_CAPS: - font_variant_caps.readIfUnset( val ); + font_variant_caps.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_NUMERIC: - font_variant_numeric.readIfUnset( val ); + font_variant_numeric.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_ALTERNATES: - font_variant_alternates.readIfUnset( val ); + font_variant_alternates.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_EAST_ASIAN: - font_variant_east_asian.readIfUnset( val ); + font_variant_east_asian.readIfUnset( val, source ); break; case SP_PROP_FONT_FEATURE_SETTINGS: - font_feature_settings.readIfUnset( val ); + font_feature_settings.readIfUnset( val, source ); break; /* Text */ case SP_PROP_TEXT_INDENT: - text_indent.readIfUnset( val ); + text_indent.readIfUnset( val, source ); break; case SP_PROP_TEXT_ALIGN: - text_align.readIfUnset( val ); + text_align.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION: - text_decoration.readIfUnset( val ); + text_decoration.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION_LINE: - text_decoration_line.readIfUnset( val ); + text_decoration_line.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION_STYLE: - text_decoration_style.readIfUnset( val ); + text_decoration_style.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION_COLOR: - text_decoration_color.readIfUnset( val ); + text_decoration_color.readIfUnset( val, source ); break; case SP_PROP_LINE_HEIGHT: - line_height.readIfUnset( val ); + line_height.readIfUnset( val, source ); break; case SP_PROP_LETTER_SPACING: - letter_spacing.readIfUnset( val ); + letter_spacing.readIfUnset( val, source ); break; case SP_PROP_WORD_SPACING: - word_spacing.readIfUnset( val ); + word_spacing.readIfUnset( val, source ); break; case SP_PROP_TEXT_TRANSFORM: - text_transform.readIfUnset( val ); + text_transform.readIfUnset( val, source ); break; /* Text (css3) */ case SP_PROP_DIRECTION: - direction.readIfUnset( val ); + direction.readIfUnset( val, source ); break; case SP_PROP_WRITING_MODE: - writing_mode.readIfUnset( val ); + writing_mode.readIfUnset( val, source ); break; case SP_PROP_TEXT_ORIENTATION: - text_orientation.readIfUnset( val ); + text_orientation.readIfUnset( val, source ); break; case SP_PROP_TEXT_ANCHOR: - text_anchor.readIfUnset( val ); + text_anchor.readIfUnset( val, source ); break; case SP_PROP_WHITE_SPACE: - white_space.readIfUnset( val ); + white_space.readIfUnset( val, source ); break; case SP_PROP_SHAPE_INSIDE: - shape_inside.readIfUnset( val ); + shape_inside.readIfUnset( val, source ); break; case SP_PROP_SHAPE_PADDING: - shape_padding.readIfUnset( val ); + shape_padding.readIfUnset( val, source ); break; case SP_PROP_DOMINANT_BASELINE: - dominant_baseline.readIfUnset( val ); + dominant_baseline.readIfUnset( val, source ); break; case SP_PROP_BASELINE_SHIFT: - baseline_shift.readIfUnset( val ); + baseline_shift.readIfUnset( val, source ); break; case SP_PROP_TEXT_RENDERING: - text_rendering.readIfUnset( val ); + text_rendering.readIfUnset( val, source ); break; case SP_PROP_ALIGNMENT_BASELINE: g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val); @@ -818,25 +820,25 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { g_warning("Unimplemented style property SP_PROP_CLIP: value: %s", val); break; case SP_PROP_COLOR: - color.readIfUnset( val ); + color.readIfUnset( val, source ); break; case SP_PROP_CURSOR: g_warning("Unimplemented style property SP_PROP_CURSOR: value: %s", val); break; case SP_PROP_DISPLAY: - display.readIfUnset( val ); + display.readIfUnset( val, source ); break; case SP_PROP_OVERFLOW: - overflow.readIfUnset( val ); + overflow.readIfUnset( val, source ); break; case SP_PROP_VISIBILITY: - visibility.readIfUnset( val ); + visibility.readIfUnset( val, source ); break; case SP_PROP_ISOLATION: - isolation.readIfUnset( val ); + isolation.readIfUnset( val, source ); break; case SP_PROP_MIX_BLEND_MODE: - mix_blend_mode.readIfUnset( val ); + mix_blend_mode.readIfUnset( val, source ); break; /* SVG */ @@ -854,7 +856,7 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { this->object->getRepr()->setAttribute("clip-path", val); break; case SP_PROP_CLIP_RULE: - clip_rule.readIfUnset( val ); + clip_rule.readIfUnset( val, source ); break; case SP_PROP_MASK: /** \todo @@ -866,14 +868,14 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { this->object->getRepr()->setAttribute("mask", val); break; case SP_PROP_OPACITY: - opacity.readIfUnset( val ); + opacity.readIfUnset( val, source ); break; case SP_PROP_ENABLE_BACKGROUND: - enable_background.readIfUnset( val ); + enable_background.readIfUnset( val, source ); break; /* Filter */ case SP_PROP_FILTER: - if( !filter.inherit ) filter.readIfUnset( val ); + if( !filter.inherit ) filter.readIfUnset( val, source ); break; case SP_PROP_FLOOD_COLOR: g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val); @@ -898,86 +900,86 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { /* Paint */ case SP_PROP_COLOR_INTERPOLATION: // We read it but issue warning - color_interpolation.readIfUnset( val ); + color_interpolation.readIfUnset( val, source ); if( color_interpolation.value != SP_CSS_COLOR_INTERPOLATION_SRGB ) { g_warning("Inkscape currently only supports color-interpolation = sRGB"); } break; case SP_PROP_COLOR_INTERPOLATION_FILTERS: - color_interpolation_filters.readIfUnset( val ); + color_interpolation_filters.readIfUnset( val, source ); break; case SP_PROP_COLOR_PROFILE: g_warning("Unimplemented style property SP_PROP_COLOR_PROFILE: value: %s", val); break; case SP_PROP_COLOR_RENDERING: - color_rendering.readIfUnset( val ); + color_rendering.readIfUnset( val, source ); break; case SP_PROP_SOLID_COLOR: - solid_color.readIfUnset( val ); + solid_color.readIfUnset( val, source ); break; case SP_PROP_SOLID_OPACITY: - solid_opacity.readIfUnset( val ); + solid_opacity.readIfUnset( val, source ); break; case SP_PROP_VECTOR_EFFECT: vector_effect.readIfUnset( val ); break; case SP_PROP_FILL: - fill.readIfUnset( val ); + fill.readIfUnset( val, source ); break; case SP_PROP_FILL_OPACITY: - fill_opacity.readIfUnset( val ); + fill_opacity.readIfUnset( val, source ); break; case SP_PROP_FILL_RULE: - fill_rule.readIfUnset( val ); + fill_rule.readIfUnset( val, source ); break; case SP_PROP_IMAGE_RENDERING: - image_rendering.readIfUnset( val ); + image_rendering.readIfUnset( val, source ); break; case SP_PROP_MARKER: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker.readIfUnset( val ); + marker.readIfUnset( val, source ); break; case SP_PROP_MARKER_START: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker_start.readIfUnset( val ); + marker_start.readIfUnset( val, source ); break; case SP_PROP_MARKER_MID: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker_mid.readIfUnset( val ); + marker_mid.readIfUnset( val, source ); break; case SP_PROP_MARKER_END: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker_end.readIfUnset( val ); + marker_end.readIfUnset( val, source ); break; case SP_PROP_SHAPE_RENDERING: - shape_rendering.readIfUnset( val ); + shape_rendering.readIfUnset( val, source ); break; case SP_PROP_STROKE: - stroke.readIfUnset( val ); + stroke.readIfUnset( val, source ); break; case SP_PROP_STROKE_WIDTH: - stroke_width.readIfUnset( val ); + stroke_width.readIfUnset( val, source ); break; case SP_PROP_STROKE_DASHARRAY: - stroke_dasharray.readIfUnset( val ); + stroke_dasharray.readIfUnset( val, source ); break; case SP_PROP_STROKE_DASHOFFSET: - stroke_dashoffset.readIfUnset( val ); + stroke_dashoffset.readIfUnset( val, source ); break; case SP_PROP_STROKE_LINECAP: - stroke_linecap.readIfUnset( val ); + stroke_linecap.readIfUnset( val, source ); break; case SP_PROP_STROKE_LINEJOIN: - stroke_linejoin.readIfUnset( val ); + stroke_linejoin.readIfUnset( val, source ); break; case SP_PROP_STROKE_MITERLIMIT: - stroke_miterlimit.readIfUnset( val ); + stroke_miterlimit.readIfUnset( val, source ); break; case SP_PROP_STROKE_OPACITY: - stroke_opacity.readIfUnset( val ); + stroke_opacity.readIfUnset( val, source ); break; case SP_PROP_PAINT_ORDER: - paint_order.readIfUnset( val ); + paint_order.readIfUnset( val, source ); break; default: g_warning("SPIStyle::readIfUnset(): Invalid style property id: %d value: %s", id, val); @@ -999,16 +1001,16 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { * \post ret != NULL. */ Glib::ustring -SPStyle::write( guint const flags, SPStyle const *const base ) const { +SPStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPStyle const *const base ) const { - // std::cout << "SPStyle::write" << std::endl; + // std::cout << "SPStyle::write: flags: " << flags << std::endl; Glib::ustring style_string; for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { if( base != NULL ) { - style_string += _properties[i]->write( flags, base->_properties[i] ); + style_string += _properties[i]->write( flags, style_src_req, base->_properties[i] ); } else { - style_string += _properties[i]->write( flags, NULL ); + style_string += _properties[i]->write( flags, style_src_req, NULL ); } } // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { @@ -1112,13 +1114,13 @@ SPStyle::_mergeString( gchar const *const p ) { CRDeclaration *const decl_list = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8); if (decl_list) { - _mergeDeclList( decl_list ); + _mergeDeclList( decl_list, SP_STYLE_SRC_STYLE_PROP ); cr_declaration_destroy(decl_list); } } void -SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) { +SPStyle::_mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source ) { // std::cout << "SPStyle::_mergeDeclList" << std::endl; @@ -1126,13 +1128,13 @@ SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) { // (Properties are only set if not previously set. See: // Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.) if (decl_list->next) { - _mergeDeclList( decl_list->next ); + _mergeDeclList( decl_list->next, source ); } - _mergeDecl( decl_list ); + _mergeDecl( decl_list, source ); } void -SPStyle::_mergeDecl( CRDeclaration const *const decl ) { +SPStyle::_mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source ) { // std::cout << "SPStyle::_mergeDecl" << std::endl; @@ -1145,7 +1147,7 @@ SPStyle::_mergeDecl( CRDeclaration const *const decl ) { */ guchar *const str_value_unsigned = cr_term_to_string(decl->value); gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned); - readIfUnset( prop_idx, str_value ); + readIfUnset( prop_idx, str_value, source ); g_free(str_value); } } @@ -1160,7 +1162,7 @@ SPStyle::_mergeProps( CRPropList *const props ) { _mergeProps( cr_prop_list_get_next( props ) ); CRDeclaration *decl = NULL; cr_prop_list_get_decl(props, &decl); - _mergeDecl( decl ); + _mergeDecl( decl, SP_STYLE_SRC_STYLE_SHEET ); } } diff --git a/src/style.h b/src/style.h index c8192f782..00c8c032a 100644 --- a/src/style.h +++ b/src/style.h @@ -50,8 +50,9 @@ public: void read(SPObject *object, Inkscape::XML::Node *repr); void readFromObject(SPObject *object); void readFromPrefs(Glib::ustring const &path); - void readIfUnset( int id, char const *val ); + void readIfUnset( int id, char const *val, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ); Glib::ustring write( unsigned int const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPStyle const *const base = NULL ) const; void cascade( SPStyle const *const parent ); void merge( SPStyle const *const parent ); @@ -64,8 +65,8 @@ public: private: void _mergeString( char const *const p ); - void _mergeDeclList( CRDeclaration const *const decl_list ); - void _mergeDecl( CRDeclaration const *const decl ); + void _mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source ); + void _mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source ); void _mergeProps( CRPropList *const props ); void _mergeObjectStylesheet( SPObject const *const object ); diff --git a/src/svg/svg-length.h b/src/svg/svg-length.h index bd3435ca6..4663aced4 100644 --- a/src/svg/svg-length.h +++ b/src/svg/svg-length.h @@ -12,6 +12,8 @@ #ifndef SEEN_SP_SVG_LENGTH_H #define SEEN_SP_SVG_LENGTH_H +#include <string> + /** * SVG length type */ diff --git a/src/trace/trace.h b/src/trace/trace.h index 662b2537e..f562e89aa 100644 --- a/src/trace/trace.h +++ b/src/trace/trace.h @@ -13,10 +13,6 @@ # include "config.h" #endif -#ifdef HAVE_STDLIB_H -# include <stdlib.h> -#endif - #ifdef HAVE_STRING_H # include <string.h> #endif diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index f2a256698..11a0c351f 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -57,6 +57,7 @@ set(ui_SRC dialog/calligraphic-profile-rename.cpp dialog/clonetiler.cpp dialog/color-item.cpp + dialog/cssdialog.cpp dialog/debug.cpp dialog/desktop-tracker.cpp dialog/dialog-manager.cpp @@ -99,6 +100,7 @@ set(ui_SRC dialog/print-colors-preview-dialog.cpp dialog/print.cpp dialog/spellcheck.cpp + dialog/styledialog.cpp dialog/svg-fonts-dialog.cpp dialog/swatches.cpp dialog/symbols.cpp @@ -195,6 +197,7 @@ set(ui_SRC dialog/calligraphic-profile-rename.h dialog/clonetiler.h dialog/color-item.h + dialog/cssdialog.h dialog/debug.h dialog/desktop-tracker.h dialog/dialog-manager.h @@ -239,6 +242,7 @@ set(ui_SRC dialog/print-colors-preview-dialog.h dialog/print.h dialog/spellcheck.h + dialog/styledialog.h dialog/svg-fonts-dialog.h dialog/swatches.h dialog/symbols.h diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index 73b632a2c..e719f8d63 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -644,7 +644,6 @@ Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop) return svgd; } - /** * Iterate over a list of items and copy them to the clipboard. */ @@ -690,25 +689,6 @@ void ClipboardManagerImpl::_copySelection(ObjectSet *selection) else obj_copy = _copyNode(obj, _doc, _clipnode); - // For lpe items, copy lpe stack if applicable - SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item); - if (lpeitem) { - Inkscape::SVGOStringStream os; - if (lpeitem->hasPathEffect()) { - for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) - { - LivePathEffectObject *lpeobj = (*it)->lpeobject; - if (lpeobj) { - Inkscape::XML::Node * lpeobjcopy = _copyNode(lpeobj->getRepr(), _doc, _defs); - gchar *new_conflict_id = sp_object_get_unique_id(lpeobj, lpeobj->getAttribute("id")); - lpeobjcopy->setAttribute("id", new_conflict_id); - g_free(new_conflict_id); - os << "#" << lpeobjcopy->attribute("id") << ";"; - } - } - } - obj_copy->setAttribute("inkscape:path-effect", os.str().c_str()); - } // copy complete inherited style SPCSSAttr *css = sp_repr_css_attr_inherited(obj, "style"); sp_repr_css_set(obj_copy, css, "style"); @@ -740,6 +720,13 @@ void ClipboardManagerImpl::_copySelection(ObjectSet *selection) sp_repr_css_set(_clipnode, style, "style"); sp_repr_css_attr_unref(style); } + // copy path effect from the first path + if (object) { + gchar const *effect =object->getRepr()->attribute("inkscape:path-effect"); + if (effect) { + _clipnode->setAttribute("inkscape:path-effect", effect); + } + } } Geom::OptRect size = selection->visualBounds(); @@ -842,6 +829,19 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) } } + // For lpe items, copy lpe stack if applicable + SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item); + if (lpeitem) { + if (lpeitem->hasPathEffect()) { + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it){ + LivePathEffectObject *lpeobj = (*it)->lpeobject; + if (lpeobj) { + _copyNode(lpeobj->getRepr(), _doc, _defs); + } + } + } + } + // recurse for(auto& o: item->children) { SPItem *childItem = dynamic_cast<SPItem *>(&o); diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp index 8f0545e96..40b948a23 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -152,16 +152,16 @@ Gtk::Widget *build_splash_widget() { the `screens' directory. Thus the translation of "about.svg" should be the filename of its translated version, e.g. about.zh.svg for Chinese. - N.B. about.svg changes once per release. (We should probably rename - the original to about-0.40.svg etc. as soon as we have a translation. - If we do so, then add an item to release-checklist saying that the - string here should be changed.) */ + Please don't translate the filename unless the translated picture exists. */ // FIXME? INKSCAPE_SCREENSDIR and "about.svg" are in UTF-8, not the // native filename encoding... and the filename passed to sp_document_new // should be in UTF-*8.. char *about=g_build_filename(INKSCAPE_SCREENSDIR, _("about.svg"), NULL); + if (!g_file_test (about, G_FILE_TEST_EXISTS)) { + about=g_build_filename(INKSCAPE_SCREENSDIR, "about.svg", NULL); + } SPDocument *doc=SPDocument::createNewDoc (about, TRUE); g_free(about); g_return_val_if_fail(doc != NULL, NULL); diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp new file mode 100644 index 000000000..fa266b012 --- /dev/null +++ b/src/ui/dialog/cssdialog.cpp @@ -0,0 +1,125 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "cssdialog.h" +#include "ui/widget/addtoicon.h" +#include "widgets/icon.h" +#include "verbs.h" +#include "sp-object.h" +#include "selection.h" +#include "xml/attribute-record.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief CssDialog::_styleButton + * @param btn + * @param iconName + * @param tooltip + * This function sets the style of '+'button at the bottom of dialog. + */ +void CssDialog::_styleButton(Gtk::Button& btn, char const* iconName, + char const* tooltip) +{ + GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName); + gtk_widget_show(child); + btn.add(*manage(Glib::wrap(child))); + btn.set_relief(Gtk::RELIEF_NONE); + btn.set_tooltip_text(tooltip); +} + +/** + * Constructor + * A treeview whose each row corresponds to a CSS property of selector selected. + * New CSS property can be added by clicking '+' at bottom of the CSS pane. '-' + * in front of the CSS property row can be clicked to delete the CSS property. + * Besides clicking on an already selected property row makes the property editable + * and clicking 'Enter' updates the property with changes reflected in the + * drawing. + */ +CssDialog::CssDialog(): + UI::Widget::Panel("", "/dialogs/css", SP_VERB_DIALOG_CSS), + _desktop(0) +{ + set_size_request(20, 15); + _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET); + _treeView.set_headers_visible(false); + _scrolledWindow.add(_treeView); + _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + _store = Gtk::ListStore::create(_cssColumns); + _treeView.set_model(_store); + + Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI:: + Widget::AddToIcon()); + addRenderer->property_active() = false; + + int addCol = _treeView.append_column("Unset Property", *addRenderer) - 1; + Gtk::TreeViewColumn *col = _treeView.get_column(addCol); + if (col) { + col->add_attribute(addRenderer->property_active(), _cssColumns._colUnsetProp); + } + _textRenderer = Gtk::manage(new Gtk::CellRendererText()); + _textRenderer->property_editable() = true; + + int nameColNum = _treeView.append_column("Property", *_textRenderer) - 1; + _propCol = _treeView.get_column(nameColNum); + + Gtk::Button* create = manage(new Gtk::Button()); + _styleButton(*create, "list-add", "Add a new property"); + + _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK); + _buttonBox.pack_start(*create, Gtk::PACK_SHRINK); + + _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET); + + _targetDesktop = getDesktop(); + setDesktop(_targetDesktop); + + create->signal_clicked().connect(sigc::mem_fun(*this, &CssDialog::_addProperty)); +} + +/** + * @brief CssDialog::~CssDialog + * Class destructor + */ +CssDialog::~CssDialog() +{ + setDesktop(NULL); +} + +/** + * @brief CssDialog::setDesktop + * @param desktop + * This function sets the 'desktop' for the CSS pane. + */ +void CssDialog::setDesktop(SPDesktop* desktop) +{ + _desktop = desktop; +} + +/** + * @brief CssDialog::_addProperty + * This function is a slot to signal_clicked for '+' button at the bottom of CSS + * panel. A new row is added, double clicking which text for new property can be + * added. _newProperty is set to true in which case the value is appended. + */ +void CssDialog::_addProperty() +{ + _propRow = *(_store->append()); + _newProperty = true; +} + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h new file mode 100644 index 000000000..3bbab5031 --- /dev/null +++ b/src/ui/dialog/cssdialog.h @@ -0,0 +1,75 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef CSSDIALOG_H +#define CSSDIALOG_H + +#include <gtkmm/treeview.h> +#include <gtkmm/liststore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include <ui/widget/panel.h> + +#include "desktop.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief The CssDialog class + * This dialog allows to add, delete and modify CSS properties for selectors + * created in Style Dialog. Double clicking any selector in Style dialog, a list + * of CSS properties will show up in this dialog (if any exist), else new properties + * can be added and each new property forms a new row in this pane. + */ +class CssDialog : public UI::Widget::Panel +{ +public: + CssDialog(); + ~CssDialog(); + + static CssDialog &getInstance() { return *new CssDialog(); } + void setDesktop(SPDesktop* desktop); + + class CssColumns : public Gtk::TreeModel::ColumnRecord + { + public: + CssColumns() + { add(_colUnsetProp); add(_propertyLabel); } + Gtk::TreeModelColumn<bool> _colUnsetProp; + Gtk::TreeModelColumn<Glib::ustring> _propertyLabel; + }; + + SPDesktop* _desktop; + SPDesktop* _targetDesktop; + CssColumns _cssColumns; + Gtk::VBox _mainBox; + Gtk::HBox _buttonBox; + Gtk::TreeView _treeView; + Glib::RefPtr<Gtk::ListStore> _store; + Gtk::ScrolledWindow _scrolledWindow; + Gtk::TreeModel::Row _propRow; + Gtk::CellRendererText *_textRenderer; + Gtk::TreeViewColumn *_propCol; + Glib::ustring _editedProp; + bool _newProperty; + + void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip); + void _addProperty(); +}; + + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif // CSSDIALOG_H diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index c53112656..b407f8200 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -57,6 +57,8 @@ #include "ui/dialog/svg-fonts-dialog.h" #include "ui/dialog/objects.h" #include "ui/dialog/tags.h" +#include "ui/dialog/styledialog.h" +#include "ui/dialog/cssdialog.h" namespace Inkscape { namespace UI { @@ -125,6 +127,8 @@ DialogManager::DialogManager() { registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>); registerFactory("TileDialog", &create<ArrangeDialog, FloatingBehavior>); registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>); + registerFactory("StyleDialog", &create<StyleDialog, FloatingBehavior>); + registerFactory("CssDialog", &create<CssDialog, FloatingBehavior>); #if HAVE_POTRACE registerFactory("Trace", &create<TraceDialog, FloatingBehavior>); @@ -164,6 +168,8 @@ DialogManager::DialogManager() { registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>); registerFactory("TileDialog", &create<ArrangeDialog, DockBehavior>); registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>); + registerFactory("StyleDialog", &create<StyleDialog, DockBehavior>); + registerFactory("CssDialog", &create<CssDialog, DockBehavior>); #if HAVE_POTRACE registerFactory("Trace", &create<TraceDialog, DockBehavior>); diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index 1bb952de4..b7207fd50 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -978,7 +978,9 @@ void Export::onExport () SPNamedView *nv = desktop->getNamedView(); SPDocument *doc = desktop->getDocument(); - + Geom::Affine rot = doc->getRoot()->c2p; + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); bool exportSuccessful = false; bool hide = hide_export.get_active (); @@ -1003,6 +1005,7 @@ void Export::onExport () if (num < 1) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No items selected.")); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1094,6 +1097,7 @@ void Export::onExport () if (filename.empty()) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename.")); sp_ui_error_dialog(_("You have to enter a filename")); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1110,6 +1114,7 @@ void Export::onExport () if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The chosen area to be exported is invalid.")); sp_ui_error_dialog(_("The chosen area to be exported is invalid")); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1132,6 +1137,7 @@ void Export::onExport () g_free(safeDir); g_free(error); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1281,6 +1287,7 @@ void Export::onExport () } } } + doc->getRoot()->c2p *= doc->getRoot()->rotation; } // end of sp_export_export_clicked() /// Called when Browse button is clicked diff --git a/src/ui/dialog/polar-arrange-tab.cpp b/src/ui/dialog/polar-arrange-tab.cpp index c51881a96..9485b6ba3 100644 --- a/src/ui/dialog/polar-arrange-tab.cpp +++ b/src/ui/dialog/polar-arrange-tab.cpp @@ -43,14 +43,14 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) anchorPointLabel.set_alignment(Gtk::ALIGN_START); pack_start(anchorPointLabel, false, false); - anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Object's bounding box:")); + anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Objects' bounding boxes:")); anchorRadioGroup = anchorBoundingBoxRadio.get_group(); anchorBoundingBoxRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed)); pack_start(anchorBoundingBoxRadio, false, false); pack_start(anchorSelector, false, false); - anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Object's rotational center")); + anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Objects' rotational centers")); anchorObjectPivotRadio.set_group(anchorRadioGroup); anchorObjectPivotRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed)); pack_start(anchorObjectPivotRadio, false, false); diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp new file mode 100644 index 000000000..5246290b4 --- /dev/null +++ b/src/ui/dialog/styledialog.cpp @@ -0,0 +1,1116 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "styledialog.h" +#include "ui/widget/addtoicon.h" +#include "widgets/icon.h" +#include "verbs.h" +#include "sp-object.h" +#include "selection.h" +#include "xml/attribute-record.h" +#include <glibmm/regex.h> + +using Inkscape::Util::List; +using Inkscape::XML::AttributeRecord; + +/** + * This macro is used to remove spaces around selectors or any strings when + * parsing is done to update XML style element or row labels in this dialog. + */ +#define REMOVE_SPACES(x) x.erase(0, x.find_first_not_of(' ')); \ + x.erase(x.find_last_not_of(' ') + 1); + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief StyleDialog::_styleButton + * @param btn + * @param iconName + * @param tooltip + * This function sets the style of '+' and '-' buttons at the bottom of dialog. + */ +void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName, + char const* tooltip) +{ + GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName); + gtk_widget_show(child); + btn.add(*manage(Glib::wrap(child))); + btn.set_relief(Gtk::RELIEF_NONE); + btn.set_tooltip_text (tooltip); +} + +/** + * Constructor + * A treeview and a set of two buttons are added to the dialog. _addSelector + * adds selectors to treeview. _delSelector deletes the selector from the dialog. + * Any addition/deletion of the selectors updates XML style element accordingly. + */ +StyleDialog::StyleDialog() : + UI::Widget::Panel("", "/dialogs/style", SP_VERB_DIALOG_STYLE), + _desktop(0) +{ + set_size_request(200, 200); + + _paned.pack1(_mainBox, Gtk::SHRINK); + _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET); + _treeView.set_headers_visible(false); + _scrolledWindow.add(_treeView); + _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + _store = Gtk::TreeStore::create(_mColumns); + _treeView.set_model(_store); + + Inkscape::UI::Widget::AddToIcon * addRenderer = manage( + new Inkscape::UI::Widget::AddToIcon() ); + addRenderer->property_active() = true; + + int addCol = _treeView.append_column("type", *addRenderer) - 1; + + Gtk::TreeViewColumn *col = _treeView.get_column(addCol); + if ( col ) { + col->add_attribute( addRenderer->property_active(), _mColumns._colAddRemove ); + } + + _treeView.append_column("Selector Name", _mColumns._selectorLabel); + _treeView.set_expander_column(*(_treeView.get_column(1))); + + create = manage( new Gtk::Button() ); + _styleButton(*create, "list-add", "Add a new CSS Selector"); + create->signal_clicked().connect(sigc::mem_fun(*this, + &StyleDialog::_addSelector)); + + del = manage( new Gtk::Button() ); + _styleButton(*del, "list-remove", "Remove a CSS Selector"); + del->signal_clicked().connect(sigc::mem_fun(*this, + &StyleDialog::_delSelector)); + del->set_sensitive(false); + + _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK); + + _buttonBox.pack_start(*create, Gtk::PACK_SHRINK); + _buttonBox.pack_start(*del, Gtk::PACK_SHRINK); + + _getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET); + + _targetDesktop = getDesktop(); + setDesktop(_targetDesktop); + + /** + * @brief document + * If an existing document is opened, its XML representation is obtained + * and is then used to populate the treeview with the already existing + * selectors in the style element. + */ + _styleExists = false; + _document = _targetDesktop->doc(); + _selectorValue = _populateTree(_getSelectorVec()); + + _treeView.signal_button_press_event().connect(sigc::mem_fun(*this, + &StyleDialog:: + _handleButtonEvent), + false); + + _treeView.signal_button_press_event().connect_notify(sigc::mem_fun + (*this, &StyleDialog:: + _buttonEventsSelectObjs), + false); + + _cssPane = new CssDialog; + + _treeView.get_selection()->signal_changed().connect(sigc::mem_fun(*this, + &StyleDialog:: + _selChanged)); +} + +/** + * @brief StyleDialog::~StyleDialog + * Class destructor + */ +StyleDialog::~StyleDialog() +{ + setDesktop(NULL); +} + +/** + * @brief StyleDialog::setDesktop + * @param desktop + * This function sets the 'desktop' for the Style Dialog. + */ +void StyleDialog::setDesktop( SPDesktop* desktop ) +{ + Panel::setDesktop(desktop); + _desktop = Panel::getDesktop(); + _desktop->getSelection()->connectChanged(sigc::mem_fun(*this, &StyleDialog:: + _selectRow)); +} + +/** + * @brief StyleDialog::_addSelector + * This function is the slot to the signal emitted when '+' at the bottom of + * the dialog is clicked. + */ +void StyleDialog::_addSelector() +{ + Gtk::TreeModel::Row row = *(_store->append()); + + /** + * On clicking '+' button, an entrybox with default text opens up. If an + * object is already selected, a selector with value in the entry + * is added to a new style element. + */ + Gtk::Dialog *textDialogPtr = new Gtk::Dialog(); + Gtk::Entry *textEditPtr = manage ( new Gtk::Entry() ); + textDialogPtr->add_button("Add", Gtk::RESPONSE_OK); + textDialogPtr->get_vbox()->pack_start(*textEditPtr, Gtk::PACK_SHRINK); + + /** + * By default, the entrybox contains 'Class1' as text. However, if object(s) + * is(are) selected and user clicks '+' at the bottom of dialog, the + * entrybox will have the id(s) of the selected objects as text. + */ + if (_desktop->getSelection()->isEmpty()) { + textEditPtr->set_text("Class1"); + } + else { + Inkscape::Selection* selection = _desktop->getSelection(); + std::vector<SPObject*> selected = std::vector<SPObject *>(selection + ->objects().begin(), + selection-> + objects().end()); + textEditPtr->set_text(_setClassAttribute(selected)); + } + + textDialogPtr->set_size_request(200, 100); + textDialogPtr->show_all(); + int result = textDialogPtr->run(); + + /** + * @brief selectorName + * This string stores selector name. The text from entrybox is saved as name + * for selector. If the entrybox is empty, the text (thus selectorName) is + * set to ".Class1" + */ + if (!textEditPtr->get_text().empty()) { + _selectorName = textEditPtr->get_text(); + } + else { + _selectorName = ".Class1"; + } + + del->set_sensitive(true); + + /** + * The selector name objects is set to the text that the user sets in the + * entrybox. If the attribute does not exist, it is + * created. In case the attribute already has a value, the new value entered + * is appended to the values. If a style attribute does not exist, it is + * created with an empty value. Also if a class selector is added, then + * class attribute for the selected object is set too. + */ + std::vector<SPObject *> objVec; + + bool objExists = false; + if (!_desktop->getSelection()->isEmpty()) { + for (auto& obj: _desktop->getSelection()->objects()) { + objExists = true; + if (!obj->getRepr()->attribute("style")) { + obj->getRepr()->setAttribute("style", NULL); + } + + if (obj->getAttribute("style") == NULL) { + _selectorValue = _selectorName + "{" + "}" + "\n"; + } + else { + _selectorValue = _selectorName + "{" + + obj->getAttribute("style") + "}" + "\n"; + } + + if (_selectorName[0] == '.') { + if (!obj->getRepr()->attribute("class")) { + obj->getRepr()->setAttribute("class", textEditPtr->get_text() + .erase(0,1)); + } + else { + obj->getRepr()->setAttribute("class", std::string(obj-> + getRepr()-> + attribute("class")) + + " " + textEditPtr->get_text() + .erase(0,1)); + } + } + } + } + else { + _selectorValue = _selectorName + "{" + "}" + "\n"; + objExists = false; + } + + switch (result) { + case Gtk::RESPONSE_OK: + textDialogPtr->hide(); + row[_mColumns._selectorLabel] = _selectorName; + row[_mColumns._colAddRemove] = true; + if (objExists) { + Inkscape::Selection* selection = _desktop->getSelection(); + row[_mColumns._colObj] = std::vector<SPObject *>(selection->objects() + .begin(), selection + ->objects().end()); + objVec = row[_mColumns._colObj]; + } + break; + default: + break; + } + + /** + * A new style element is added to the document with value obtained + * from selectorValue above. If style element already exists, then + * the new selector's content is appended to its previous content. + */ + inkSelector._selector = _selectorName; + inkSelector._matchingObjs = objVec; + inkSelector._xmlContent = _selectorValue; + _selectorVec.push_back(inkSelector); + + if (_styleElementNode()) { + _styleChild = _styleElementNode(); + _updateStyleContent(); + } + else if (_styleExists && !_newDrawing) { + _updateStyleContent(); + } + else if (!_styleExists) { + Inkscape::XML::Node *root = _document->getReprDoc()->root(); + Inkscape::XML::Node *newChild = _document->getReprDoc() + ->createElement("svg:style"); + Inkscape::XML::Node *smallChildren = _document->getReprDoc() + ->createTextNode(_selectorValue.c_str()); + + newChild->appendChild(smallChildren); + Inkscape::GC::release(smallChildren); + + root->addChild(newChild, NULL); + Inkscape::GC::release(newChild); + _styleChild = newChild; + } + _selAdd(row); +} + +/** + * @brief StyleDialog::_updateStyleContent + * This function updates the content in style element as new selectors (or + * objects) are added/removed. + */ +void StyleDialog::_updateStyleContent() +{ + std::string styleContent = ""; + for (unsigned i = 0; i < _selectorVec.size(); ++i) { + styleContent = styleContent + _selectorVec[i]._xmlContent; + } + _styleChild->firstChild()->setContent(styleContent.c_str()); +} + +/** + * @brief StyleDialog::_delSelector + * This function deletes selector when '-' at the bottom is clicked. The index + * of selected row is obtained and the corresponding selector and its values are + * deleted from the selector vector. If a row has no parent, it is directly + * erased from the vector along with its child rows. The style element is updated + * accordingly. + */ +void StyleDialog::_delSelector() +{ + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + std::string sel, key, value; + std::vector<InkSelector>::iterator it; + for (it = _selectorVec.begin(); it != _selectorVec.end();) { + sel = (*it)._xmlContent; + REMOVE_SPACES(sel); + if (!sel.empty()) { + key = strtok((char*)sel.c_str(), "{"); + REMOVE_SPACES(key); + char *temp = strtok(NULL, "}"); + if (strtok(temp, "}") != NULL) { + value = strtok(temp, "}"); + } + } + + Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel]; + std::string matchSelector = selectedRowLabel; + REMOVE_SPACES(matchSelector); + if (key == matchSelector) { + it = _selectorVec.erase(it); + _store->erase(row); + } + else { + ++it; + } + + /** + * The _stylechild is obtained which contains the style element and + * the content in style element is updated. If _selectorVec is + * empty, the style element is removed from the XML repr else + * the content is updated simply using _updateStyleContent(). + */ + _styleChild = _styleElementNode(); + if (_store->children().empty()) { + _document->getReprRoot()->removeChild(_styleChild); + _styleExists = false; + } + else { + _updateStyleContent(); + } + } + } +} + +/** + * @brief StyleDialog::_styleElementNode + * @return + * This function returns the node containing style element. The document's + * children are iterated and the repr of the style element that occurs is + * obtained. + */ +Inkscape::XML::Node* StyleDialog::_styleElementNode() +{ + for (unsigned i = 0; i < _document->getReprRoot()->childCount(); ++i) { + if (std::string(_document->getReprRoot()->nthChild(i)->name()) + == "svg:style") { + _styleExists = true; + _newDrawing = true; + return _document->getReprRoot()->nthChild(i); + } + } + return NULL; +} + +/** + * @brief StyleDialog::_setClassAttribute + * @param sel + * @return This function returns the ids of objects selected which are passed + * to entrybox. + */ +std::string StyleDialog::_setClassAttribute(std::vector<SPObject*> sel) +{ + std::string str = ""; + for ( unsigned i = 0; i < sel.size(); ++i ) { + SPObject *obj = sel.at(i); + str = str + "#" + std::string(obj->getId()) + " "; + } + return str; +} + +/** + * @brief StyleDialog::_getSelectorVec + * @return selVec + * This function returns a vector whose key is the style selector name and value + * is the style properties. All style selectors are extracted from svg:style + * element. _newDrawing is flag is set to false check if an existing drawing is + * opened. + */ +std::vector<StyleDialog::InkSelector> StyleDialog::_getSelectorVec() +{ + for (unsigned i = 0; i < _document->getReprRoot()->childCount(); ++i) { + if (std::string(_document->getReprRoot()->nthChild(i)->name()) == "svg:style") { + _styleExists = true; + _newDrawing = false; + _styleChild = _document->getReprRoot()->nthChild(i); + + // Get content from first style element. + std::string content = _styleChild->firstChild()->content(); + + // Remove end-of-lines (check it works on Windoze). + content.erase(std::remove(content.begin(), content.end(), '\n'), content.end()); + + // First split into selector/value chunks. + // An attempt to use Glib::Regex failed. A C++11 version worked but + // reportedly has problems on Windows. Using split_simple() is simpler + // and probably faster. + // + // Glib::RefPtr<Glib::Regex> regex1 = + // Glib::Regex::create("([^\\{]+)\\{([^\\{]+)\\}"); + // + // Glib::MatchInfo minfo; + // regex1->match(content, minfo); + + // Split on curly brackets. Even tokens are selectors, odd are values. + std::vector<std::string> tokens = Glib::Regex::split_simple("[}{]", content); + + for (unsigned i = 0; i < tokens.size()-1; i += 2) { + std::string selectors = tokens[i]; + REMOVE_SPACES(selectors); // Remove leading/trailing spaces + + /** Make a list of all objects that selector matches. This is + * currently limited to simple id, class, and element selectors. + * Expanding this would take integrating a true CSS parser. + */ + std::vector<SPObject *>objVec; + + // Split selector string into individual selectors (which are comma separated). + std::vector<std::string> tokens2 = Glib::Regex::split_simple + ("\\s*,\\s*", selectors ); + + for(unsigned i = 0; i < tokens2.size(); ++i) { + std::string token2 = tokens2[i]; + + // Find objects that match class selector + if (token2[0] == '.') { + token2.erase(0,1); + std::vector<SPObject *> objects = _document + ->getObjectsByClass(token2); + objVec.insert(objVec.end(), objects.begin(), objects.end()); + } + + // Find objects that match id selector + else if (token2[0] == '#') { + token2.erase(0,1); + SPObject * object = _document->getObjectById(token2); + if (object) { + objVec.push_back(object); + } + } + + // Find objects that match element selector + else { + std::vector<SPObject *> objects = _document-> + getObjectsByElement(token2); + objVec.insert(objVec.end(), objects.begin(), objects.end()); + } + } + + std::string values; + // Check to make sure we do have a value to match selector. + if ((i+1) < tokens.size()) { + values = tokens[i+1]; + } else { + std::cerr << "StyleDialog::_getSelectorVec: Missing values " + "for last selector!" << std::endl; + } + + _selectorValue = selectors + "{" + values + "}\n"; + inkSelector._selector = selectors; + inkSelector._matchingObjs = objVec; + inkSelector._xmlContent = _selectorValue; + _selectorVec.push_back(inkSelector); + } + } + } + return _selectorVec; +} + +/** + * @brief StyleDialog::_populateTree + * @param _selVec + * This function populates the treeview with selectors available in the + * stylesheet. + */ +std::string StyleDialog::_populateTree(std::vector<InkSelector> _selVec) +{ + _selectorVec = _selVec; + std::string selectorValue; + + for(unsigned it = 0; it < _selectorVec.size(); ++it) { + Gtk::TreeModel::Row row = *(_store->append()); + row[_mColumns._selectorLabel] = _selectorVec[it]._selector; + row[_mColumns._colAddRemove] = true; + row[_mColumns._colObj] = _selectorVec[it]._matchingObjs; + std::string selValue = _selectorVec[it]._xmlContent; + selectorValue.append(selValue.c_str()); + } + + if (_selectorVec.size() > 0) { + del->set_sensitive(true); + } + + return selectorValue; +} + +/** + * @brief StyleDialog::_handleButtonEvent + * @param event + * @return + * This function handles the event when '+' button in front of a selector name + * is clicked. The selected objects (if any) is added to the selector as a child + * in the treeview. + */ +bool StyleDialog::_handleButtonEvent(GdkEventButton *event) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + Gtk::TreeViewColumn *col = 0; + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) { + if (col == _treeView.get_column(0)) { + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = + _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection-> + get_selected(); + Gtk::TreeModel::Row row = *iter; + + /** + * This adds child rows to selected rows. If the parent row is + * a class selector, then the class attribute of object added + * to child row is appended with class in the parent row. The + * else below deletes objects from selectors when 'delete' button + * in front of child row is clicked. The class attribute is updated + * by removing the parent row's class selector name. + */ + if (!row.parent()) { + _selAdd(row); + } + + else { + std::string sel, key, value; + std::vector<InkSelector>::iterator it; + Gtk::TreeModel::Row parentRow = *(row).parent(); + Glib::ustring parentKey = parentRow[_mColumns._selectorLabel]; + + for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) { + sel = (*it)._xmlContent; + REMOVE_SPACES(sel); + if (!sel.empty()) { + key = strtok((char*)sel.c_str(), "{"); + REMOVE_SPACES(key); + char *temp = strtok(NULL, "}"); + if (strtok(temp, "}") != NULL) { + value = strtok(temp, "}"); + } + } + + /** + * @brief matchSelector + * For id selectors, whenever any child row is deleted, + * the row label is updated and so is the entry for the + * selector in style element. + */ + std::string matchSelector = parentKey; + REMOVE_SPACES(matchSelector); + if (key == matchSelector) { + if (key[0] == '#') { + std::string s = parentKey; + Glib::ustring toDelRow = row[_mColumns._selectorLabel]; + std::string toDelKey = toDelRow; + std::size_t idFound = s.find(toDelKey); + if (idFound != std::string::npos) { + if (idFound == 0) { + s.erase(idFound, toDelKey.length()+1); + parentKey = s; + parentRow[_mColumns._selectorLabel] = parentKey; + (*it)._xmlContent.erase(idFound, toDelKey.length()); + } + else { + s.erase(idFound-2, toDelKey.length()+2); + parentKey = s; + parentRow[_mColumns._selectorLabel] = parentKey; + (*it)._xmlContent.erase(idFound-2, toDelKey. + length()+2); + } + } + } + } + + if (parentKey[0] == '.') { + std::vector<SPObject *> objVec = row[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { + SPObject *obj = objVec[i]; + std::string classAttr = std::string(obj->getRepr() + ->attribute("class")); + std::size_t found = classAttr.find(parentKey.erase(0,1)); + if (found != std::string::npos) { + classAttr.erase(found, parentKey.length()+1); + obj->getRepr()->setAttribute("class", classAttr); + } + } + } + + if (parentKey.empty()) { + (*it)._xmlContent = ""; + } + } + + if (_styleChild) { + _updateStyleContent(); + } + else { + _styleChild = _styleElementNode(); + _updateStyleContent(); + } + + _store->erase(row); + + /** + * On continuous deletion of objects (child rows) from the + * selector (parent row), if the parent row has no child, then + * the row is erased from the _store. Further if there is no + * row left in _store, which implies there is no content in + * XML style element, then the 'svg:style' element is also + * removed. + */ + if (parentKey.empty()) { + _store->erase(parentRow); + } + + if (parentKey.empty() && _store->children().empty()) { + _document->getReprRoot()->removeChild(_styleChild); + _styleExists = false; + } + } + } + } + } + return false; +} + +/** + * @brief StyleDialog::_selAdd + * @param row + * This routine is called when an object is added to a selector by clicking on + * '+' in front of the row with selector's label. + */ +void StyleDialog::_selAdd(Gtk::TreeModel::Row row) +{ + Glib::ustring selectorName; + Gtk::TreeModel::Row childrow; + Inkscape::Selection* selection = _desktop->getSelection(); + std::vector<SPObject *> sel = std::vector<SPObject *> + (selection->objects().begin(), selection->objects().end()); + for (auto& obj: selection->objects()) { + if (*row) { + if (_selectorVec.size() != 0) { + childrow = *(_store->append(row->children())); + childrow[_mColumns._selectorLabel] = "#" + + std::string(obj->getId()); + childrow[_mColumns._colAddRemove] = false; + childrow[_mColumns._colObj] = sel; + Glib::ustring key = row[_mColumns._selectorLabel]; + if (key[0] == '.') { + if (!obj->getRepr()->attribute("class")) { + obj->setAttribute("class", key.erase(0,1)); + } + else { + if (obj->getRepr()->attribute("class") != key + .erase(0,1)) { + obj->setAttribute("class", std::string + (obj->getRepr()-> + attribute("class")) + + " " + key + .erase(0,1)); + } + } + } + } + selectorName = row[_mColumns._selectorLabel]; + } + + /** + * If the object's parent row is a class selector, then + * there are no changes in style element except the class + * attribute is updated. For the id selector cases, XML + * content's style element is updated. + */ + REMOVE_SPACES(selectorName); + std::vector<InkSelector>::iterator it; + for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) { + std::string sel, key, value; + sel = (*it)._xmlContent; + REMOVE_SPACES(sel); + if (!sel.empty()) { + key = strtok((char*)sel.c_str(), "{"); + REMOVE_SPACES(key); + char *temp = strtok(NULL, "}"); + if (strtok(temp, "}") != NULL) { + value = strtok(temp, "}"); + } + } + + Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel]; + std::string matchSelector = selectedRowLabel; + REMOVE_SPACES(matchSelector); + if (key == matchSelector) { + REMOVE_SPACES((*it)._selector); + if (selectorName[0] == '#') { + if ("#" + std::string(obj->getId()) != selectorName) { + inkSelector._selector = (*it)._selector; + inkSelector._selector.append(", #" + std::string(obj->getId())); + inkSelector._xmlContent = inkSelector._selector + "{" + value + "}\n"; + row[_mColumns._selectorLabel] = selectorName + ", " + + childrow[_mColumns._selectorLabel]; + } + } + else if (selectorName[0] == '.') { + inkSelector._xmlContent = (*it)._selector + "{" + value + "}\n"; + } + + it = _selectorVec.erase(it); + it = _selectorVec.insert(it, inkSelector); + } + } + } + if (_styleElementNode()) { + _styleChild = _styleElementNode(); + _updateStyleContent(); + } + else if (_styleExists && !_newDrawing) { + _updateStyleContent(); + } +} + +/** + * @brief StyleDialog::_selectObjects + * @param event + * This function detects single or double click on a selector in any row. Single + * click on a selector selects the matching objects. A double click on any + * selector selects the matching objects as well as will open CSS dialog. It + * calls _selectObjects to add objects to selection. + */ +void StyleDialog::_buttonEventsSelectObjs(GdkEventButton* event ) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + _selectObjects(x, y); + } + else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + _selectObjects(x, y); + + //Open CSS dialog here. + if (!_cssPane->get_visible()) { + _paned.pack2(*_cssPane, Gtk::SHRINK); + _cssPane->show_all(); + } + + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + std::string sel, key, value; + std::vector<InkSelector>::iterator it; + for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) { + sel = (*it)._xmlContent; + REMOVE_SPACES(sel); + if (!sel.empty()) { + key = strtok((char*)sel.c_str(), "{"); + REMOVE_SPACES(key); + char *temp = strtok(NULL, "}"); + if (strtok(temp, "}") != NULL) { + value = strtok(temp, "}"); + } + } + + Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel]; + std::string matchSelector = selectedRowLabel; + REMOVE_SPACES(matchSelector); + + if (key == matchSelector) { + _cssPane->_store->clear(); + std::stringstream ss(value); + std::string token; + std::size_t found = value.find(";"); + if (found!=std::string::npos) { + while(std::getline(ss, token, ';')) { + REMOVE_SPACES(token); + if (!token.empty()) { + _cssPane->_propRow = *(_cssPane->_store->append()); + _cssPane->_propRow[_cssPane->_cssColumns._colUnsetProp] = false; + _cssPane->_propRow[_cssPane->_cssColumns._propertyLabel] = token; + _cssPane->_propCol->add_attribute(_cssPane->_textRenderer + ->property_text(), + _cssPane->_cssColumns + ._propertyLabel); + } + } + } + } + } + } + else { + _cssPane->_store->clear(); + _cssPane->hide(); + } + + _cssPane->_textRenderer->signal_edited().connect(sigc::mem_fun(*this, + &StyleDialog:: + _handleEdited)); + _cssPane->_treeView.signal_button_press_event().connect(sigc::mem_fun + (*this, &StyleDialog:: + _delProperty), + false); + } +} + +/** + * @brief StyleDialog::_selChanged + * When no row in _treeView of Style Dialog is selected, the _cssPane is hidden. + */ +void StyleDialog::_selChanged() { + if (_treeView.get_selection()->count_selected_rows() == 0) { + _cssPane->hide(); + } +} + +/** + * @brief StyleDialog::_handleEdited + * @param path + * @param new_text + * This function edits CSS properties of the selector chosen. new_text is used + * to update the property in XML repr. The value from selected selector is + * obtained and modified as per value of new_text. If a new property is added, + * value is appended with new_text. Later _updateStyleContent() is called to + * update XML repr and hence changes are reflected in the drawing too. + */ +void StyleDialog::_handleEdited(const Glib::ustring& path, const Glib::ustring& new_text) +{ + Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path); + if (iterCss) { + Gtk::TreeModel::Row row = *iterCss; + row[_cssPane->_cssColumns._propertyLabel] = new_text; + _cssPane->_editedProp = new_text; + } + + // Selected selector row is obtained here to get corresponding key and value. + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + std::string sel, key, value; + std::vector<InkSelector>::iterator it; + for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) { + sel = (*it)._xmlContent; + REMOVE_SPACES(sel); + if (!sel.empty()) { + key = strtok((char*)sel.c_str(), "{"); + REMOVE_SPACES(key); + char *temp = strtok(NULL, "}"); + if (strtok(temp, "}") != NULL) { + value = strtok(temp, "}"); + } + } + + Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel]; + std::string matchSelector = selectedRowLabel; + REMOVE_SPACES(matchSelector); + + if (key == matchSelector) { + /** If a new property is added, existing value is appended with new + * property, else replacements in value are done in the 'else' block. + */ + if (_cssPane->_newProperty) { + if (!new_text.empty()) { + value.append((new_text + ";").c_str()); + _cssPane->_propCol->add_attribute(_cssPane->_textRenderer + ->property_text(), + _cssPane->_cssColumns + ._propertyLabel); + _cssPane->_newProperty = false; + } + } + else { + std::stringstream ss(value); + std::string token, editedToken; + std::size_t found = value.find(";"); + if (found!=std::string::npos) { + while(std::getline(ss, token, ';')) { + REMOVE_SPACES(token); + if (!token.empty()) { + if (token.substr(0, token.find(":")) == _cssPane + ->_editedProp.substr(0, _cssPane->_editedProp + .find(":"))) { + editedToken = _cssPane->_editedProp; + size_t startPos = value.find(token); + value.replace(startPos, token.length(), editedToken); + } + } + } + } + } + value.erase(std::remove(value.begin(), value.end(), '\n'), value.end()); + (*it)._xmlContent = key + "{" + value + "}\n"; + _updateStyleContent(); + } + } + } +} + +/** + * @brief StyleDialog::_delProperty + * @param event + * @return + * This function deletes property when '-' in front of property in CSS panel is + * clicked. The property row is deleted from CSS panel and XML repr is updated. + * toDelProperty is the property to be deleted which is looked in 'value' and is + * erased from 'value'. + */ +bool StyleDialog::_delProperty(GdkEventButton *event) +{ + if (event->type == GDK_BUTTON_PRESS && event->button == 1) { + Gtk::TreeViewColumn *col = 0; + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + Gtk::TreeModel::Row cssRow; + Glib::ustring toDelProperty; + if (_cssPane->_treeView.get_path_at_pos(x, y, path, col, x2, y2)) { + if (col == _cssPane->_treeView.get_column(0)) { + Gtk::TreeModel::iterator cssIter = _cssPane->_treeView.get_selection() + ->get_selected(); + if (cssIter) { + cssRow = *cssIter; + toDelProperty = cssRow[_cssPane->_cssColumns._propertyLabel]; + } + + Gtk::TreeModel::iterator iter = _treeView.get_selection()->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + std::string sel, key, value; + std::vector<InkSelector>::iterator it; + for (it = _selectorVec.begin(); it != _selectorVec.end(); ++it) { + sel = (*it)._xmlContent; + REMOVE_SPACES(sel); + if (!sel.empty()) { + key = strtok((char*)sel.c_str(), "{"); + REMOVE_SPACES(key); + char *temp = strtok(NULL, "}"); + if (strtok(temp, "}") != NULL) { + value = strtok(temp, "}"); + } + } + + Glib::ustring selectedRowLabel = row[_mColumns._selectorLabel]; + std::string matchSelector = selectedRowLabel; + REMOVE_SPACES(matchSelector); + + if (key == matchSelector) { + std::size_t found = value.find(toDelProperty); + if (found!=std::string::npos) { + if (!toDelProperty.empty()) { + value.erase(found, toDelProperty.length()+1); + (*it)._xmlContent = key + "{" + value + "}\n"; + _updateStyleContent(); + _cssPane->_store->erase(cssRow); + } + } + } + } + } + } + } + } + return false; +} + +/** + * @brief StyleDialog::_selectObjects + * @param eventX + * @param eventY + * This function selects objects in the drawing corresponding to the selector + * selected in the treeview. + */ +void StyleDialog::_selectObjects(int eventX, int eventY) +{ + _desktop->selection->clear(); + Gtk::TreeViewColumn *col = _treeView.get_column(1); + Gtk::TreeModel::Path path; + int x = eventX; + int y = eventY; + int x2 = 0; + int y2 = 0; + if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) { + if (col == _treeView.get_column(1)) { + Gtk::TreeModel::iterator iter = _store->get_iter(path); + if (iter) { + Gtk::TreeModel::Row row = *iter; + Gtk::TreeModel::Children children = row.children(); + std::vector<SPObject *> objVec = row[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { + SPObject *obj = objVec[i]; + _desktop->selection->add(obj); + } + if (children) { + _checkAllChildren(children); + } + } + } + } +} + +/** + * @brief StyleDialog::_checkAllChildren + * @param children + * This function iterates children of the row selected in treeview and selects + * the objects corresponding to any selector in child rows. + */ +void StyleDialog::_checkAllChildren(Gtk::TreeModel::Children& children) +{ + for (Gtk::TreeModel::Children::iterator iter = children.begin(); + iter!= children.end(); ++iter) { + Gtk::TreeModel::Row childrow = *iter; + std::vector<SPObject *> objVec = childrow[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { + SPObject *obj = objVec[i]; + _desktop->selection->add(obj); + } + } +} + +/** + * @brief StyleDialog::_selectRow + * This function selects the rows in treeview corresponding to an object selected + * in the drawing. + */ +void StyleDialog::_selectRow(Selection */*sel*/) +{ + SPObject *obj = NULL; + if (!_desktop->selection->isEmpty()) { + Inkscape::Selection* selection = _desktop->getSelection(); + std::vector<SPObject*> selected = std::vector<SPObject *> + (selection->objects().begin(), selection->objects().end()); + obj = selected.back(); + } + + /** + * If obj has some SPObject, then it is added to desktop's selection. If a + * row in treeview has children, those rows are checked too against selected + * object's id. If an object which is not present in any selector is selected, + * the treeview's selections are unselected. + */ + if (obj != NULL) { + Gtk::TreeModel::Children children = _store->children(); + for(Gtk::TreeModel::Children::iterator iter = children.begin(); + iter != children.end(); ++iter) { + Gtk::TreeModel::Row row = *iter; + std::vector<SPObject *> objVec = row[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { + if (obj->getId() == objVec[i]->getId()) { + _treeView.get_selection()->select(row); + } + } + } + } + else { + _treeView.get_selection()->unselect_all(); + } +} + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h new file mode 100644 index 000000000..b03a1d2e1 --- /dev/null +++ b/src/ui/dialog/styledialog.h @@ -0,0 +1,117 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef STYLEDIALOG_H +#define STYLEDIALOG_H + +#include <ui/widget/panel.h> +#include <gtkmm/treeview.h> +#include <gtkmm/treestore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include <gtkmm/treeselection.h> +#include <gtkmm/paned.h> + +#include "desktop.h" +#include "document.h" +#include "ui/dialog/cssdialog.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief The StyleDialog class + * A list of CSS selectors will show up in this dialog. This dialog allows to + * add and delete selectors. Objects can be added to and removed from the selectors + * in the dialog. Besides, selection of any selector row selects the matching + * objects in the drawing and vice-versa. + */ +typedef std::pair<std::pair<std::string, std::vector<SPObject *> >, std::string> +_selectorVecType; + +class StyleDialog : public UI::Widget::Panel +{ +public: + StyleDialog(); + ~StyleDialog(); + + static StyleDialog &getInstance() { return *new StyleDialog(); } + void setDesktop(SPDesktop* desktop); + +private: + void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip); + std::string _setClassAttribute(std::vector<SPObject *>); + + class InkSelector { + public: + std::string _selector; + std::vector<SPObject *> _matchingObjs; + std::string _xmlContent; + }; + + InkSelector inkSelector; + std::vector<InkSelector> _selectorVec; + std::vector<InkSelector> _getSelectorVec(); + std::string _populateTree(std::vector<InkSelector>); + bool _handleButtonEvent(GdkEventButton *event); + void _buttonEventsSelectObjs(GdkEventButton *event); + void _selectObjects(int, int); + void _checkAllChildren(Gtk::TreeModel::Children& children); + Inkscape::XML::Node *_styleElementNode(); + void _updateStyleContent(); + void _selectRow(Selection *); + + class ModelColumns : public Gtk::TreeModel::ColumnRecord + { + public: + ModelColumns() + { add(_selectorLabel); add(_colAddRemove); add(_colObj); } + Gtk::TreeModelColumn<Glib::ustring> _selectorLabel; + Gtk::TreeModelColumn<bool> _colAddRemove; + Gtk::TreeModelColumn<std::vector<SPObject *> > _colObj; + }; + + SPDesktop* _desktop; + SPDesktop* _targetDesktop; + ModelColumns _mColumns; + Gtk::VPaned _paned; + Gtk::VBox _mainBox; + Gtk::HBox _buttonBox; + Gtk::TreeView _treeView; + Glib::RefPtr<Gtk::TreeStore> _store; + Gtk::ScrolledWindow _scrolledWindow; + Gtk::Button* del; + Gtk::Button* create; + SPDocument* _document; + bool _styleExists; + Inkscape::XML::Node *_styleChild; + std::string _selectorName; + std::string _selectorValue; + bool _newDrawing; + CssDialog *_cssPane; + void _selAdd(Gtk::TreeModel::Row row); + + // Signal handlers + void _addSelector(); + void _delSelector(); + void _selChanged(); + + // Signal handler for CssDialog + void _handleEdited(const Glib::ustring& path, const Glib::ustring& new_text); + bool _delProperty(GdkEventButton *event); +}; + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif // STYLEDIALOG_H diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index c908cede2..4f152e0a2 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -82,8 +82,19 @@ private: template <typename R> void invokeForAll(R (PathManipulator::*method)()) { - for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { - ((i->second.get())->*method)(); + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ) { + // Sometimes the PathManipulator got freed at loop end, thus + // invalidating the iterator so make sure that next_i will + // be a valid iterator and then assign i to it. + MapType::iterator next_i = i; + ++next_i; + // i->second is a boost::shared_ptr so try to hold on to it so + // it won't get freed prematurely by the WriteXML() method or + // whatever. See https://bugs.launchpad.net/inkscape/+bug/1617615 + // Applicable to empty paths. + boost::shared_ptr<PathManipulator> hold(i->second); + ((hold.get())->*method)(); + i = next_i; } } template <typename R, typename A> diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index 025c460e2..a05f0e3b9 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -20,12 +20,7 @@ #include <iosfwd> #include <stdexcept> #include <cstddef> - -#if __cplusplus >= 201103L #include <functional> -#else -#include <tr1/functional> -#endif #include <boost/enable_shared_from_this.hpp> #include <boost/shared_ptr.hpp> diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index 33015fe11..083a7d0ba 100644 --- a/src/ui/tool/transform-handle-set.cpp +++ b/src/ui/tool/transform-handle-set.cpp @@ -183,6 +183,11 @@ void TransformHandle::ungrabbed(GdkEventButton *) _setState(_state); endTransform(); _th.signal_commit.emit(getCommitEvent()); + + //updates the positions of the nodes + Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context); + ControlPointSelection* selection = nt->_selected_nodes; + selection->setOriginalPoints(); } diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp index 1801a0ea0..6e1d085aa 100644 --- a/src/ui/tools/flood-tool.cpp +++ b/src/ui/tools/flood-tool.cpp @@ -21,6 +21,7 @@ #include <config.h> #endif +#include <cmath> #include "trace/potrace/inkscape-potrace.h" #include <2geom/pathvector.h> #include <gdk/gdkkeysyms.h> @@ -186,6 +187,21 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width } /** + * \brief Check whether two unsigned integers are close to each other + * + * \param[in] a The 1st unsigned int + * \param[in] b The 2nd unsigned int + * \param[in] d The threshold for comparison + * + * \return true if |a-b| <= d; false otherwise + */ +static bool compare_guint32(guint32 const a, guint32 const b, guint32 const d) +{ + const int difference = std::abs(static_cast<int>(a) - static_cast<int>(b)); + return difference <= d; +} + +/** * Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation. * @param check The pixel in the pixel buffer to check. * @param orig The original selected pixel to use as the fill target color. @@ -196,7 +212,6 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width */ static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixel, guint32 dtc, int threshold, PaintBucketChannels method) { - int diff = 0; float hsl_check[3] = {0,0,0}, hsl_orig[3] = {0,0,0}; guint32 ac = 0, rc = 0, gc = 0, bc = 0; @@ -222,27 +237,35 @@ static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixe switch (method) { case FLOOD_CHANNELS_ALPHA: - return abs(static_cast<int>(ac) - ao) <= threshold; + return compare_guint32(ac, ao, threshold); case FLOOD_CHANNELS_R: - return abs(static_cast<int>(ac ? unpremul_alpha(rc, ac) : 0) - (ao ? unpremul_alpha(ro, ao) : 0)) <= threshold; + return compare_guint32(ac ? unpremul_alpha(rc, ac) : 0, + ao ? unpremul_alpha(ro, ao) : 0, + threshold); case FLOOD_CHANNELS_G: - return abs(static_cast<int>(ac ? unpremul_alpha(gc, ac) : 0) - (ao ? unpremul_alpha(go, ao) : 0)) <= threshold; + return compare_guint32(ac ? unpremul_alpha(gc, ac) : 0, + ao ? unpremul_alpha(go, ao) : 0, + threshold); case FLOOD_CHANNELS_B: - return abs(static_cast<int>(ac ? unpremul_alpha(bc, ac) : 0) - (ao ? unpremul_alpha(bo, ao) : 0)) <= threshold; + return compare_guint32(ac ? unpremul_alpha(bc, ac) : 0, + ao ? unpremul_alpha(bo, ao) : 0, + threshold); case FLOOD_CHANNELS_RGB: - guint32 amc, rmc, bmc, gmc; - //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255; - //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255; - amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha - rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255; - gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255; - bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255; - - diff += abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - (amop ? unpremul_alpha(rmop, amop) : 0)); - diff += abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - (amop ? unpremul_alpha(gmop, amop) : 0)); - diff += abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - (amop ? unpremul_alpha(bmop, amop) : 0)); - return ((diff / 3) <= ((threshold * 3) / 4)); - + { + guint32 amc, rmc, bmc, gmc; + //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255; + //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255; + amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha + rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255; + gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255; + bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255; + + int diff = 0; // The total difference between each of the 3 color components + diff += std::abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(rmop, amop) : 0)); + diff += std::abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(gmop, amop) : 0)); + diff += std::abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(bmop, amop) : 0)); + return ((diff / 3) <= ((threshold * 3) / 4)); + } case FLOOD_CHANNELS_H: return ((int)(fabs(hsl_check[0] - hsl_orig[0]) * 100.0) <= threshold); case FLOOD_CHANNELS_S: diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index e628094d9..ac43b6c9d 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -1137,7 +1137,7 @@ static void sp_mesh_new_default(MeshTool &rc) { Inkscape::FOR_FILL : Inkscape::FOR_STROKE; // Ensure mesh is immediately editable. - // Editting both fill and stroke at same time doesn't work well so avoid. + // Editing both fill and stroke at same time doesn't work well so avoid. if (fill_or_stroke == Inkscape::FOR_FILL) { prefs->setBool("/tools/mesh/edit_fill", true ); prefs->setBool("/tools/mesh/edit_stroke", false); diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index 8a35882b9..aab256712 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -94,6 +94,7 @@ ToolBase::ToolBase(gchar const *const *cursor_shape, gint hot_x, gint hot_y, boo , _grdrag(NULL) , shape_editor(NULL) , space_panning(false) + , rotating_mode(false) , _delayed_snap_event(NULL) , _dse_callback_in_process(false) , desktop(NULL) @@ -327,99 +328,130 @@ bool ToolBase::root_handler(GdkEvent* event) { static unsigned int panning = 0; static unsigned int panning_cursor = 0; static unsigned int zoom_rb = 0; + static double angle = 0; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); /// @todo REmove redundant /value in preference keys tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); bool allow_panning = prefs->getBool("/options/spacebarpans/value"); + int rotation_snap = 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12); gint ret = FALSE; switch (event->type) { - case GDK_2BUTTON_PRESS: - if (panning) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); - ret = TRUE; - } else { - /* sp_desktop_dialog(); */ - } - break; - - case GDK_BUTTON_PRESS: - // save drag origin - xp = (gint) event->button.x; - yp = (gint) event->button.y; - within_tolerance = true; - - button_w = Geom::Point(event->button.x, event->button.y); - - switch (event->button.button) { - case 1: - if (this->space_panning) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - if (_uses_snap) { - sp_event_context_discard_delayed_snap_event(this); - } - panning = 1; - - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK, NULL, - event->button.time - 1); - + case GDK_2BUTTON_PRESS: + if (panning) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); ret = TRUE; + } else { + /* sp_desktop_dialog(); */ } break; - case 2: - if (event->button.state & GDK_SHIFT_MASK) { - zoom_rb = 2; - } else { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - if (_uses_snap) { - sp_event_context_discard_delayed_snap_event(this); - } - panning = 2; + case GDK_BUTTON_PRESS: + // save drag origin + xp = (gint) event->button.x; + yp = (gint) event->button.y; + within_tolerance = true; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK, NULL, - event->button.time - 1); + button_w = Geom::Point(event->button.x, event->button.y); + switch (event->button.button) { + case 1: + if (this->space_panning) { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + if (_uses_snap) { + sp_event_context_discard_delayed_snap_event(this); + } + panning = 1; - } + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time - 1); - ret = TRUE; - break; + ret = TRUE; + } + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + break; - case 3: - if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - if (_uses_snap) { - sp_event_context_discard_delayed_snap_event(this); + case 2: + if (event->button.state & GDK_CONTROL_MASK) { + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); + desktop->canvas->startRotateTo(desktop->namedview->document_rotation); + this->rotating_mode = true; + this->message_context->set(Inkscape::INFORMATION_MESSAGE, + _("<b>MMB + mouse move</b> to rotate canvas, use modifiers on screen to change snaps")); + } else { + if (event->button.state & GDK_SHIFT_MASK) { + zoom_rb = 2; + } else { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + if (_uses_snap) { + sp_event_context_discard_delayed_snap_event(this); + } + panning = 2; + + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time - 1); + } + ret = TRUE; } - panning = 3; + ret = TRUE; + break; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK, NULL, - event->button.time); + case 3: + if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + if (_uses_snap) { + sp_event_context_discard_delayed_snap_event(this); + } + panning = 3; + + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time); + ret = TRUE; + } else if( !this->space_panning) { + sp_event_root_menu_popup(desktop, NULL, event); + } ret = TRUE; - } else { - sp_event_root_menu_popup(desktop, NULL, event); - } - break; + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + break; - default: + default: + break; + } break; - } - break; - case GDK_MOTION_NOTIFY: - if (panning) { - if (panning == 4 && !xp && !yp ) { + case GDK_MOTION_NOTIFY: + if (this->rotating_mode) { + button_w = Geom::Point(event->motion.x, event->motion.y); + Geom::Point const motion_dt(desktop->doc2dt(desktop->w2d(button_w))); + Geom::Rect view = desktop->get_display_area(); + Geom::Point view_center = desktop->doc2dt(view.midpoint()); + Geom::Ray center_ray(motion_dt, view_center); + angle = Geom::deg_from_rad(center_ray.angle()) - 90; + if (event->motion.state & GDK_SHIFT_MASK && event->motion.state & GDK_CONTROL_MASK) { + angle = 0; + } else if(event->motion.state & GDK_CONTROL_MASK) { + angle = floor(angle/rotation_snap) * rotation_snap; + } else if (event->motion.state & GDK_SHIFT_MASK) { + angle = desktop->namedview->document_rotation; + } else if (event->motion.state & GDK_MOD1_MASK) { + //Decimal raw angle + } else { + angle = floor(angle); + } + desktop->canvas->rotateTo(angle); + } else if (panning == 4 && !xp && !yp ) { // <Space> + mouse panning started, save location and grab canvas xp = event->motion.x; yp = event->motion.y; @@ -431,401 +463,465 @@ bool ToolBase::root_handler(GdkEvent* event) { | GDK_POINTER_MOTION_HINT_MASK, NULL, event->motion.time - 1); } + if (panning && !this->rotating_mode) { + if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK)) + || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK)) + || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) { + /* Gdk seems to lose button release for us sometimes :-( */ + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); + ret = TRUE; + } else { + if (within_tolerance && (abs((gint) event->motion.x - xp) + < tolerance) && (abs((gint) event->motion.y - yp) + < tolerance)) { + // do not drag if we're within tolerance from origin + break; + } + + // Once the user has moved farther than tolerance from + // the original location (indicating they intend to move + // the object, not click), then always process the motion + // notify coordinates as given (no snapping back to origin) + within_tolerance = false; + + // gobble subsequent motion events to prevent "sticking" + // when scrolling is slow + gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning + == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK)); + + if (panning_cursor == 0) { + panning_cursor = 1; + this->sp_event_context_set_cursor(GDK_FLEUR); + } + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const moved_w(motion_w - button_w); + this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw + ret = TRUE; + } + } else if (zoom_rb) { + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt(desktop->w2d(motion_w)); - if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK)) - || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK)) - || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) { - /* Gdk seems to lose button release for us sometimes :-( */ - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); - ret = TRUE; - } else { if (within_tolerance && (abs((gint) event->motion.x - xp) < tolerance) && (abs((gint) event->motion.y - yp) < tolerance)) { - // do not drag if we're within tolerance from origin - break; + break; // do not drag if we're within tolerance from origin } - // Once the user has moved farther than tolerance from - // the original location (indicating they intend to move - // the object, not click), then always process the motion - // notify coordinates as given (no snapping back to origin) + // Once the user has moved farther than tolerance from the original location + // (indicating they intend to move the object, not click), then always process the + // motion notify coordinates as given (no snapping back to origin) within_tolerance = false; - // gobble subsequent motion events to prevent "sticking" - // when scrolling is slow - gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning - == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK)); - - if (panning_cursor == 0) { - panning_cursor = 1; - this->sp_event_context_set_cursor(GDK_FLEUR); + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->move(motion_dt); + } else { + Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt); } - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const moved_w(motion_w - button_w); - this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw - ret = TRUE; - } - } else if (zoom_rb) { - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const motion_dt(desktop->w2d(motion_w)); - - if (within_tolerance && (abs((gint) event->motion.x - xp) - < tolerance) && (abs((gint) event->motion.y - yp) - < tolerance)) { - break; // do not drag if we're within tolerance from origin + if (zoom_rb == 2) { + gobble_motion_events(GDK_BUTTON2_MASK); + } } + break; - // Once the user has moved farther than tolerance from the original location - // (indicating they intend to move the object, not click), then always process the - // motion notify coordinates as given (no snapping back to origin) - within_tolerance = false; - - if (Inkscape::Rubberband::get(desktop)->is_started()) { - Inkscape::Rubberband::get(desktop)->move(motion_dt); + case GDK_BUTTON_RELEASE: + if (this->rotating_mode && event->button.button == 2) { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + ret = TRUE; + if (desktop->canvas->endRotateTo()) { + sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle); + } } else { - Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt); - } + xp = yp = 0; + if (panning_cursor == 1) { + panning_cursor = 0; + GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); + gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); + } - if (zoom_rb == 2) { - gobble_motion_events(GDK_BUTTON2_MASK); - } - } - break; + if (within_tolerance && (panning || zoom_rb)) { + zoom_rb = 0; - case GDK_BUTTON_RELEASE: - xp = yp = 0; + if (panning) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); + } - if (panning_cursor == 1) { - panning_cursor = 0; - GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); - gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); - } + Geom::Point const event_w(event->button.x, event->button.y); + Geom::Point const event_dt(desktop->w2d(event_w)); - if (within_tolerance && (panning || zoom_rb)) { - zoom_rb = 0; + double const zoom_inc = prefs->getDoubleLimited( + "/options/zoomincrement/value", M_SQRT2, 1.01, 10); - if (panning) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - } + desktop->zoom_relative_keep_point(event_dt, (event->button.state + & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc); - Geom::Point const event_w(event->button.x, event->button.y); - Geom::Point const event_dt(desktop->w2d(event_w)); + desktop->updateNow(); + ret = TRUE; + } else if (panning == event->button.button) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); - double const zoom_inc = prefs->getDoubleLimited( - "/options/zoomincrement/value", M_SQRT2, 1.01, 10); + // in slow complex drawings, some of the motion events are lost; + // to make up for this, we scroll it once again to the button-up event coordinates + // (i.e. canvas will always get scrolled all the way to the mouse release point, + // even if few intermediate steps were visible) + Geom::Point const motion_w(event->button.x, event->button.y); + Geom::Point const moved_w(motion_w - button_w); - desktop->zoom_relative_keep_point(event_dt, (event->button.state - & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc); + this->desktop->scroll_world(moved_w); + desktop->updateNow(); + ret = TRUE; + } else if (zoom_rb == event->button.button) { + zoom_rb = 0; - desktop->updateNow(); - ret = TRUE; - } else if (panning == event->button.button) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - - // in slow complex drawings, some of the motion events are lost; - // to make up for this, we scroll it once again to the button-up event coordinates - // (i.e. canvas will always get scrolled all the way to the mouse release point, - // even if few intermediate steps were visible) - Geom::Point const motion_w(event->button.x, event->button.y); - Geom::Point const moved_w(motion_w - button_w); - - this->desktop->scroll_world(moved_w); - desktop->updateNow(); - ret = TRUE; - } else if (zoom_rb == event->button.button) { - zoom_rb = 0; + Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); + Inkscape::Rubberband::get(desktop)->stop(); - Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); - Inkscape::Rubberband::get(desktop)->stop(); + if (b && !within_tolerance) { + desktop->set_display_area(*b, 10); + } - if (b && !within_tolerance) { - desktop->set_display_area(*b, 10); + ret = TRUE; + } + if (this->rotating_mode && event->button.button != 3) { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + ret = TRUE; + desktop->canvas->endRotateTo(); + } } + break; - ret = TRUE; - } - break; - - case GDK_KEY_PRESS: { - double const acceleration = prefs->getDoubleLimited( - "/options/scrollingacceleration/value", 0, 0, 6); - int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", - 10, 0, 1000); - - switch (get_group0_keyval(&event->key)) { - // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets - // in the editing window). So we resteal them back and run our regular shortcut - // invoker on them. - unsigned int shortcut; - case GDK_KEY_Tab: - case GDK_KEY_ISO_Left_Tab: - case GDK_KEY_F1: - shortcut = get_group0_keyval(&event->key); - - if (event->key.state & GDK_SHIFT_MASK) { - shortcut |= SP_SHORTCUT_SHIFT_MASK; + case GDK_KEY_PRESS: { + double const acceleration = prefs->getDoubleLimited( + "/options/scrollingacceleration/value", 0, 0, 6); + int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", + 10, 0, 1000); + + if (this->rotating_mode && + get_group0_keyval(&event->key) != GDK_KEY_space && + get_group0_keyval(&event->key) != GDK_KEY_Shift_L && + get_group0_keyval(&event->key) != GDK_KEY_Shift_R && + get_group0_keyval(&event->key) != GDK_KEY_Control_L && + get_group0_keyval(&event->key) != GDK_KEY_Control_R && + get_group0_keyval(&event->key) != GDK_KEY_Alt_L && + get_group0_keyval(&event->key) != GDK_KEY_Alt_R ) + { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + ret = TRUE; + desktop->canvas->endRotateTo(); + break; } - if (event->key.state & GDK_CONTROL_MASK) { - shortcut |= SP_SHORTCUT_CONTROL_MASK; - } + switch (get_group0_keyval(&event->key)) { + // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets + // in the editing window). So we resteal them back and run our regular shortcut + // invoker on them. + unsigned int shortcut; + case GDK_KEY_Tab: + case GDK_KEY_ISO_Left_Tab: + case GDK_KEY_F1: + shortcut = get_group0_keyval(&event->key); + + if (event->key.state & GDK_SHIFT_MASK) { + shortcut |= SP_SHORTCUT_SHIFT_MASK; + } - if (event->key.state & GDK_MOD1_MASK) { - shortcut |= SP_SHORTCUT_ALT_MASK; - } + if (event->key.state & GDK_CONTROL_MASK) { + shortcut |= SP_SHORTCUT_CONTROL_MASK; + } - ret = sp_shortcut_invoke(shortcut, desktop); - break; + if (event->key.state & GDK_MOD1_MASK) { + shortcut |= SP_SHORTCUT_ALT_MASK; + } - case GDK_KEY_Q: - case GDK_KEY_q: - if (desktop->quick_zoomed()) { - ret = TRUE; - } - if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) { - desktop->zoom_quick(true); - ret = TRUE; - } - break; + ret = sp_shortcut_invoke(shortcut, desktop); + break; - case GDK_KEY_W: - case GDK_KEY_w: - case GDK_KEY_F4: - /* Close view */ - if (MOD__CTRL_ONLY(event)) { - sp_ui_close_view(NULL); - ret = TRUE; - } - break; + case GDK_KEY_Q: + case GDK_KEY_q: + if (desktop->quick_zoomed()) { + ret = TRUE; + } + if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) { + desktop->zoom_quick(true); + ret = TRUE; + } + break; - case GDK_KEY_Left: // Ctrl Left - case GDK_KEY_KP_Left: - case GDK_KEY_KP_4: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + case GDK_KEY_W: + case GDK_KEY_w: + case GDK_KEY_F4: + /* Close view */ + if (MOD__CTRL_ONLY(event)) { + sp_ui_close_view(NULL); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(i, 0); - ret = TRUE; - } - break; + case GDK_KEY_Left: // Ctrl Left + case GDK_KEY_KP_Left: + case GDK_KEY_KP_4: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Up: // Ctrl Up - case GDK_KEY_KP_Up: - case GDK_KEY_KP_8: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(i, 0); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(0, i); - ret = TRUE; - } - break; + case GDK_KEY_Up: // Ctrl Up + case GDK_KEY_KP_Up: + case GDK_KEY_KP_8: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Right: // Ctrl Right - case GDK_KEY_KP_Right: - case GDK_KEY_KP_6: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(0, i); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(-i, 0); - ret = TRUE; - } - break; + case GDK_KEY_Right: // Ctrl Right + case GDK_KEY_KP_Right: + case GDK_KEY_KP_6: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Down: // Ctrl Down - case GDK_KEY_KP_Down: - case GDK_KEY_KP_2: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(-i, 0); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(0, -i); - ret = TRUE; - } - break; + case GDK_KEY_Down: // Ctrl Down + case GDK_KEY_KP_Down: + case GDK_KEY_KP_2: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Menu: - sp_event_root_menu_popup(desktop, NULL, event); - ret = TRUE; - break; + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(0, -i); + ret = TRUE; + } + break; - case GDK_KEY_F10: - if (MOD__SHIFT_ONLY(event)) { + case GDK_KEY_Menu: sp_event_root_menu_popup(desktop, NULL, event); ret = TRUE; - } - break; + break; - case GDK_KEY_space: - within_tolerance = true; - xp = yp = 0; - if (!allow_panning) break; - panning = 4; - this->space_panning = true; - this->message_context->set(Inkscape::INFORMATION_MESSAGE, - _("<b>Space+mouse move</b> to pan canvas")); + case GDK_KEY_F10: + if (MOD__SHIFT_ONLY(event)) { + sp_event_root_menu_popup(desktop, NULL, event); + ret = TRUE; + } + break; - ret = TRUE; + case GDK_KEY_space: +// if (event->key.state & GDK_CONTROL_MASK) { +// sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); +// desktop->canvas->startRotateTo(desktop->namedview->document_rotation); +// this->rotating_mode = true; +// this->message_context->set(Inkscape::INFORMATION_MESSAGE, +// _("<b>Space+mouse move</b> to rotate canvas, use modifiers on screen to change snaps")); +// } else { + within_tolerance = true; + xp = yp = 0; + if (!allow_panning) break; + panning = 4; + this->space_panning = true; + this->message_context->set(Inkscape::INFORMATION_MESSAGE, + _("<b>Space+mouse move</b> to pan canvas")); +// } + ret = TRUE; + break; + + case GDK_KEY_z: + case GDK_KEY_Z: + if (MOD__ALT_ONLY(event)) { + desktop->zoom_grab_focus(); + ret = TRUE; + } + break; + + default: + break; + } + } break; - case GDK_KEY_z: - case GDK_KEY_Z: - if (MOD__ALT_ONLY(event)) { - desktop->zoom_grab_focus(); + case GDK_KEY_RELEASE: + if (this->rotating_mode && + get_group0_keyval(&event->key) != GDK_KEY_space && + get_group0_keyval(&event->key) != GDK_KEY_Shift_L && + get_group0_keyval(&event->key) != GDK_KEY_Shift_R && + get_group0_keyval(&event->key) != GDK_KEY_Control_L && + get_group0_keyval(&event->key) != GDK_KEY_Control_R && + get_group0_keyval(&event->key) != GDK_KEY_Alt_L && + get_group0_keyval(&event->key) != GDK_KEY_Alt_R ) + { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; ret = TRUE; + desktop->canvas->endRotateTo(); + break; } - break; - default: - break; + // Stop panning on any key release + if (this->space_panning) { + this->space_panning = false; + this->message_context->clear(); } - } - break; - case GDK_KEY_RELEASE: - // Stop panning on any key release - if (this->space_panning) { - this->space_panning = false; - this->message_context->clear(); - } + if (panning) { + panning = 0; + xp = yp = 0; - if (panning) { - panning = 0; - xp = yp = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->key.time); - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->key.time); + desktop->updateNow(); + } - desktop->updateNow(); - } + if (panning_cursor == 1) { + panning_cursor = 0; + GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); + gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); + } - if (panning_cursor == 1) { - panning_cursor = 0; - GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); - gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); - } + switch (get_group0_keyval(&event->key)) { + case GDK_KEY_space: +// if (this->rotating_mode) { +// desktop->canvas->clearRotateTo(); +// this->rotating_mode = false; +// ret = TRUE; +// if (desktop->canvas->endRotateTo()) { +// sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle); +// } +// } + if (within_tolerance) { + // Space was pressed, but not panned + sp_toggle_selector(desktop); + + // Be careful, sp_toggle_selector will delete ourselves. + // Thus, make sure we return immediately. + return true; + } + break; - switch (get_group0_keyval(&event->key)) { - case GDK_KEY_space: - if (within_tolerance) { - // Space was pressed, but not panned - sp_toggle_selector(desktop); + case GDK_KEY_Q: + case GDK_KEY_q: + if (desktop->quick_zoomed()) { + desktop->zoom_quick(false); + ret = TRUE; + } + break; - // Be careful, sp_toggle_selector will delete ourselves. - // Thus, make sure we return immediately. - return true; + default: + break; } - break; - case GDK_KEY_Q: - case GDK_KEY_q: - if (desktop->quick_zoomed()) { - desktop->zoom_quick(false); - ret = TRUE; + case GDK_SCROLL: { + if (this->rotating_mode) { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + desktop->canvas->endRotateTo(); } - break; - - default: - break; - } - break; + bool ctrl = (event->scroll.state & GDK_CONTROL_MASK); + bool wheelzooms = prefs->getBool("/options/wheelzooms/value"); - case GDK_SCROLL: { - bool ctrl = (event->scroll.state & GDK_CONTROL_MASK); - bool wheelzooms = prefs->getBool("/options/wheelzooms/value"); + int const wheel_scroll = prefs->getIntLimited( + "/options/wheelscroll/value", 40, 0, 1000); - int const wheel_scroll = prefs->getIntLimited( - "/options/wheelscroll/value", 40, 0, 1000); + // Size of smooth-scrolls (only used in GTK+ 3) + gdouble delta_x = 0; + gdouble delta_y = 0; - // Size of smooth-scrolls (only used in GTK+ 3) - gdouble delta_x = 0; - gdouble delta_y = 0; - - /* shift + wheel, pan left--right */ - if (event->scroll.state & GDK_SHIFT_MASK) { - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - desktop->scroll_world(wheel_scroll, 0); - break; + /* shift + wheel, pan left--right */ + if (event->scroll.state & GDK_SHIFT_MASK) { + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + desktop->scroll_world(wheel_scroll, 0); + break; - case GDK_SCROLL_DOWN: - desktop->scroll_world(-wheel_scroll, 0); - break; + case GDK_SCROLL_DOWN: + desktop->scroll_world(-wheel_scroll, 0); + break; - default: - break; - } + default: + break; + } - /* ctrl + wheel, zoom in--out */ - } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) { - double rel_zoom; - double const zoom_inc = prefs->getDoubleLimited( - "/options/zoomincrement/value", M_SQRT2, 1.01, 10); + /* ctrl + wheel, zoom in--out */ + } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) { + double rel_zoom; + double const zoom_inc = prefs->getDoubleLimited( + "/options/zoomincrement/value", M_SQRT2, 1.01, 10); - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - rel_zoom = zoom_inc; - break; + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + rel_zoom = zoom_inc; + break; - case GDK_SCROLL_DOWN: - rel_zoom = 1 / zoom_inc; - break; + case GDK_SCROLL_DOWN: + rel_zoom = 1 / zoom_inc; + break; - default: - rel_zoom = 0.0; - break; - } + default: + rel_zoom = 0.0; + break; + } - if (rel_zoom != 0.0) { - Geom::Point const scroll_dt = desktop->point(); - desktop->zoom_relative_keep_point(scroll_dt, rel_zoom); - } + if (rel_zoom != 0.0) { + Geom::Point const scroll_dt = desktop->point(); + desktop->zoom_relative_keep_point(scroll_dt, rel_zoom); + } - /* no modifier, pan up--down (left--right on multiwheel mice?) */ - } else { - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - desktop->scroll_world(0, wheel_scroll); - break; + /* no modifier, pan up--down (left--right on multiwheel mice?) */ + } else { + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + desktop->scroll_world(0, wheel_scroll); + break; - case GDK_SCROLL_DOWN: - desktop->scroll_world(0, -wheel_scroll); - break; + case GDK_SCROLL_DOWN: + desktop->scroll_world(0, -wheel_scroll); + break; - case GDK_SCROLL_LEFT: - desktop->scroll_world(wheel_scroll, 0); - break; + case GDK_SCROLL_LEFT: + desktop->scroll_world(wheel_scroll, 0); + break; - case GDK_SCROLL_RIGHT: - desktop->scroll_world(-wheel_scroll, 0); - break; + case GDK_SCROLL_RIGHT: + desktop->scroll_world(-wheel_scroll, 0); + break; - case GDK_SCROLL_SMOOTH: - gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); - desktop->scroll_world(delta_x, delta_y); - break; + case GDK_SCROLL_SMOOTH: + gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); + desktop->scroll_world(delta_x, delta_y); + break; + } } + break; } - break; - } - default: - break; + default: + break; } - return ret; } diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h index 58eb6f88e..3d22fc66f 100644 --- a/src/ui/tools/tool-base.h +++ b/src/ui/tools/tool-base.h @@ -176,6 +176,7 @@ public: ShapeEditor* shape_editor; bool space_panning; + bool rotating_mode; DelayedSnapEvent *_delayed_snap_event; bool _dse_callback_in_process; diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index fa7a83732..0370d55db 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -781,62 +781,7 @@ void SelectedStyle::on_stroke_paste() { } void SelectedStyle::on_fillstroke_swap() { - SPCSSAttr *css = sp_repr_css_attr_new (); - - switch (_mode[SS_FILL]) { - case SS_NA: - case SS_MANY: - break; - case SS_NONE: - sp_repr_css_set_property (css, "stroke", "none"); - break; - case SS_UNSET: - sp_repr_css_unset_property (css, "stroke"); - break; - case SS_COLOR: - gchar c[64]; - sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]); - sp_repr_css_set_property (css, "stroke", c); - break; - case SS_LGRADIENT: - case SS_RGRADIENT: -#ifdef WITH_MESH - case SS_MGRADIENT: -#endif - case SS_PATTERN: - sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str()); - break; - } - - switch (_mode[SS_STROKE]) { - case SS_NA: - case SS_MANY: - break; - case SS_NONE: - sp_repr_css_set_property (css, "fill", "none"); - break; - case SS_UNSET: - sp_repr_css_unset_property (css, "fill"); - break; - case SS_COLOR: - gchar c[64]; - sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]); - sp_repr_css_set_property (css, "fill", c); - break; - case SS_LGRADIENT: - case SS_RGRADIENT: -#ifdef WITH_MESH - case SS_MGRADIENT: -#endif - case SS_PATTERN: - sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str()); - break; - } - - sp_desktop_set_style (_desktop, css); - sp_repr_css_attr_unref (css); - DocumentUndo::done(_desktop->getDocument(), SP_VERB_DIALOG_FILL_STROKE, - _("Swap fill and stroke")); + _desktop->getSelection()->swapFillStroke(); } void SelectedStyle::on_fill_edit() { diff --git a/src/uri-references.cpp b/src/uri-references.cpp index 170c98beb..7d6abd10a 100644 --- a/src/uri-references.cpp +++ b/src/uri-references.cpp @@ -11,16 +11,19 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "uri-references.h" + #include <iostream> #include <cstring> +#include <glibmm/miscutils.h> + +#include "bad-uri-exception.h" #include "document.h" #include "sp-object.h" #include "uri.h" -#include "uri-references.h" #include "extract-uri.h" #include "sp-tag-use.h" -#include <glibmm/miscutils.h> namespace Inkscape { @@ -103,7 +106,7 @@ bool URIReference::_acceptObject(SPObject *obj) const -void URIReference::attach(const URI &uri) throw(BadURIException) +void URIReference::attach(const URI &uri) { SPDocument *document = NULL; diff --git a/src/uri-references.h b/src/uri-references.h index e56ea0612..ce0f5f2ae 100644 --- a/src/uri-references.h +++ b/src/uri-references.h @@ -20,7 +20,6 @@ #include <sigc++/connection.h> #include <sigc++/trackable.h> -#include "bad-uri-exception.h" #include "sp-object.h" #include "sp-item.h" #include "sp-use.h" @@ -66,7 +65,7 @@ public: * @param rel_document document for relative URIs * @param uri the URI to watch */ - void attach(URI const& uri) throw(BadURIException); + void attach(URI const& uri); /** * Detaches from the currently attached URI target, if any; diff --git a/src/uri.cpp b/src/uri.cpp index f2578b989..9178c8ae8 100644 --- a/src/uri.cpp +++ b/src/uri.cpp @@ -8,11 +8,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib.h> #include "uri.h" + #include <glibmm/ustring.h> #include <glibmm/miscutils.h> +#include "bad-uri-exception.h" + namespace Inkscape { URI::URI() { @@ -25,7 +27,7 @@ URI::URI(const URI &uri) { _impl = uri._impl; } -URI::URI(gchar const *preformed) throw(BadURIException) { +URI::URI(gchar const *preformed) { xmlURIPtr uri; if (!preformed) { throw MalformedURIException(); @@ -132,7 +134,7 @@ const gchar *URI::Impl::getOpaque() const { return (gchar *)_uri->opaque; } -gchar *URI::to_native_filename(gchar const* uri) throw(BadURIException) +gchar *URI::to_native_filename(gchar const* uri) { gchar *filename = NULL; URI tmp(uri); @@ -166,7 +168,7 @@ const std::string URI::getFullPath(std::string const &base) const { /* TODO !!! proper error handling */ -gchar *URI::toNativeFilename() const throw(BadURIException) { +gchar *URI::toNativeFilename() const { gchar *uriString = toString(); if (isRelativePath()) { return uriString; @@ -181,7 +183,7 @@ gchar *URI::toNativeFilename() const throw(BadURIException) { } } -URI URI::fromUtf8( gchar const* path ) throw (BadURIException) { +URI URI::fromUtf8( gchar const* path ) { if ( !path ) { throw MalformedURIException(); } @@ -215,7 +217,7 @@ URI URI::fromUtf8( gchar const* path ) throw (BadURIException) { } /* TODO !!! proper error handling */ -URI URI::from_native_filename(gchar const *path) throw(BadURIException) { +URI URI::from_native_filename(gchar const *path) { gchar *uri = g_filename_to_uri(path, NULL, NULL); URI result(uri); g_free( uri ); @@ -13,7 +13,6 @@ #include <exception> #include <libxml/uri.h> -#include "bad-uri-exception.h" #include <string> namespace Inkscape { @@ -37,7 +36,7 @@ public: * * @param preformed Properly quoted C-style string to be represented. */ - explicit URI(char const *preformed) throw(BadURIException); + explicit URI(char const *preformed); /** * Destructor. @@ -98,15 +97,15 @@ public: const char *getOpaque() const { return _impl->getOpaque(); } - static URI fromUtf8( char const* path ) throw (BadURIException); + static URI fromUtf8( char const* path ); - static URI from_native_filename(char const *path) throw(BadURIException); + static URI from_native_filename(char const *path); - static char *to_native_filename(char const* uri) throw(BadURIException); + static char *to_native_filename(char const* uri); const std::string getFullPath(std::string const &base) const; - char *toNativeFilename() const throw(BadURIException); + char *toNativeFilename() const; /** * Returns a glib string version of this URI. diff --git a/src/verbs.cpp b/src/verbs.cpp index 975a3679e..fce823408 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1086,6 +1086,9 @@ void EditVerb::perform(SPAction *action, void *data) case SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER: sp_selection_next_patheffect_param(dt); break; + case SP_VERB_EDIT_SWAP_FILL_STROKE: + dt->selection->swapFillStroke(); + break; case SP_VERB_EDIT_LINK_COLOR_PROFILE: break; case SP_VERB_EDIT_REMOVE_COLOR_PROFILE: @@ -2154,6 +2157,12 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_PRINT_COLORS_PREVIEW: dt->_dlg_mgr->showDialog("PrintColorsPreviewDialog"); break; + case SP_VERB_DIALOG_STYLE: + dt->_dlg_mgr->showDialog("StyleDialog"); + break; + case SP_VERB_DIALOG_CSS: + dt->_dlg_mgr->showDialog("CssDialog"); + break; default: break; @@ -2585,6 +2594,8 @@ Verb *Verb::_base_verbs[] = { N_("Create four guides aligned with the page borders"), NULL), new EditVerb(SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER, "EditNextPathEffectParameter", N_("Next path effect parameter"), N_("Show next editable path effect parameter"), INKSCAPE_ICON("path-effect-parameter-next")), + new EditVerb(SP_VERB_EDIT_SWAP_FILL_STROKE, "EditSwapFillStroke", N_("Swap fill and stroke"), + N_("Swap fill and stroke of an object"), NULL), // Selection new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"), @@ -3000,6 +3011,10 @@ Verb *Verb::_base_verbs[] = { N_("View Objects"), INKSCAPE_ICON("dialog-layers")), new DialogVerb(SP_VERB_DIALOG_TAGS, "DialogTags", N_("Selection se_ts..."), N_("View Tags"), INKSCAPE_ICON("edit-select-all-layers")), + new DialogVerb(SP_VERB_DIALOG_STYLE, "DialogStyle", N_("Style Dialog..."), + N_("View Style Dialog"), NULL), + new DialogVerb(SP_VERB_DIALOG_CSS, "DialogCss", N_("Css Dialog..."), + N_("View Css Dialog"), NULL), new DialogVerb(SP_VERB_DIALOG_LIVE_PATH_EFFECT, "DialogLivePathEffect", N_("Path E_ffects ..."), N_("Manage, edit, and apply path effects"), INKSCAPE_ICON("dialog-path-effects")), new DialogVerb(SP_VERB_DIALOG_FILTER_EFFECTS, "DialogFilterEffects", N_("Filter _Editor..."), diff --git a/src/verbs.h b/src/verbs.h index d7e966ae4..21e9b5547 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -110,6 +110,7 @@ enum { SP_VERB_EDIT_GUIDES_TOGGLE_LOCK, SP_VERB_EDIT_GUIDES_AROUND_PAGE, SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER, + SP_VERB_EDIT_SWAP_FILL_STROKE, /* Selection */ SP_VERB_SELECTION_TO_FRONT, SP_VERB_SELECTION_TO_BACK, @@ -316,6 +317,8 @@ enum { SP_VERB_DIALOG_LAYERS, SP_VERB_DIALOG_OBJECTS, SP_VERB_DIALOG_TAGS, + SP_VERB_DIALOG_STYLE, + SP_VERB_DIALOG_CSS, SP_VERB_DIALOG_LIVE_PATH_EFFECT, SP_VERB_DIALOG_FILTER_EFFECTS, SP_VERB_DIALOG_SVG_FONTS, diff --git a/src/viewbox.cpp b/src/viewbox.cpp index 1b50fe71c..ebfc04d8a 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -17,6 +17,8 @@ #include "viewbox.h" #include "enums.h" #include "sp-item.h" +#include "inkscape.h" +#include "desktop.h" SPViewBox::SPViewBox() : viewBox_set(false) @@ -25,6 +27,11 @@ SPViewBox::SPViewBox() , aspect_align(SP_ASPECT_XMID_YMID) // Default per spec , aspect_clip(SP_ASPECT_MEET) , c2p(Geom::identity()) + , vbt(Geom::identity()) + , rotation(Geom::identity()) + , angle(0) + , previous_angle(0) + , rotated(false) { } @@ -159,6 +166,16 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) { } } +double SPViewBox::get_rotation() { + return this->angle; +} + +void SPViewBox::set_rotation(double angle_val) { + this->previous_angle = this->angle; + this->angle = angle_val; + this->rotated = true; +} + // Apply scaling from viewbox void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) { @@ -222,22 +239,41 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) { break; } } - /* Viewbox transform from scale and position */ - Geom::Affine q; - q[0] = scale_x; - q[1] = 0.0; - q[2] = 0.0; - q[3] = scale_y; - q[4] = x - scale_x * this->viewBox.left(); - q[5] = y - scale_y * this->viewBox.top(); - - // std::cout << " q\n" << q << std::endl; - - /* Append viewbox transformation */ - this->c2p = q * this->c2p; + vbt = Geom::identity(); + vbt[0] = scale_x; + vbt[1] = 0.0; + vbt[2] = 0.0; + vbt[3] = scale_y; + vbt[4] = x - scale_x * this->viewBox.left(); + vbt[5] = y - scale_y * this->viewBox.top(); + /* Append viewbox and turn transformation */ + Geom::Point page_center = this->viewBox.midpoint(); + SPDesktop * desktop = SP_ACTIVE_DESKTOP; + if (this->angle > 0.0 || this->angle < 0.0 ) { //!0 + if (desktop) { + rotation = Geom::Translate(page_center).inverse() * Geom::Rotate(Geom::rad_from_deg(angle)) * Geom::Translate(page_center); + this->c2p = rotation * vbt * this->c2p; + } else { + this->c2p = vbt * this->c2p; + } + } else { + this->c2p = vbt * this->c2p; + } + if (desktop && this->rotated) { + Geom::Rect view = desktop->get_display_area(); + Geom::Point view_center = desktop->doc2dt(view.midpoint()); + Geom::Affine center_rotation = Geom::identity(); + center_rotation *= Geom::Translate(page_center * vbt).inverse(); + center_rotation *= Geom::Rotate(Geom::rad_from_deg(this->angle - this->previous_angle)); + center_rotation *= Geom::Translate(page_center * vbt); + view_center = desktop->dt2doc(view_center * center_rotation); + desktop->zoom_relative(view_center[Geom::X], view_center[Geom::Y], 1.0); + this->rotated = false; + } } + SPItemCtx SPViewBox::get_rctx(const SPItemCtx* ictx, double scale_none) { /* Create copy of item context */ diff --git a/src/viewbox.h b/src/viewbox.h index c71abb610..2f107132c 100644 --- a/src/viewbox.h +++ b/src/viewbox.h @@ -36,7 +36,14 @@ public: /* Child to parent additional transform */ Geom::Affine c2p; + Geom::Affine vbt; + Geom::Affine rotation; + double angle; + double previous_angle; + bool rotated; + double get_rotation(); + void set_rotation(double angle_val); void set_viewBox(const gchar* value); void set_preserveAspectRatio(const gchar* value); diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 6b8ade0fb..682e4e103 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -54,7 +54,7 @@ #include "ui/uxmanager.h" #include "util/ege-appear-time-tracker.h" #include "sp-root.h" - +#include "attributes.h" // We're in the "widgets" directory, so no need to explicitly prefix these: #include "button.h" #include "gimp/ruler.h" @@ -62,16 +62,11 @@ #include "spw-utilities.h" #include "toolbox.h" #include "widget-sizes.h" - #include "verbs.h" #include <gtkmm/cssprovider.h> #include <gtkmm/paned.h> #include <gtkmm/messagedialog.h> - -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif +#include <iomanip> using Inkscape::UI::Widget::UnitTracker; using Inkscape::UI::UXManager; @@ -97,8 +92,20 @@ static void sp_update_guides_lock( GtkWidget *button, gpointer data ); static void cms_adjust_toggled( GtkWidget *button, gpointer data ); #endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) static void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled ); +static void sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw); static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw); +static gint sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer data); +static bool sp_dtw_rotation_output (GtkSpinButton *spin, gpointer data); +static void sp_dtw_rotation_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data); +static void sp_dtw_rotate_minus_180 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_minus_135 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_minus_90 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_minus_45 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_0 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_45 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_90 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_135 (GtkMenuItem *item, SPDesktopWidget * data); static gdouble sp_dtw_zoom_value_to_display (gdouble value); static gdouble sp_dtw_zoom_display_to_value (gdouble value); static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data); @@ -585,6 +592,34 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw ) g_signal_connect (G_OBJECT (dtw->zoom_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->zoom_status); dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw); dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw); + auto css_provider_spinbutton = Gtk::CssProvider::create(); + css_provider_spinbutton->load_from_data("* { padding-left: 2px; padding-right: 2px; padding-top: 0px; padding-bottom: 0px;}"); + auto zoomstat = Glib::wrap(dtw->zoom_status); + zoomstat->set_name("ZoomStatus"); + auto context_zoom = zoomstat->get_style_context(); + context_zoom->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + // Rotate status spinbutton + dtw->rotation_status = gtk_spin_button_new_with_range (-360.0,360.0, 1.0); + gtk_widget_set_tooltip_text (dtw->rotation_status, _("Rotation. Can be interactive with CTRL+MMB")); + gtk_widget_set_size_request (dtw->rotation_status, STATUS_ROTATION_WIDTH, -1); + gtk_entry_set_width_chars (GTK_ENTRY (dtw->rotation_status), 7); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dtw->rotation_status), FALSE); + gtk_spin_button_set_digits (GTK_SPIN_BUTTON (dtw->rotation_status), 2); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dtw->rotation_status), GTK_UPDATE_ALWAYS); + g_signal_connect (G_OBJECT (dtw->rotation_status), "input", G_CALLBACK (sp_dtw_rotation_input), dtw); + g_signal_connect (G_OBJECT (dtw->rotation_status), "output", G_CALLBACK (sp_dtw_rotation_output), dtw); + g_object_set_data (G_OBJECT (dtw->rotation_status), "dtw", dtw->canvas); + g_signal_connect (G_OBJECT (dtw->rotation_status), "focus-in-event", G_CALLBACK (spinbutton_focus_in), dtw->rotation_status); + g_signal_connect (G_OBJECT (dtw->rotation_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->rotation_status); + dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "value_changed", G_CALLBACK (sp_desktop_widget_rotate_document), dtw); + dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "populate_popup", G_CALLBACK (sp_dtw_rotation_populate_popup), dtw); + + auto rotstat = Glib::wrap(dtw->rotation_status); + rotstat->set_name("RotationStatus"); + auto context_rotation = rotstat->get_style_context(); + context_rotation->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + // Cursor coordinates dtw->coord_status = gtk_grid_new(); @@ -611,12 +646,16 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw ) auto label_z = gtk_label_new(_("Z:")); gtk_widget_set_name(label_z, "ZLabel"); + auto label_r = gtk_label_new(_("R:")); + gtk_widget_set_name(label_r, "RLabel"); gtk_widget_set_halign(dtw->coord_status_x, GTK_ALIGN_END); gtk_widget_set_halign(dtw->coord_status_y, GTK_ALIGN_END); gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_x, 2, 0, 1, 1); gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_y, 2, 1, 1, 1); gtk_grid_attach(GTK_GRID(dtw->coord_status), label_z, 3, 0, 1, 2); + gtk_grid_attach(GTK_GRID(dtw->coord_status), label_r, 5, 0, 1, 2); gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->zoom_status, 4, 0, 1, 2); + gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->rotation_status, 6, 0, 1, 2); sp_set_font_size_smaller (dtw->coord_status); @@ -684,6 +723,11 @@ static void sp_desktop_widget_dispose(GObject *object) g_signal_handlers_disconnect_matched (G_OBJECT (dtw->zoom_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->zoom_status); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_value_changed), dtw); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_populate_popup), dtw); + g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_input), dtw); + g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_output), dtw); + g_signal_handlers_disconnect_matched (G_OBJECT (dtw->rotation_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->rotation_status); + g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_desktop_widget_rotate_document), dtw); + g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_dtw_rotation_populate_popup), dtw); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas), (gpointer) G_CALLBACK (sp_desktop_widget_event), dtw); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas_tbl), (gpointer) G_CALLBACK (canvas_tbl_size_allocate), dtw); @@ -1415,7 +1459,7 @@ void SPDesktopWidget::layoutWidgets() } else { gtk_widget_show_all (dtw->menubar); } - + if (!prefs->getBool(pref_root + "commands/state", true)) { gtk_widget_hide (dtw->commands_toolbox); } else { @@ -1621,10 +1665,11 @@ SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview) dtw->menubar = sp_ui_main_menubar (dtw->desktop); gtk_widget_set_name(dtw->menubar, "MenuBar"); gtk_widget_show_all (dtw->menubar); + SPNamedView *nv = dtw->desktop->namedview; gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0); - dtw->layoutWidgets(); - + gtk_spin_button_set_value(GTK_SPIN_BUTTON (dtw->rotation_status), namedview->document_rotation); + sp_namedview_set_document_rotation(namedview); std::vector<GtkWidget *> toolboxes; toolboxes.push_back(dtw->tool_toolbox); toolboxes.push_back(dtw->aux_toolbox); @@ -1664,6 +1709,8 @@ sp_desktop_widget_update_rulers (SPDesktopWidget *dtw) void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) { SPNamedView *nv=SP_NAMEDVIEW(obj); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(this->rotation_status), desktop->namedview->document_rotation); + sp_namedview_set_document_rotation(nv); if (flags & SP_OBJECT_MODIFIED_FLAG) { this->dt2r = 1. / nv->display_units->factor; @@ -1689,9 +1736,16 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) if (GTK_IS_CONTAINER(i->data)) { GList *grch = gtk_container_get_children (GTK_CONTAINER(i->data)); for (GList *j = grch; j != NULL; j = j->next) { + if (!GTK_IS_WIDGET(j->data)) // wasn't a widget continue; + // Don't apply to text toolbar. We want to be able to + // use different units for text. (Bug 1562217) + const gchar* name = gtk_widget_get_name( (GTK_WIDGET(j->data)) ); + if (strcmp( name, "TextToolbar") == 0) + continue; + gpointer t = sp_search_by_data_recursive(GTK_WIDGET(j->data), (gpointer) "tracker"); if (t == NULL) // didn't find any tracker data continue; @@ -1715,6 +1769,18 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) } static void +sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw) +{ + SPNamedView *nv = dtw->desktop->namedview; + double value = gtk_spin_button_get_value (spin); + if (!dtw->desktop->getDocument()->getRoot()->rotated && value != nv->document_rotation) { + sp_repr_set_svg_double(nv->getRepr(), "inkscape:document-rotation", value); + } + spinbutton_defocus (GTK_WIDGET(spin)); +} + + +static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment */*adj*/, SPDesktopWidget *dtw) { if (dtw->update) @@ -1794,6 +1860,34 @@ sp_dtw_zoom_output (GtkSpinButton *spin, gpointer /*data*/) return TRUE; } +static gint +sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/) +{ + gdouble new_scrolled = gtk_spin_button_get_value (spin); + const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin)); + gdouble new_typed = atof (b); + + if (new_scrolled == new_typed) { // the new value is set by scrolling + *new_val = new_scrolled; + } else { // the new value is typed in + *new_val = new_typed; + } + + return TRUE; +} + +static bool +sp_dtw_rotation_output (GtkSpinButton *spin, gpointer /*data*/) +{ + gchar b[64]; + double val = gtk_spin_button_get_value (spin); + std::ostringstream s; + s.imbue(std::locale(""));; + s << std::fixed << std::setprecision(2) << val << "º"; + gtk_entry_set_text (GTK_ENTRY (spin), s.str().c_str()); + return TRUE; +} + static void sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data) { @@ -1932,6 +2026,110 @@ sp_dtw_zoom_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data) gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); } + +static void +sp_dtw_rotation_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data) +{ + GList *children, *iter; + GtkWidget *item; + SPDesktopWidget *dtw = static_cast<SPDesktopWidget*>(data); + children = gtk_container_get_children (GTK_CONTAINER (menu)); + for ( iter = children ; iter ; iter = g_list_next (iter)) { + gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (iter->data)); + } + g_list_free (children); + + item = gtk_menu_item_new_with_label ("-180º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_180), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("-135º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_135), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("-90º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_90), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("-45º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_45), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("0º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_0), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("45º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_45), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + + item = gtk_menu_item_new_with_label ("90º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_90), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + + item = gtk_menu_item_new_with_label ("135º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_135), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); +} + +static void +sp_dtw_rotate_minus_180 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status),-180); +} + +static void +sp_dtw_rotate_minus_135 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -135); +} + +static void +sp_dtw_rotate_minus_90 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -90); +} + +static void +sp_dtw_rotate_minus_45 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -45); +} + +static void +sp_dtw_rotate_0 (GtkMenuItem */*item*/,SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 0); +} + +static void +sp_dtw_rotate_45 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 45); +} + +static void +sp_dtw_rotate_90 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 90); +} + +static void +sp_dtw_rotate_135 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 135); +} + static void sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor) { diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h index 08966ad5f..61c3b8b37 100644 --- a/src/widgets/desktop-widget.h +++ b/src/widgets/desktop-widget.h @@ -78,7 +78,7 @@ struct SPDesktopWidget { GtkWidget *hbox; - GtkWidget *menubar, *statusbar; + GtkWidget *menubar, *statusbar, *rotatebar; Inkscape::UI::Dialogs::SwatchesPanel *panels; @@ -97,7 +97,9 @@ struct SPDesktopWidget { GtkWidget *select_status; GtkWidget *select_status_eventbox; GtkWidget *zoom_status; + GtkWidget *rotation_status; gulong zoom_update; + gulong rotation_update; Inkscape::UI::Widget::Dock *dock; diff --git a/src/widgets/gimp/ruler.cpp b/src/widgets/gimp/ruler.cpp index 2a71b5c08..6a1f7f903 100644 --- a/src/widgets/gimp/ruler.cpp +++ b/src/widgets/gimp/ruler.cpp @@ -34,11 +34,10 @@ #include <cstdio> #include "ruler.h" -#include "round.h" #include <glibmm/i18n.h> #include "util/units.h" -#define ROUND(x) ((int) ((x) + 0.5)) +#define ROUND(x) ((int) round(x)) #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB @@ -1217,7 +1216,7 @@ sp_ruler_draw_ticks (SPRuler *ruler) // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h) // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-)) - pos = gint(Inkscape::round((cur - lower) * increment + 1e-12)); + pos = gint(round((cur - lower) * increment + 1e-12)); if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp index 1efa8f06b..885deaab7 100644 --- a/src/widgets/icon.cpp +++ b/src/widgets/icon.cpp @@ -35,6 +35,7 @@ #include "display/drawing.h" #include "io/sys.h" #include "sp-root.h" +#include "sp-namedview.h" #include "util/units.h" #include "icon.h" @@ -1163,6 +1164,19 @@ sp_icon_doc_icon( SPDocument *doc, Inkscape::Drawing &drawing, CAIRO_FORMAT_ARGB32, psize, psize, stride); Inkscape::DrawingContext dc(s, ua.min()); + SPNamedView *nv = sp_document_namedview(doc, NULL); + float bg_r = SP_RGBA32_R_F(nv->pagecolor); + float bg_g = SP_RGBA32_G_F(nv->pagecolor); + float bg_b = SP_RGBA32_B_F(nv->pagecolor); + float bg_a = SP_RGBA32_A_F(nv->pagecolor); + + cairo_t *cr = cairo_create(s); + cairo_set_source_rgba(cr, bg_r, bg_g, bg_b, bg_a); + cairo_rectangle(cr, 0, 0, psize, psize); + cairo_fill(cr); + cairo_save(cr); + cairo_destroy(cr); + drawing.render(dc, ua); cairo_surface_destroy(s); diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index ba79517d2..24665be37 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -301,10 +301,23 @@ static void sp_text_fontstyle_value_changed( Ink_ComboBoxEntry_Action *act, GObj SPDesktop *desktop = SP_ACTIVE_DESKTOP; sp_desktop_set_style (desktop, css, true, true); + + + // If no selected objects, set default. + SPStyle query(SP_ACTIVE_DOCUMENT); + int result_style = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTSTYLE); + if (result_style == QUERY_STYLE_NOTHING) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); + } else { + // Save for undo + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Change font style")); + } + sp_repr_css_attr_unref (css); - DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, - _("Text: Change font style")); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -588,17 +601,14 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl ) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - // Only save if not relative unit - if ( !is_relative(unit) ) { - // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit so - // we can save it (allows us to adjust line height value when unit changes). - SPILength temp_length; - Inkscape::CSSOStringStream temp_stream; - temp_stream << 1 << unit->abbr; - temp_length.read(temp_stream.str().c_str()); - prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); - g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); - } + // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit so + // we can save it (allows us to adjust line height value when unit changes). + SPILength temp_length; + Inkscape::CSSOStringStream temp_stream; + temp_stream << 1 << unit->abbr; + temp_length.read(temp_stream.str().c_str()); + prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); + g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); // Set css line height. SPCSSAttr *css = sp_repr_css_attr_new (); @@ -701,16 +711,13 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl ) g_return_if_fail(unit != NULL); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - // Only save if not relative unit - if ( !is_relative(unit) ) { - // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit. - SPILength temp_length; - Inkscape::CSSOStringStream temp_stream; - temp_stream << 1 << unit->abbr; - temp_length.read(temp_stream.str().c_str()); - prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); - g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); - } + // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit. + SPILength temp_length; + Inkscape::CSSOStringStream temp_stream; + temp_stream << 1 << unit->abbr; + temp_length.read(temp_stream.str().c_str()); + prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); + g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); // Read current line height value EgeAdjustmentAction *line_height_act = @@ -723,7 +730,10 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl ) auto itemlist = selection->items(); // Convert between units - if ((unit->abbr == "" || unit->abbr == "em") && old_unit == SP_CSS_UNIT_EX) { + if ((unit->abbr == "" || unit->abbr == "em") && + (old_unit == SP_CSS_UNIT_NONE || old_unit == SP_CSS_UNIT_EM)) { + // Do nothing + } else if ((unit->abbr == "" || unit->abbr == "em") && old_unit == SP_CSS_UNIT_EX) { line_height *= 0.5; } else if ((unit->abbr) == "ex" && (old_unit == SP_CSS_UNIT_EM || old_unit == SP_CSS_UNIT_NONE) ) { line_height *= 2.0; @@ -1130,6 +1140,54 @@ static void sp_text_orientation_changed( EgeSelectOneAction *act, GObject *tbl ) g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } +static void sp_text_direction_changed( EgeSelectOneAction *act, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + int mode = ege_select_one_action_get_active( act ); + + SPCSSAttr *css = sp_repr_css_attr_new (); + switch (mode) + { + case 0: + { + sp_repr_css_set_property (css, "direction", "ltr"); + break; + } + + case 1: + { + sp_repr_css_set_property (css, "direction", "rtl"); + break; + } + } + + SPStyle query(SP_ACTIVE_DOCUMENT); + int result_numbers = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + + // If querying returned nothing, update default style. + if (result_numbers == QUERY_STYLE_NOTHING) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); + } + + sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true); + if(result_numbers != QUERY_STYLE_NOTHING) + { + DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Change direction")); + } + sp_repr_css_attr_unref (css); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + /* * Set the default list of font sizes, scaled to the users preferred unit */ @@ -1170,8 +1228,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; std::cout << "sp_text_toolbox_selection_changed: start " << count << std::endl; - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::Selection *selection = (SP_ACTIVE_DESKTOP)->getSelection(); auto itemlist0= selection->items(); for(auto i=itemlist0.begin();i!=itemlist0.end(); ++i) { const gchar* id = (*i)->getId(); @@ -1494,6 +1551,15 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ ege_select_one_action_update_sensitive( textOrientationAction ); + // Direction + int activeButton4 = 0; + if (query.direction.computed == SP_CSS_DIRECTION_LTR ) activeButton4 = 0; + if (query.direction.computed == SP_CSS_DIRECTION_RTL ) activeButton4 = 1; + + EgeSelectOneAction* textDirectionAction = + EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextDirectionAction" ) ); + ege_select_one_action_set_active( textDirectionAction, activeButton4 ); + } #ifdef DEBUG_TEXT @@ -1926,6 +1992,52 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder ); } + + // Text direction (predominant direction of horizontal text). + { + GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN ); + + GtkTreeIter iter; + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("LTR"), + 1, _("Left to right text"), + 2, INKSCAPE_ICON("format-text-direction-horizontal"), + 3, true, + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("RTL"), + 1, _("Right to left text"), + 2, INKSCAPE_ICON("format-text-direction-r2l"), + 3, true, + -1 ); + + EgeSelectOneAction* act = ege_select_one_action_new( "TextDirectionAction", // Name + _("Text direction"), // Label + _("Text direction for normally horizontal text."), // Tooltip + NULL, // Icon name + GTK_TREE_MODEL(model) ); // Model + + g_object_set( act, "short_label", "NotUsed", NULL ); + gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); + g_object_set_data( holder, "TextDirectAction", act ); + + ege_select_one_action_set_appearance( act, "full" ); + ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); + g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL ); + ege_select_one_action_set_icon_column( act, 2 ); + ege_select_one_action_set_icon_size( act, secondarySize ); + ege_select_one_action_set_tooltip_column( act, 1 ); + ege_select_one_action_set_sensitive_column( act, 3 ); + + gint mode = prefs->getInt("/tools/text/text_direction", 0); + ege_select_one_action_set_active( act, mode ); + g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_direction_changed), holder ); + } + /* Line height unit tracker */ UnitTracker* tracker = new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR); tracker->prependUnit(unit_table.getUnit("")); // No unit diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 126eac9cd..016eafdaa 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -204,6 +204,7 @@ static struct { SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")}, { "/tools/lpetool", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar", SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")}, + // If you change TextToolbar here, change it also in desktop-widget.cpp { "/tools/text", "text_toolbox", 0, sp_text_toolbox_prep, "TextToolbar", SP_VERB_INVALID, 0, 0}, { "/tools/dropper", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar", @@ -529,6 +530,8 @@ static gchar const * ui_descr = " <toolitem action='TextWritingModeAction' />" " <separator />" " <toolitem action='TextOrientationAction' />" + " <separator />" + " <toolitem action='TextDirectionAction' />" " </toolbar>" " <toolbar name='LPEToolToolbar'>" @@ -1428,7 +1431,7 @@ void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop) auto kludge = dataHolders[aux_toolboxes[i].type_name]; auto holder = gtk_grid_new(); - gtk_widget_set_name( holder, "ToolbarHolder" ); + gtk_widget_set_name( holder, aux_toolboxes[i].ui_name ); gtk_grid_attach( GTK_GRID(holder), kludge, 2, 0, 1, 1); gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name ); GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp ); diff --git a/src/widgets/widget-sizes.h b/src/widgets/widget-sizes.h index 87c7ca2e0..186cf4730 100644 --- a/src/widgets/widget-sizes.h +++ b/src/widgets/widget-sizes.h @@ -28,6 +28,7 @@ #define STATUS_BAR_FONT_SIZE 10000 #define STATUS_ZOOM_WIDTH 57 +#define STATUS_ROTATION_WIDTH 57 #define SELECTED_STYLE_SB_WIDTH 48 #define SELECTED_STYLE_WIDTH 190 diff --git a/src/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp index 7e3d4fa7e..a34df09a5 100644 --- a/src/xml/rebase-hrefs.cpp +++ b/src/xml/rebase-hrefs.cpp @@ -276,7 +276,7 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b #ifdef WIN32 /* Native Windows path separators are replaced with / so that the href * also works on Gnu/Linux and OSX */ - std::replace(href.begin(), href.end(), '\\', '/'); + std::replace(new_href.begin(), new_href.end(), '\\', '/'); #endif ir->setAttribute("xlink:href", new_href.c_str()); } else { |
