diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2017-01-25 09:46:27 +0000 |
|---|---|---|
| committer | jabiertxof <info@marker.es> | 2017-01-25 09:46:27 +0000 |
| commit | 59ed5cf5fae750ea136fe8f5a37df23b541f80d5 (patch) | |
| tree | 08fd8c0ac7d70c22fe8e2c1edc960235e2710010 /src | |
| parent | Update to trunk (diff) | |
| parent | Comented one of the shortcuts for doc rotate because bad response now interac... (diff) | |
| download | inkscape-59ed5cf5fae750ea136fe8f5a37df23b541f80d5.tar.gz inkscape-59ed5cf5fae750ea136fe8f5a37df23b541f80d5.zip | |
update to trunk
(bzr r13645.1.168)
Diffstat (limited to 'src')
58 files changed, 3010 insertions, 655 deletions
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp index d469d3ffc..cac010942 100644 --- a/src/2geom/intersection-graph.cpp +++ b/src/2geom/intersection-graph.cpp @@ -410,10 +410,10 @@ PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b) assert(!result.back().empty()); } - /*if (n_processed != size() * 2) { + if (n_processed != size() * 2) { std::cerr << "Processed " << n_processed << " intersections, expected " << (size() * 2) << std::endl; - }*/ - assert(n_processed == size() * 2); + } + //assert(n_processed == size() * 2); return result; } diff --git a/src/attributes.cpp b/src/attributes.cpp index 02b9450b0..8154bfbbe 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -89,6 +89,7 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_ZOOM, "inkscape:zoom"}, {SP_ATTR_INKSCAPE_CX, "inkscape:cx"}, {SP_ATTR_INKSCAPE_CY, "inkscape:cy"}, + {SP_ATTR_INKSCAPE_DOCUMENT_ROTATION, "inkscape:document-rotation"}, {SP_ATTR_INKSCAPE_WINDOW_WIDTH, "inkscape:window-width"}, {SP_ATTR_INKSCAPE_WINDOW_HEIGHT, "inkscape:window-height"}, {SP_ATTR_INKSCAPE_WINDOW_X, "inkscape:window-x"}, diff --git a/src/attributes.h b/src/attributes.h index 12adcf8e2..e21851bcf 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -97,6 +97,7 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_ZOOM, SP_ATTR_INKSCAPE_CX, SP_ATTR_INKSCAPE_CY, + SP_ATTR_INKSCAPE_DOCUMENT_ROTATION, SP_ATTR_INKSCAPE_WINDOW_WIDTH, SP_ATTR_INKSCAPE_WINDOW_HEIGHT, SP_ATTR_INKSCAPE_WINDOW_X, diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index 5fef8cbfc..cb3e1f7c8 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -155,6 +155,10 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge } } + SPNamedView *namedview = desktop->namedview; + if (namedview && namedview->document_rotation) { + normal *= Geom::Rotate(Geom::rad_from_deg(namedview->document_rotation * -1)); + } guide = sp_guideline_new(desktop->guides, NULL, event_dt, normal); sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor); diff --git a/src/display/canvas-temporary-item-list.h b/src/display/canvas-temporary-item-list.h index 471bb99b9..6b0e5796b 100644 --- a/src/display/canvas-temporary-item-list.h +++ b/src/display/canvas-temporary-item-list.h @@ -14,6 +14,7 @@ struct SPCanvasItem; class SPDesktop; +class SPViewBox; namespace Inkscape { namespace Display { diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 416c2c804..68eae0a65 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -29,18 +29,24 @@ #include "helper/sp-marshal.h" #include <2geom/rect.h> #include <2geom/affine.h> -#include "display/cairo-utils.h" #include "display/sp-canvas.h" #include "display/sp-canvas-group.h" +#include "display/rendermode.h" +#include "display/cairo-utils.h" +#include "display/cairo-templates.h" +#include "display/drawing-context.h" +#include "display/drawing-item.h" +#include "display/nr-filter-colormatrix.h" +#include "display/canvas-arena.h" #include "preferences.h" #include "inkscape.h" #include "sodipodi-ctrlrect.h" #include "cms-system.h" -#include "display/rendermode.h" -#include "display/cairo-utils.h" #include "debug/gdk-event-latency-tracker.h" #include "desktop.h" #include "color.h" +#include <iomanip> +#include <glibmm/i18n.h> using Inkscape::Debug::GdkEventLatencyTracker; @@ -1888,6 +1894,19 @@ SPCanvasGroup *SPCanvas::getRoot() return SP_CANVAS_GROUP(_root); } +gdouble grayscale_value_matrix[20] = { + 0.21, 0.72, 0.072, 0, 0, + 0.21, 0.72, 0.072, 0, 0, + 0.21, 0.72, 0.072, 0, 0, + 0 , 0 , 0 , 1, 0 + }; +cairo_surface_t *surface_rotated; +cairo_surface_t *surface_origin; +cairo_surface_t *surface_measure; +double start_angle = 0; +bool started = false; +bool rotated = false; + void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling) { GtkAllocation allocation; @@ -1910,9 +1929,14 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // Paint the background cairo_translate(cr, -ix, -iy); + if (rotated) { + cairo_translate(cr, dx, dy); + rotated = false; + } cairo_set_source(cr, _background); cairo_paint(cr); // Copy the old backing store contents + cairo_set_source_surface(cr, _backing_store, _x0, _y0); cairo_rectangle(cr, _x0, _y0, allocation.width, allocation.height); cairo_clip(cr); @@ -1949,6 +1973,271 @@ void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scroll addIdle(); } +void SPCanvas::startRotateTo(double angle) +{ + if (!_backing_store || started) { + return; + } + start_angle = angle; + started = true; + GtkAllocation allocation; + gtk_widget_get_allocation(&_widget, &allocation); + int half_w = allocation.width/2; + int half_h = allocation.height/2; + int half_min = std::min(half_w,half_h); + + cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cairo_t *cr = cairo_create(new_backing_store); + cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI); + cairo_fill(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_IN); + cairo_set_source_surface(cr, _backing_store, 0, 0); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_arc(cr, half_w, half_h, half_min-16, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 0.5); + cairo_stroke(cr); + cairo_destroy(cr); + surface_rotated = new_backing_store; + + cairo_surface_t *new_backing_store_measure = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cr = cairo_create(new_backing_store_measure); + cairo_arc(cr, half_w, half_h, half_min-15, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 0.2); + cairo_fill(cr); + cairo_translate(cr, half_w, half_h); + cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 10.0); + for (gint x = 0; x < 360 ; x++){ + gint ang = 360 - x ;//+ 90; + if (ang > 180) { + ang -= 360; + } + double rot = (-180.0 + x)*(M_PI/180.); + double dist = half_min-9; + gint inverse = 1; + if((x) < 91 || (x) > 270) { + inverse = -1; + } + if(x%10 == 0) { + cairo_rotate(cr, -rot); + cairo_text_extents_t extents; + std::string s = std::to_string(ang) + "º"; + cairo_text_extents(cr, s.c_str(), &extents); + //std::cout << extents.width/2 << "extents.x_bearing\n"; + cairo_translate(cr, (extents.width/2) * inverse * -1, (dist + ((extents.height/2)* inverse))); + if((x) < 91 || (x) > 270) { + cairo_rotate(cr, 180*(M_PI/180.0)); + } + cairo_text_path(cr, s.c_str()); + if((x) < 91 || (x) > 270) { + cairo_rotate(cr, -180*(M_PI/180.0)); + } + cairo_translate(cr, (extents.width/2) * inverse , (dist + ((extents.height/2)* inverse)) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_fill(cr); + cairo_rotate(cr, rot); + } + cairo_rotate(cr, x*(M_PI/180.)); + if(x%5 == 0) { + cairo_move_to(cr, 0, half_min-30); + cairo_line_to(cr, 0, half_min-17); + } else { + cairo_move_to(cr, 0, half_min-20); + cairo_line_to(cr, 0, half_min-15); + } + cairo_line_to(cr, 0, half_min-15); + cairo_set_source_rgba (cr, 0, 0, 0, 0.4); + cairo_set_line_width (cr,1); + cairo_stroke(cr); + cairo_rotate(cr, -x*(M_PI/180.)); + } + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_translate(cr, -half_w, -half_h); + cairo_arc(cr, half_w, half_h, half_min-30, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_fill(cr); + cairo_translate(cr, half_w, half_h); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_rotate(cr, start_angle*(M_PI/180.)); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.25); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr,5); + cairo_stroke(cr); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 0, 0, 0.9); + cairo_set_line_width (cr,1); + cairo_stroke(cr); + cairo_rotate(cr, -start_angle*(M_PI/180.)); + cairo_destroy(cr); + surface_measure = new_backing_store_measure; + + cairo_surface_t *new_backing_store_grey = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cr = cairo_create(new_backing_store_grey); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, _backing_store, 0, 0); + cairo_paint(cr); + Inkscape::Filters::FilterColorMatrix::ColorMatrixMatrix _grayscale_colormatrix = std::vector<gdouble> (grayscale_value_matrix, grayscale_value_matrix + 20); + cairo_surface_t *out = ink_cairo_surface_create_identical(new_backing_store_grey); + ink_cairo_surface_filter(new_backing_store_grey, out, _grayscale_colormatrix); + cairo_set_source_surface(cr, out, 0, 0); + cairo_surface_destroy(out); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_destroy(cr); + surface_origin = new_backing_store_grey; +} + +bool SPCanvas::endRotateTo() +{ + if (!_backing_store || !started) { + return false; + } + started = false; + surface_rotated = NULL; + surface_origin = NULL; + gtk_widget_queue_draw(GTK_WIDGET(this)); + dirtyAll(); + addIdle(); + rotated = true; + return true; +} + +void SPCanvas::clearRotateTo() +{ + if (!started) { + return; + } + gtk_widget_queue_draw(GTK_WIDGET(this)); + dirtyAll(); + addIdle(); +} + +void SPCanvas::rotateTo(double angle) +{ + if (!_backing_store || !started) { + return; + } + GtkAllocation allocation; + gtk_widget_get_allocation(&_widget, &allocation); + int half_w = allocation.width/2; + int half_h = allocation.height/2; + int half_min = std::min(half_w,half_h); + cairo_surface_t *new_backing_store = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + cairo_t *cr = cairo_create(new_backing_store); + cairo_set_source_surface(cr, surface_origin, 0, 0); + cairo_paint(cr); + cairo_set_source_rgba (cr, 0, 0, 0, 0.5); + cairo_paint(cr); + cairo_pattern_t *source_pattern; + cairo_matrix_t matrix; + source_pattern = cairo_pattern_create_for_surface (surface_rotated); + cairo_matrix_init_identity (&matrix); + cairo_matrix_translate (&matrix, allocation.width/2.0, allocation.height/2.0); + cairo_matrix_rotate (&matrix, Geom::rad_from_deg(angle - start_angle) * -1); + cairo_matrix_translate (&matrix, -allocation.width/2.0, -allocation.height/2.0); + cairo_pattern_set_matrix (source_pattern, &matrix); + cairo_set_source(cr, source_pattern); + cairo_paint(cr); + cairo_set_source_surface(cr, surface_measure, 0, 0); + cairo_paint(cr); + cairo_translate(cr, half_w, half_h); + cairo_rotate(cr, angle*(M_PI/180.)); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.25); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_width (cr,5); + cairo_stroke(cr); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.9); + cairo_set_line_width (cr,1); + cairo_stroke(cr); + cairo_move_to(cr, 0, 0); + cairo_line_to(cr, 0, (half_min-17) * -1); + cairo_set_source_rgba (cr, 1, 0, 0, 0.9); + const double dashed[] = {6.0, 3.0}; + int len = sizeof(dashed) / sizeof(dashed[0]); + cairo_set_dash(cr, dashed, len, 1); + cairo_stroke(cr); + cairo_translate(cr, -half_w, -half_h); + cairo_set_source_rgba (cr, 1, 1, 1, 0.25); + cairo_arc(cr, half_w, half_h, 7, 0, 2*M_PI); + cairo_fill(cr); + cairo_set_source_rgba (cr, 1, 0, 0, 0.7); + cairo_arc(cr, half_w, half_h, 5, 0, 2*M_PI); + cairo_fill(cr); + cairo_translate(cr, half_w, half_h); + cairo_rotate(cr, -angle*(M_PI/180.)); + cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cr, 15.0); + cairo_text_extents_t extents; + std::ostringstream s; + s << _("Original angle ") << std::fixed << std::setprecision(2) << start_angle << "º"; + cairo_text_extents(cr, s.str().c_str(), &extents); + cairo_translate(cr, half_w - extents.width -15 ,-half_h + 25); + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 25) *-1); + s.str(""); + s << _("New angle ") << std::fixed << std::setprecision(2) << angle << "º"; + cairo_text_extents(cr, s.str().c_str(), &extents); + cairo_translate(cr, half_w - extents.width -15 ,-half_h + 45); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 45) *-1); + s.str(""); + s << _("Gap ") << std::fixed << std::setprecision(2) << std::abs(start_angle-angle) << "º"; + cairo_text_extents(cr, s.str().c_str(), &extents); + cairo_translate(cr, half_w - extents.width -15 ,-half_h + 65); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (half_w - extents.width -15) *-1 ,(-half_h + 65) *-1); + cairo_translate(cr, -half_w + 10 ,-half_h + 25); + s.str(""); + cairo_set_font_size(cr, 12.0); + s << _("Normal mode, 1º round step"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w +10) * -1 ,(-half_h + 25) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 40); + s.str(""); + s << _("+ALT, Fractional degrees"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 40) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 55); + s.str(""); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + s << _("+CTRL, ") << 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12) << _("º round step"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 55) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 70); + s.str(""); + s << _("+SHIFT, Reset"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 70) * -1); + cairo_translate(cr, -half_w + 10 ,-half_h + 85); + s.str(""); + s << _("+CTRL+SHIFT, 0º"); + cairo_text_path(cr, s.str().c_str()); + cairo_fill(cr); + //cairo_translate(cr, (-half_w + 10) * -1 ,(-half_h + 60) * -1); + cairo_destroy(cr); + cairo_surface_destroy(_backing_store); + _backing_store = new_backing_store; + cairo_pattern_destroy (source_pattern); + gtk_widget_queue_draw(GTK_WIDGET(this)); + addIdle(); +} + void SPCanvas::updateNow() { if (_need_update) { diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 78d96d728..21b6760f2 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -72,7 +72,10 @@ GType sp_canvas_get_type() G_GNUC_CONST; struct SPCanvas { /// Scrolls canvas to specific position (cx and cy are measured in screen pixels). void scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling = false); - + void startRotateTo(double angle); + void rotateTo(double angle); + bool endRotateTo(); + void clearRotateTo(); /// Synchronously updates the canvas if necessary. void updateNow(); diff --git a/src/document-undo.cpp b/src/document-undo.cpp index 113d09d66..ae90c916e 100644 --- a/src/document-undo.cpp +++ b/src/document-undo.cpp @@ -240,49 +240,52 @@ static void perform_document_update(SPDocument &doc) { gboolean Inkscape::DocumentUndo::undo(SPDocument *doc) { - using Inkscape::Debug::EventTracker; - using Inkscape::Debug::SimpleEvent; + using Inkscape::Debug::EventTracker; + using Inkscape::Debug::SimpleEvent; - gboolean ret; + gboolean ret; - EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo"); + EventTracker<SimpleEvent<Inkscape::Debug::Event::DOCUMENT> > tracker("undo"); - g_assert (doc != NULL); - g_assert (doc->priv != NULL); - g_assert (doc->priv->sensitive); + g_assert (doc != NULL); + g_assert (doc->priv != NULL); + g_assert (doc->priv->sensitive); - doc->priv->sensitive = FALSE; + doc->priv->sensitive = FALSE; doc->priv->seeking = true; - doc->actionkey.clear(); + doc->actionkey.clear(); - finish_incomplete_transaction(*doc); + finish_incomplete_transaction(*doc); - if (! doc->priv->undo.empty()) { - Inkscape::Event *log = doc->priv->undo.back(); - doc->priv->undo.pop_back(); - sp_repr_undo_log (log->event); - perform_document_update(*doc); + if (! doc->priv->undo.empty()) { + Inkscape::Event *log = doc->priv->undo.back(); + doc->priv->undo.pop_back(); + sp_repr_undo_log (log->event); + perform_document_update(*doc); - doc->priv->redo.push_back(log); + doc->priv->redo.push_back(log); doc->setModifiedSinceSave(); doc->priv->undoStackObservers.notifyUndoEvent(log); - ret = TRUE; - } else { - ret = FALSE; - } + ret = TRUE; + } else { + ret = FALSE; + } - sp_repr_begin_transaction (doc->rdoc); + sp_repr_begin_transaction (doc->rdoc); - doc->priv->sensitive = TRUE; + doc->priv->sensitive = TRUE; doc->priv->seeking = false; - if (ret) - INKSCAPE.external_change(); + if (ret) INKSCAPE.external_change(); - return ret; + SPObject *updated = doc->getRoot(); + if (updated) { + updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + return ret; } gboolean Inkscape::DocumentUndo::redo(SPDocument *doc) diff --git a/src/document.cpp b/src/document.cpp index 0c77a8f48..8072ec53d 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -386,6 +386,7 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, if (!bordercolor.empty()) { rnew->setAttribute("bordercolor", bordercolor.data()); } + sp_repr_set_svg_double(rnew, "inkscape:document-rotation", 0.); sp_repr_set_svg_double(rnew, "borderopacity", prefs->getDouble("/template/base/borderopacity", 1.0)); sp_repr_set_svg_double(rnew, "objecttolerance", @@ -407,6 +408,11 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, rroot->addChild(rnew, NULL); // clean up Inkscape::GC::release(rnew); + } else { + Inkscape::XML::Node *nv_repr = sp_item_group_get_child_by_name(document->root, NULL, "sodipodi:namedview")->getRepr(); + if (!nv_repr->attribute("inkscape:document-rotation")) { + sp_repr_set_svg_double(nv_repr, "inkscape:document-rotation", 0.); + } } // Defs diff --git a/src/extension/internal/cairo-png-out.cpp b/src/extension/internal/cairo-png-out.cpp index 956fcce9a..5859a82da 100644 --- a/src/extension/internal/cairo-png-out.cpp +++ b/src/extension/internal/cairo-png-out.cpp @@ -53,11 +53,10 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename) { CairoRenderer *renderer; CairoRenderContext *ctx; - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); /* Start */ - SPItem *base = doc->getRoot(); Inkscape::Drawing drawing; unsigned dkey = SPItem::display_key_new(1); @@ -77,6 +76,7 @@ png_render_document_to_file(SPDocument *doc, gchar const *filename) renderer->destroyContext(ctx); base->invoke_hide(dkey); + doc->getRoot()->c2p *= doc->getRoot()->rotation; /* end */ delete renderer; diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp index e8f47e79e..809125266 100644 --- a/src/extension/internal/cairo-ps-out.cpp +++ b/src/extension/internal/cairo-ps-out.cpp @@ -68,6 +68,7 @@ static bool ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool omittext, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px, bool eps = false) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); SPItem *base = NULL; @@ -84,9 +85,10 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l pageBoundingBox = !exportDrawing; } - if (!base) + if (!base) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; - + } Inkscape::Drawing drawing; unsigned dkey = SPItem::display_key_new(1); base->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY); @@ -115,6 +117,7 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l renderer->destroyContext(ctx); delete renderer; + doc->getRoot()->c2p *= doc->getRoot()->rotation; return ret; } diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp index 5576676b2..5558fe1ad 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -61,6 +61,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int bool texttopath, bool omittext, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, float bleedmargin_px) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); /* Start */ @@ -80,6 +81,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int } if (!base) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } @@ -112,7 +114,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int renderer->destroyContext(ctx); delete renderer; - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return ret; } diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index 12751c5ec..198c18ff2 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -94,7 +94,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename) const gchar *oldconst; gchar *oldoutput; unsigned int ret; - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); mod = Inkscape::Extension::get_print(PRINT_EMF); @@ -114,6 +114,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename) /* Print document */ ret = mod->begin(doc); if (ret) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; g_free(oldoutput); throw Inkscape::Extension::Output::save_failed(); } @@ -127,7 +128,7 @@ Emf::print_document_to_file(SPDocument *doc, const gchar *filename) mod->set_param_string("destination", oldoutput); g_free(oldoutput); - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp index d7ad7e6f7..c95434939 100644 --- a/src/extension/internal/javafx-out.cpp +++ b/src/extension/internal/javafx-out.cpp @@ -843,7 +843,8 @@ void JavaFXOutput::reset() bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) { reset(); - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); name = Glib::path_get_basename(filename_utf8); int pos = name.find('.'); @@ -856,12 +857,14 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) //# Lets do the curves first, to get the stats if (!doTree(doc)) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } String curveBuf = outbuf; outbuf.clear(); if (!doHeader()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } @@ -875,6 +878,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) doBody(doc, doc->getRoot()); if (!doTail()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return false; } @@ -884,6 +888,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w"); if (!f) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could open JavaFX file '%s' for writing", filename_utf8); return false; } @@ -894,7 +899,7 @@ bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) } fclose(f); - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return true; } diff --git a/src/extension/internal/latex-pstricks-out.cpp b/src/extension/internal/latex-pstricks-out.cpp index 3ce2c5531..aa6ea6963 100644 --- a/src/extension/internal/latex-pstricks-out.cpp +++ b/src/extension/internal/latex-pstricks-out.cpp @@ -49,6 +49,7 @@ bool LatexOutput::check(Inkscape::Extension::Extension * /*module*/) void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc, gchar const *filename) { SPPrintContext context; + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); Inkscape::Extension::Print *mod = Inkscape::Extension::get_print(SP_MODULE_KEY_PRINT_LATEX); @@ -76,6 +77,7 @@ void LatexOutput::save(Inkscape::Extension::Output * /*mod2*/, SPDocument *doc, mod->set_param_string("destination", oldoutput); g_free(oldoutput); + doc->getRoot()->c2p *= doc->getRoot()->rotation; } #include "clear-n_.h" diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp index f885ef5e5..66d370357 100644 --- a/src/extension/internal/odf.cpp +++ b/src/extension/internal/odf.cpp @@ -72,6 +72,7 @@ #include "sp-path.h" #include "sp-text.h" #include "sp-flowtext.h" +#include "sp-root.h" #include "svg/svg.h" #include "text-editing.h" #include "util/units.h" @@ -2095,7 +2096,8 @@ void OdfOutput::reset() void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename) { reset(); - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); documentUri = Inkscape::URI(filename); ZipFile zf; @@ -2104,25 +2106,30 @@ void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gcha if (!writeManifest(zf)) { g_warning("Failed to write manifest"); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } if (!writeContent(zf, doc->rroot)) { g_warning("Failed to write content"); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } if (!writeMeta(zf)) { g_warning("Failed to write metafile"); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } if (!zf.writeFile(filename)) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } + doc->getRoot()->c2p *= doc->getRoot()->rotation; } diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp index 8df883069..03a2ecd62 100644 --- a/src/extension/internal/pov-out.cpp +++ b/src/extension/internal/pov-out.cpp @@ -616,11 +616,13 @@ void PovOutput::reset() void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) { reset(); - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); //###### SAVE IN POV FORMAT TO BUFFER //# Lets do the curves first, to get the stats if (!doTree(doc)) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could not output curves for %s", filename_utf8); return; } @@ -630,6 +632,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) if (!doHeader()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could not write header for %s", filename_utf8); return; } @@ -638,6 +641,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) if (!doTail()) { + doc->getRoot()->c2p *= doc->getRoot()->rotation; err("Could not write footer for %s", filename_utf8); return; } @@ -648,9 +652,11 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) //###### WRITE TO FILE Inkscape::IO::dump_fopen_call(filename_utf8, "L"); FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w"); - if (!f) + if (!f){ + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; - + } + for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); ++iter) { int ch = *iter; @@ -658,6 +664,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) } fclose(f); + doc->getRoot()->c2p *= doc->getRoot()->rotation; } diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index c7226a58a..a79af5ec1 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -95,7 +95,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) SPPrintContext context; const gchar *oldconst; gchar *oldoutput; - + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); mod = Inkscape::Extension::get_print(PRINT_WMF); @@ -115,6 +115,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) /* Print document */ if (mod->begin(doc)) { g_free(oldoutput); + doc->getRoot()->c2p *= doc->getRoot()->rotation; throw Inkscape::Extension::Output::save_failed(); } mod->base->invoke_print(&context); @@ -127,7 +128,7 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) mod->set_param_string("destination", oldoutput); g_free(oldoutput); - + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -135,6 +136,8 @@ Wmf::print_document_to_file(SPDocument *doc, const gchar *filename) void Wmf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); Inkscape::Extension::Extension * ext; ext = Inkscape::Extension::db.get(PRINT_WMF); diff --git a/src/file.cpp b/src/file.cpp index e8248bb8e..6613b1e65 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -293,10 +293,12 @@ bool sp_file_open(const Glib::ustring &uri, bool replace_empty) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Inkscape::Display::TemporaryItem *page_border_rotated = NULL; if (desktop) { desktop->setWaitingCursor(); + page_border_rotated = sp_document_namedview(desktop->getDocument(), NULL)->page_border_rotated; } - + SPDocument *doc = NULL; bool cancelled = false; try { @@ -315,7 +317,6 @@ bool sp_file_open(const Glib::ustring &uri, } if (doc) { - SPDocument *existing = desktop ? desktop->getDocument() : NULL; if (existing && existing->virgin && replace_empty) { @@ -323,6 +324,7 @@ bool sp_file_open(const Glib::ustring &uri, doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc. desktop->change_document(doc); doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px")); + desktop->remove_temporary_canvasitem(page_border_rotated); } else { // create a whole new desktop and window SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc. diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp index 42c494c00..e1f05c3ce 100644 --- a/src/helper/geom.cpp +++ b/src/helper/geom.cpp @@ -843,6 +843,13 @@ recursive_bezier4(const double x1, const double y1, recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1); } +void +swap(Geom::Point &A, Geom::Point &B){ + Geom::Point tmp = A; + A = B; + B = tmp; +} + /* Local Variables: mode:c++ diff --git a/src/helper/geom.h b/src/helper/geom.h index d49e2070c..b3d907e51 100644 --- a/src/helper/geom.h +++ b/src/helper/geom.h @@ -32,7 +32,7 @@ void recursive_bezier4(const double x1, const double y1, const double x2, const const double x3, const double y3, const double x4, const double y4, std::vector<Geom::Point> &pointlist, int level); - +void swap(Geom::Point &A, Geom::Point &B); #endif // INKSCAPE_HELPER_GEOM_H /* diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 59ff06a4e..f67c43db0 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -59,6 +59,9 @@ set(live_effects_SRC parameter/array.cpp parameter/bool.cpp + parameter/item-reference.cpp + parameter/item.cpp + parameter/originalitem.cpp parameter/originalpath.cpp parameter/originalpatharray.cpp parameter/parameter.cpp @@ -141,6 +144,9 @@ set(live_effects_SRC parameter/array.h parameter/bool.h parameter/enum.h + parameter/item.h + parameter/item-reference.h + parameter/originalitem.h parameter/originalpath.h parameter/originalpatharray.h parameter/parameter.h diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 19e80c611..a02f62ac9 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -70,6 +70,8 @@ #include "ui/tools/node-tool.h" #include "ui/tools-switch.h" #include "knotholder.h" +#include "path-chemistry.h" +#include "xml/sp-css-attr.h" #include "live_effects/lpeobject.h" #include "display/curve.h" @@ -115,7 +117,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { {RULER, N_("Ruler"), "ruler"}, /* 0.91 */ {POWERSTROKE, N_("Power stroke"), "powerstroke"}, - {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"}, + {CLONE_ORIGINAL, N_("Clone original"), "clone_original"}, /* 0.92 */ {SIMPLIFY, N_("Simplify"), "simplify"}, {LATTICE2, N_("Lattice Deformation 2"), "lattice2"}, @@ -356,6 +358,7 @@ Effect::Effect(LivePathEffectObject *lpeobject) sp_lpe_item(NULL), current_zoom(1), upd_params(true), + sp_shape(NULL), sp_curve(NULL), provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden @@ -392,12 +395,19 @@ Effect::doOnApply (SPLPEItem const*/*lpeitem*/) } void +Effect::setCurrentZoom(double cZ) +{ + current_zoom = cZ; +} + +void Effect::setSelectedNodePos(std::vector<Geom::Point> selected_nodes_pos_data) { selected_nodes_pos = selected_nodes_pos_data; } void + Effect::setSelectedNodeIndex(Geom::PathVector pv) { selected_nodes_index.clear(); @@ -435,12 +445,6 @@ Effect::setSelectedNodeIndex(Geom::PathVector pv) } } -void -Effect::setCurrentZoom(double cZ) -{ - current_zoom = cZ; -} - bool Effect::isNodeSelected(Geom::Point const &nodePoint) const { @@ -461,6 +465,74 @@ Effect::isNodeSelected(Geom::Point const &nodePoint) const return false; } +void +Effect::processObjects(LpeAction lpe_action) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + for (std::vector<const char *>::iterator el_it = items.begin(); + el_it != items.end(); ++el_it) { + const char * id = *el_it; + if (!id || strlen(id) == 0) { + return; + } + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + Inkscape::XML::Node * elemnode = elemref->getRepr(); + std::vector<SPItem*> item_list; + item_list.push_back(SP_ITEM(elemref)); + std::vector<Inkscape::XML::Node*> item_to_select; + std::vector<SPItem*> item_selected; + SPCSSAttr *css; + Glib::ustring css_str; + switch (lpe_action){ + case LPE_TO_OBJECTS: + if (SP_ITEM(elemref)->isHidden()) { + elemref->deleteObject(); + } else { + if (elemnode->attribute("inkscape:path-effect")) { + sp_item_list_to_curves(item_list, item_selected, item_to_select); + } + elemnode->setAttribute("sodipodi:insensitive", NULL); + } + break; + + case LPE_ERASE: + elemref->deleteObject(); + break; + + case LPE_VISIBILITY: + css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); + if (!this->isVisible()/* && std::strcmp(elemref->getId(),sp_lpe_item->getId()) != 0*/) { + css->setAttribute("display", "none"); + } else { + css->setAttribute("display", NULL); + } + sp_repr_css_write_string(css,css_str); + elemnode->setAttribute("style", css_str.c_str()); + break; + + default: + break; + } + } + } + if (lpe_action == LPE_ERASE || lpe_action == LPE_TO_OBJECTS) { + items.clear(); + } +} + +void Effect::setCurrentShape(SPShape * shape){ + if(shape){ + sp_shape = shape; + if (!(sp_curve = sp_shape->getCurve())) { + // oops + return; + } + pathvector_before_effect = sp_curve->get_pathvector(); + } +} + /** * Is performed each time before the effect is updated. */ @@ -484,8 +556,12 @@ void Effect::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) void Effect::doOnApply_impl(SPLPEItem const* lpeitem) { sp_lpe_item = const_cast<SPLPEItem *>(lpeitem); - /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); - pathvector_before_effect = sp_curve->get_pathvector();*/ + sp_curve = SP_SHAPE(sp_lpe_item)->getCurve(); + pathvector_before_effect = sp_curve->get_pathvector(); + SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); + if(shape){ + setCurrentShape(shape); + } doOnApply(lpeitem); } @@ -495,6 +571,7 @@ void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem) //printf("(SPLPEITEM*) %p\n", sp_lpe_item); SPShape * shape = dynamic_cast<SPShape *>(sp_lpe_item); if(shape){ + setCurrentShape(shape); sp_curve = shape->getCurve(); pathvector_before_effect = sp_curve->get_pathvector(); } diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 0190325c0..6c11e0b67 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -19,7 +19,7 @@ class SPDocument; class SPDesktop; class SPItem; -class LivePathEffectObject; +class LivePathEffectObject; class SPLPEItem; class KnotHolder; class KnotHolderEntity; @@ -44,6 +44,12 @@ enum LPEPathFlashType { DEFAULT }; +enum LpeAction { + LPE_ERASE = 0, + LPE_TO_OBJECTS, + LPE_VISIBILITY +}; + class Effect { public: static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj); @@ -74,6 +80,9 @@ public: static int acceptsNumClicks(EffectType type); int acceptsNumClicks() const { return acceptsNumClicks(effectType()); } void doAcceptPathPreparations(SPLPEItem *lpeitem); + SPShape * getCurrentShape(){ return sp_shape; }; + void setCurrentShape(SPShape * shape); + void processObjects(LpeAction lpe_action); /* * isReady() indicates whether all preparations which are necessary to apply the LPE are done, @@ -126,7 +135,9 @@ public: bool apply_to_clippath_and_mask; bool erase_extra_objects; // set this to false allow retain extra generated objects, see measure line LPE bool upd_params; - + BoolParam is_visible; + SPCurve * sp_curve; + Geom::PathVector pathvector_before_effect; protected: Effect(LivePathEffectObject *lpeobject); @@ -151,7 +162,6 @@ protected: bool _provides_knotholder_entities; int oncanvasedit_it; - BoolParam is_visible; bool show_orig_path; // set this to true in derived effects to automatically have the original // path displayed as helperpath @@ -165,11 +175,11 @@ protected: bool concatenate_before_pwd2; SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them. + SPShape * sp_shape; // these get stored in doBeforeEffect_impl before doEffect chain, or in performPathEffects on groups, and derived classes may do as they please with them. + std::vector<const char *> items; double current_zoom; std::vector<Geom::Point> selected_nodes_pos; std::vector<size_t> selected_nodes_index; - SPCurve * sp_curve; - Geom::PathVector pathvector_before_effect; private: bool provides_own_flash_paths; // if true, the standard flash path is suppressed diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp index 10418a02d..31d5bab65 100644 --- a/src/live_effects/lpe-clone-original.cpp +++ b/src/live_effects/lpe-clone-original.cpp @@ -6,6 +6,12 @@ #include "live_effects/lpe-clone-original.h" #include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "sp-clippath.h" +#include "sp-mask.h" +#include "xml/sp-css-attr.h" + // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -14,9 +20,310 @@ namespace LivePathEffect { LPECloneOriginal::LPECloneOriginal(LivePathEffectObject *lpeobject) : Effect(lpeobject), - linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this) + linked_path("LEGACY FALLBACK", "LEGACY FALLBACK", "linkedpath", &wr, this), + linked_item(_("Linked Item:"), _("Item from which to take the original data"), "linked_item", &wr, this), + scale(_("Scale %"), _("Scale item %"), "scale", &wr, this, 100.0), + preserve_position(_("Preserve position"), _("Preserve position"), "preserve_position", &wr, this, false), + inverse(_("Inverse clone"), _("Use LPE item as origin"), "inverse", &wr, this, false), + d(_("Clone shape -d-"), _("Clone shape -d-"), "d", &wr, this, true), + transform(_("Clone transforms"), _("Clone transforms"), "transform", &wr, this, true), + fill(_("Clone fill"), _("Clone fill"), "fill", &wr, this, false), + stroke(_("Clone stroke"), _("Clone stroke"), "stroke", &wr, this, false), + paintorder(_("Clone paint order"), _("Clone paint order"), "paintorder", &wr, this, false), + opacity(_("Clone opacity"), _("Clone opacity"), "opacity", &wr, this, false), + filter(_("Clone filter"), _("Clone filter"), "filter", &wr, this, false), + attributes("Attributes linked", "Attributes linked, comma separated atributes", "attributes", &wr, this,""), + style_attributes("Style attributes linked", "Style attributes linked, comma separated atributes", "style_attributes", &wr, this,""), + expanded(false) { - registerParameter( dynamic_cast<Parameter *>(&linked_path) ); + registerParameter(&linked_path); + registerParameter(&linked_item); + registerParameter(&scale); + registerParameter(&attributes); + registerParameter(&style_attributes); + registerParameter(&preserve_position); + registerParameter(&inverse); + registerParameter(&d); + registerParameter(&transform); + registerParameter(&fill); + registerParameter(&stroke); + registerParameter(&paintorder); + registerParameter(&opacity); + registerParameter(&filter); + scale.param_set_range(0.01, 999999.0); + scale.param_set_increments(1, 1); + scale.param_set_digits(2); + attributes.param_hide_canvas_text(); + style_attributes.param_hide_canvas_text(); + preserve_position_changed = preserve_position; + preserve_affine = Geom::identity(); +} + +void +LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneAttrbutes((*obj_it), dest_child, live, attributes, style_attributes, false); + index++; + } + } + //Attributes + SPShape * shape_origin = SP_SHAPE(origin); + SPPath * path_origin = SP_PATH(origin); + SPShape * shape_dest = SP_SHAPE(dest); + SPMask *mask_origin = SP_ITEM(origin)->mask_ref->getObject(); + SPMask *mask_dest = SP_ITEM(dest)->mask_ref->getObject(); + if(mask_origin && mask_dest) { + std::vector<SPObject*> mask_list = mask_origin->childList(true); + std::vector<SPObject*> mask_list_dest = mask_dest->childList(true); + if (mask_list.size() == mask_list_dest.size()) { + size_t i = 0; + for ( std::vector<SPObject*>::const_iterator iter=mask_list.begin();iter!=mask_list.end();++iter) { + SPObject * mask_data = *iter; + SPObject * mask_dest_data = mask_list_dest[i]; + cloneAttrbutes(mask_data, mask_dest_data, live, attributes, style_attributes, false); + i++; + } + } + } + SPClipPath *clippath_origin = SP_ITEM(origin)->clip_ref->getObject(); + SPClipPath *clippath_dest = SP_ITEM(dest)->clip_ref->getObject(); + if(clippath_origin && clippath_dest) { + std::vector<SPObject*> clippath_list = clippath_origin->childList(true); + std::vector<SPObject*> clippath_list_dest = clippath_dest->childList(true); + if (clippath_list.size() == clippath_list_dest.size()) { + size_t i = 0; + for ( std::vector<SPObject*>::const_iterator iter=clippath_list.begin();iter!=clippath_list.end();++iter) { + SPObject * clippath_data = *iter; + SPObject * clippath_dest_data = clippath_list_dest[i]; + cloneAttrbutes(clippath_data, clippath_dest_data, live, attributes, style_attributes, false); + i++; + } + } + } + gchar ** attarray = g_strsplit(attributes, ",", 0); + gchar ** iter = attarray; + Geom::Affine affine_dest = Geom::identity(); + Geom::Affine affine_origin = Geom::identity(); + Geom::Affine affine_previous = Geom::identity(); + sp_svg_transform_read(SP_ITEM(dest)->getAttribute("transform"), &affine_dest); + sp_svg_transform_read(SP_ITEM(origin)->getAttribute("transform"), &affine_origin); + while (*iter != NULL) { + const char* attribute = (*iter); + if ( std::strcmp(attribute, "transform") == 0 ) { + if (preserve_position) { + Geom::Affine dest_affine = Geom::identity(); + if (root) { + dest_affine *= affine_origin; + if (preserve_affine == Geom::identity()) { + dest_affine *= Geom::Translate(affine_dest.translation()); + } + dest_affine *= Geom::Translate(affine_origin.translation()).inverse(); + dest_affine *= Geom::Translate(preserve_affine.translation()); + affine_previous = preserve_affine; + preserve_affine = Geom::identity(); + SP_ITEM(dest)->getRepr()->setAttribute("transform",sp_svg_transform_write(dest_affine)); + } + } else { + SP_ITEM(dest)->getRepr()->setAttribute("transform",sp_svg_transform_write(affine_origin)); + } + } else if ( shape_dest && shape_origin && live && (std::strcmp(attribute, "d") == 0)) { + SPCurve *c = NULL; + if (inverse) { + c = shape_origin->getCurveBeforeLPE(); + } else { + c = shape_origin->getCurve(); + } + if (c) { + Geom::PathVector c_pv = c->get_pathvector(); + Geom::OptRect orig_bbox = SP_ITEM(origin)->geometricBounds(); + Geom::OptRect dest_bbox = SP_ITEM(dest)->geometricBounds(); + if (dest_bbox && orig_bbox && root) { + Geom::Point orig_point = (*orig_bbox).corner(0); + Geom::Point dest_point = (*dest_bbox).corner(0); + if (scale != 100.0) { + double scale_affine = scale/100.0; + Geom::Scale scale = Geom::Scale(scale_affine); + c_pv *= Geom::Translate(orig_point).inverse(); + c_pv *= scale; + c_pv *= Geom::Translate(orig_point); + } + if (preserve_position) { + c_pv *= Geom::Translate(dest_point - orig_point); + } + } + if (inverse) { + c_pv *= i2anc_affine(origin, sp_lpe_item); + } else { + c_pv *= i2anc_affine(dest, sp_lpe_item); + } + c->set_pathvector(c_pv); + if (!path_origin) { + shape_dest->setCurveInsync(c, TRUE); + dest->getRepr()->setAttribute(attribute, sp_svg_write_path(c_pv)); + } else { + shape_dest->setCurve(c, TRUE); + } + c->unref(); + } else { + dest->getRepr()->setAttribute(attribute, NULL); + } + } else { + dest->getRepr()->setAttribute(attribute, origin->getRepr()->attribute(attribute)); + } + iter++; + } + g_strfreev (attarray); + SPCSSAttr *css_origin = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css_origin, origin->getRepr()->attribute("style")); + SPCSSAttr *css_dest = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css_dest, dest->getRepr()->attribute("style")); + gchar ** styleattarray = g_strsplit(style_attributes, ",", 0); + gchar ** styleiter = styleattarray; + while (*styleiter != NULL) { + const char* attribute = (*styleiter); + const char* origin_attribute = sp_repr_css_property(css_origin, attribute, ""); + if (origin_attribute == "") { + sp_repr_css_set_property (css_dest, attribute, NULL); + } else { + sp_repr_css_set_property (css_dest, attribute, origin_attribute); + } + styleiter++; + } + g_strfreev (styleattarray); + Glib::ustring css_str; + sp_repr_css_write_string(css_dest,css_str); + dest->getRepr()->setAttribute("style", css_str.c_str()); +} + +void +LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){ + if (linked_path.linksToPath()) { //Legacy staff + Glib::ustring attributes_value("d"); + attributes.param_setValue(attributes_value); + attributes.write_to_SVG(); + Glib::ustring style_attributes_value(""); + style_attributes.param_setValue(style_attributes_value); + style_attributes.write_to_SVG(); + linked_item.param_readSVGValue(linked_path.param_getSVGValue()); + linked_path.param_readSVGValue(""); + } + + if (linked_item.linksToItem()) { + linked_item.setInverse(inverse); + if ( preserve_position_changed != preserve_position ) { + if (!preserve_position) { + sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &preserve_affine); + } + preserve_position_changed = preserve_position; + } + Glib::ustring attr = ""; + if (d) { + attr.append("d,"); + } + if (transform) { + attr.append("transform,"); + } + attr.append(Glib::ustring(attributes.param_getSVGValue()).append(",")); + if (attr.size() && !Glib::ustring(attributes.param_getSVGValue()).size()) { + attr.erase (attr.size()-1, 1); + } + Glib::ustring style_attr = ""; + if (fill) { + style_attr.append("fill,").append("fill-rule,"); + } + if (stroke) { + style_attr.append("stroke,").append("stroke-width,").append("stroke-linecap,").append("stroke-linejoin,"); + style_attr.append("stroke-opacity,").append("stroke-miterlimit,").append("stroke-dasharray,"); + style_attr.append("stroke-opacity,").append("stroke-dashoffset,").append("marker-start,"); + style_attr.append("marker-mid,").append("marker-end,"); + } + if (paintorder) { + style_attr.append("paint-order,"); + } + if (filter) { + style_attr.append("filter,"); + } + if (opacity) { + style_attr.append("opacity,"); + } + if (style_attr.size() && !Glib::ustring(style_attributes.param_getSVGValue()).size()) { + style_attr.erase (style_attr.size()-1, 1); + } + style_attr.append(Glib::ustring(style_attributes.param_getSVGValue()).append(",")); + if (inverse) { + cloneAttrbutes(SP_OBJECT(sp_lpe_item), linked_item.getObject(), true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true); + } else { + cloneAttrbutes(linked_item.getObject(), SP_OBJECT(sp_lpe_item), true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true); + } + } +} + + +Gtk::Widget * +LPECloneOriginal::newWidget() +{ + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); + vbox_expander->set_border_width(0); + vbox_expander->set_spacing(2); + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter * param = *it; + if (param->param_key == "linkedpath") { + ++it; + continue; + } + Gtk::Widget * widg = param->param_newWidget(); + Glib::ustring * tip = param->param_getTooltip(); + if (widg) { + if (param->param_key != "attributes" && + param->param_key != "style_attributes") { + vbox->pack_start(*widg, true, true, 2); + } else { + vbox_expander->pack_start(*widg, true, true, 2); + } + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + + ++it; + } + expander = Gtk::manage(new Gtk::Expander(Glib::ustring(_("Show attributes override")))); + expander->add(*vbox_expander); + expander->set_expanded(expanded); + expander->property_expanded().signal_changed().connect(sigc::mem_fun(*this, &LPECloneOriginal::onExpanderChanged) ); + vbox->pack_start(*expander, true, true, 2); + this->upd_params = false; + return dynamic_cast<Gtk::Widget *>(vbox); +} + +void +LPECloneOriginal::onExpanderChanged() +{ + expanded = expander->get_expanded(); + if(expanded) { + expander->set_label (Glib::ustring(_("Hide attributes override"))); + } else { + expander->set_label (Glib::ustring(_("Show attributes override"))); + } } LPECloneOriginal::~LPECloneOriginal() @@ -24,12 +331,22 @@ LPECloneOriginal::~LPECloneOriginal() } -void LPECloneOriginal::doEffect (SPCurve * curve) +void +LPECloneOriginal::transform_multiply(Geom::Affine const& postmul, bool set) +{ + if (linked_item.linksToItem()) { + bool changed = false; + linked_item.getObject()->requestModified(SP_OBJECT_MODIFIED_FLAG); + } +} + +void +LPECloneOriginal::doEffect (SPCurve * curve) { - if ( linked_path.linksToPath() ) { - Geom::PathVector linked_pathv = linked_path.get_pathvector(); - if ( !linked_pathv.empty() ) { - curve->set_pathvector(linked_pathv); + if (linked_item.linksToItem() && !inverse) { + SPShape * shape = getCurrentShape(); + if(shape){ + curve->set_pathvector(shape->getCurve()->get_pathvector()); } } } diff --git a/src/live_effects/lpe-clone-original.h b/src/live_effects/lpe-clone-original.h index abf65ded8..0ff5eb01d 100644 --- a/src/live_effects/lpe-clone-original.h +++ b/src/live_effects/lpe-clone-original.h @@ -8,24 +8,48 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - +#include <gtkmm/expander.h> #include "live_effects/effect.h" +#include "live_effects/parameter/originalitem.h" #include "live_effects/parameter/originalpath.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/point.h" +#include "live_effects/parameter/text.h" +#include "live_effects/lpegroupbbox.h" namespace Inkscape { namespace LivePathEffect { -class LPECloneOriginal : public Effect { +class LPECloneOriginal : public Effect, GroupBBoxEffect { public: LPECloneOriginal(LivePathEffectObject *lpeobject); virtual ~LPECloneOriginal(); - virtual void doEffect (SPCurve * curve); + virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void transform_multiply(Geom::Affine const& postmul, bool set); + virtual Gtk::Widget * newWidget(); + void onExpanderChanged(); + void cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root); private: OriginalPathParam linked_path; - -private: + OriginalItemParam linked_item; + ScalarParam scale; + BoolParam preserve_position; + BoolParam inverse; + BoolParam d; + BoolParam transform; + BoolParam fill; + BoolParam stroke; + BoolParam paintorder; + BoolParam opacity; + BoolParam filter; + TextParam attributes; + TextParam style_attributes; + bool preserve_position_changed; + bool expanded; + Gtk::Expander * expander; + Geom::Affine preserve_affine; LPECloneOriginal(const LPECloneOriginal&); LPECloneOriginal& operator=(const LPECloneOriginal&); }; diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index 813f25d3d..42e055062 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -17,6 +17,13 @@ #include <2geom/sbasis-to-bezier.h> #include <2geom/intersection-graph.h> #include "live_effects/lpe-copy_rotate.h" +#include "display/curve.h" +#include "svg/path-string.h" +#include "svg/svg.h" +#include "style.h" +#include "helper/geom.h" +#include "xml/sp-css-attr.h" +#include "path-chemistry.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -45,9 +52,11 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : starting_angle(_("Starting angle"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0), rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0), num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 6), + split_gap(_("Gap on split"), _("Gap on split"), "split_gap", &wr, this, -0.001), copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true), fuse_paths(_("Kaleidoskope"), _("Kaleidoskope by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false), join_paths(_("Join paths"), _("Join paths, use fill-rule: evenodd for best result"), "join_paths", &wr, this, false), + split_items(_("Split elements"), _("Split elements, this allow gradients and other paints."), "split_items", &wr, this, false), dist_angle_handle(100.0) { show_orig_path = true; @@ -56,15 +65,21 @@ LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : registerParameter(&copies_to_360); registerParameter(&fuse_paths); registerParameter(&join_paths); + registerParameter(&split_items); registerParameter(&starting_angle); registerParameter(&starting_point); registerParameter(&rotation_angle); registerParameter(&num_copies); + registerParameter(&split_gap); registerParameter(&origin); - + split_gap.param_set_range(-999999.0, 999999.0); + split_gap.param_set_increments(0.1, 0.1); + split_gap.param_set_digits(5); + num_copies.param_set_range(0, 999999); num_copies.param_make_integer(true); - num_copies.param_set_range(0, 1000); apply_to_clippath_and_mask = true; + previous_num_copies = num_copies; + reset = false; } LPECopyRotate::~LPECopyRotate() @@ -72,6 +87,207 @@ LPECopyRotate::~LPECopyRotate() } +void +LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem) +{ + if (split_items) { + SPDocument * document = SP_ACTIVE_DOCUMENT; + items.clear(); + container = dynamic_cast<SPObject *>(sp_lpe_item->parent); + SPDocument * doc = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); + Inkscape::XML::Node *root_origin = doc->getReprRoot(); + if (root_origin != root) { + return; + } + if (previous_num_copies != num_copies) { + gint numcopies_gap = previous_num_copies - num_copies; + if (numcopies_gap > 0 && num_copies != 0) { + guint counter = num_copies - 1; + while (numcopies_gap > 0) { + const char * id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + if (!id || strlen(id) == 0) { + return; + } + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + SP_ITEM(elemref)->setHidden(true); + } + counter++; + numcopies_gap--; + } + } + previous_num_copies = num_copies; + } + SPObject *elemref = NULL; + char * id = g_strdup(Glib::ustring("rotated-").append("1").append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + guint counter = 0; + while(elemref = document->getObjectById(id)) { + if (SP_ITEM(elemref)->isHidden()) { + items.push_back(id); + } + id = g_strdup(Glib::ustring("rotated-").append(std::to_string(counter)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + counter++; + } + g_free(id); + double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + Geom::Rect bbox(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + double size_divider = Geom::distance(origin,bbox) + (diagonal * 2); + Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider; + Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider; + Geom::Affine m = Geom::Translate(-origin) * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))); + if (fuse_paths) { + size_t rest = 0; + for (size_t i = 1; i < num_copies; ++i) { + Geom::Affine r = Geom::identity(); + Geom::Point dir = unit_vector((Geom::Point)origin - Geom::middle_point(line_start,line_end)); + if( rest%2 == 0) { + r *= Geom::Rotate(Geom::Angle(dir)).inverse(); + r *= Geom::Scale(1, -1); + r *= Geom::Rotate(Geom::Angle(dir)); + } + Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i))); + Geom::Affine t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + if( rest%2 == 0) { + t = m * r * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)).inverse() * Geom::Translate(origin); + } + t *= sp_lpe_item->transform; + toItem(t, i-1, reset); + rest ++; + } + } else { + for (size_t i = 1; i < num_copies; ++i) { + Geom::Rotate rot(-(Geom::rad_from_deg(rotation_angle * i))); + Geom::Affine t = m * rot * Geom::Rotate(Geom::rad_from_deg(starting_angle)) * Geom::Translate(origin); + t *= sp_lpe_item->transform; + toItem(t, i - 1, reset); + } + } + reset = false; + } else { + processObjects(LPE_ERASE); + items.clear(); + } + + std::cout << previous_num_copies << "previous_num_copies\n"; + std::cout << num_copies << "num_copies\n"; +} + +void +LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { + std::vector< SPObject * > childs = origin->childList(true); + size_t index = 0; + for (std::vector<SPObject * >::iterator obj_it = childs.begin(); + obj_it != childs.end(); ++obj_it) { + SPObject *dest_child = dest->nthChild(index); + cloneD(*obj_it, dest_child, false, reset); + index++; + } + } + SPShape * shape = SP_SHAPE(origin); + SPPath * path = SP_PATH(dest); + if (!path && !SP_IS_GROUP(dest)) { + Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); + dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); + path = SP_PATH(dest); + } + if (path && shape) { + SPCurve *c = NULL; + if (root) { + c = new SPCurve(); + c->set_pathvector(pathvector_before_effect); + } else { + c = shape->getCurve(); + } + if (c) { + path->setCurve(c, TRUE); + c->unref(); + } else { + dest->getRepr()->setAttribute("d", NULL); + } + if (reset) { + dest->getRepr()->setAttribute("style", shape->getRepr()->attribute("style")); + } + } +} + +void +LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset) +{ + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); + items.push_back(elemref_id); + SPObject *elemref= NULL; + Inkscape::XML::Node *phantom = NULL; + if (elemref = document->getObjectById(elemref_id)) { + phantom = elemref->getRepr(); + } else { + phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); + std::vector<const char *> attrs; + attrs.push_back("inkscape:path-effect"); + attrs.push_back("inkscape:original-d"); + attrs.push_back("sodipodi:type"); + attrs.push_back("sodipodi:rx"); + attrs.push_back("sodipodi:ry"); + attrs.push_back("sodipodi:cx"); + attrs.push_back("sodipodi:cy"); + attrs.push_back("sodipodi:end"); + attrs.push_back("sodipodi:start"); + attrs.push_back("inkscape:flatsided"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:rounded"); + attrs.push_back("sodipodi:arg1"); + attrs.push_back("sodipodi:arg2"); + attrs.push_back("sodipodi:r1"); + attrs.push_back("sodipodi:r2"); + attrs.push_back("sodipodi:sides"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("sodipodi:argument"); + attrs.push_back("sodipodi:expansion"); + attrs.push_back("sodipodi:radius"); + attrs.push_back("sodipodi:revolution"); + attrs.push_back("sodipodi:t0"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("x"); + attrs.push_back("y"); + attrs.push_back("rx"); + attrs.push_back("ry"); + attrs.push_back("width"); + attrs.push_back("height"); + phantom->setAttribute("id", elemref_id); + for(const char * attr : attrs) { + phantom->setAttribute(attr, NULL); + } + } + if (!elemref) { + elemref = container->appendChildRepr(phantom); + Inkscape::GC::release(phantom); + } + cloneD(SP_OBJECT(sp_lpe_item), elemref, true, reset); + elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); + SP_ITEM(elemref)->setHidden(false); + if (elemref->parent != container) { + Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); + copy->setAttribute("id", elemref_id); + container->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + } +} + +void +LPECopyRotate::resetStyles(){ + reset = true; + doAfterEffect(sp_lpe_item); +} + Gtk::Widget * LPECopyRotate::newWidget() { // use manage here, because after deletion of Effect object, others might @@ -81,7 +297,15 @@ Gtk::Widget * LPECopyRotate::newWidget() vbox->set_border_width(5); vbox->set_homogeneous(false); vbox->set_spacing(2); - + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); + Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); + vbox_expander->set_border_width(0); + vbox_expander->set_spacing(2); + Gtk::Button * reset_button = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset styles")))); + reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPECopyRotate::resetStyles)); + reset_button->set_size_request(140,30); + vbox->pack_start(*hbox, true,true,2); + hbox->pack_start(*reset_button, false, false,2); std::vector<Parameter *>::iterator it = param_vector.begin(); while (it != param_vector.end()) { if ((*it)->widget_is_visible) { @@ -125,11 +349,11 @@ void LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set) { // cycle through all parameters. Most parameters will not need transformation, but path and point params do. - for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) { Parameter * param = *it; param->param_transform_multiply(postmul, set); } + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); } void @@ -140,17 +364,17 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) if (copies_to_360) { rotation_angle.param_set_value(360.0/(double)num_copies); } - if (fuse_paths && rotation_angle * num_copies > 360 && rotation_angle > 0) { + if (fuse_paths && rotation_angle * num_copies > 360.1 && rotation_angle > 0) { num_copies.param_set_value(floor(360/rotation_angle)); } if (fuse_paths && copies_to_360) { - num_copies.param_set_increments(2,2); + num_copies.param_set_increments(2.0,10.0); if ((int)num_copies%2 !=0) { num_copies.param_set_value(num_copies+1); rotation_angle.param_set_value(360.0/(double)num_copies); } } else { - num_copies.param_set_increments(1,1); + num_copies.param_set_increments(1.0, 10.0); } if (dist_angle_handle < 1.0) { @@ -352,12 +576,56 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s tmp_path.clear(); } +Geom::PathVector +LPECopyRotate::doEffect_path (Geom::PathVector const & path_in) +{ + Geom::PathVector path_out; + if (split_items && (fuse_paths || join_paths)) { + if (num_copies == 0) { + return path_out; + } + path_out = pathv_to_linear_and_cubic_beziers(path_in); + double diagonal = Geom::distance(Geom::Point(boundingbox_X.min(),boundingbox_Y.min()),Geom::Point(boundingbox_X.max(),boundingbox_Y.max())); + Geom::OptRect bbox = sp_lpe_item->geometricBounds(); + double size_divider = Geom::distance(origin,bbox) + (diagonal * 2); + Geom::Point line_start = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(starting_angle))) * size_divider; + Geom::Point line_end = origin + dir * Geom::Rotate(-(Geom::rad_from_deg(rotation_angle + starting_angle))) * size_divider; + Geom::Path divider = Geom::Path(line_start); + divider.appendNew<Geom::LineSegment>((Geom::Point)origin); + divider.appendNew<Geom::LineSegment>(line_end); + divider.close(); + Geom::PathVector triangle; + triangle.push_back(divider); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(triangle, path_out); + if (pig && ! path_out.empty() && !triangle.empty()) { + //TODO: Here can produce a crash because some knows problems in new boolops code + path_out = pig->getIntersection(); + } + Geom::Affine r = Geom::identity(); + Geom::Point dir = unit_vector(Geom::middle_point(line_start,line_end) - (Geom::Point)origin); + Geom::Point gap = dir * split_gap; + path_out *= Geom::Translate(gap); + } else { + // default behavior + for (unsigned int i=0; i < path_in.size(); i++) { + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb(); + Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in); + Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE); + // add the output path vector to the already accumulated vector: + for (unsigned int j=0; j < path.size(); j++) { + path_out.push_back(path[j]); + } + } + } + return pathv_to_linear_and_cubic_beziers(path_out); +} + Geom::Piecewise<Geom::D2<Geom::SBasis> > LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) { using namespace Geom; - if (num_copies == 1 && !fuse_paths) { + if ((num_copies == 1 && !fuse_paths) || split_items) { return pwd2_in; } @@ -373,10 +641,10 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p divider.appendNew<Geom::LineSegment>(line_end); Piecewise<D2<SBasis> > output; Affine pre = Translate(-origin) * Rotate(-rad_from_deg(starting_angle)); + PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001)); if (fuse_paths) { Geom::PathVector path_out; Geom::PathVector tmp_path; - PathVector const original_pathv = path_from_piecewise(remove_short_cuts(pwd2_in, 0.1), 0.001); for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) { if (path_it->empty()) { continue; @@ -403,7 +671,7 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p output = paths_to_pw(path_out); } } else { - Geom::PathVector output_pv = path_from_piecewise(output , 0.01); + Geom::PathVector output_pv; for (int i = 0; i < num_copies; ++i) { Rotate rot(-rad_from_deg(rotation_angle * i)); Affine t = pre * rot * Translate(origin); @@ -449,6 +717,23 @@ LPECopyRotate::resetDefaults(SPItem const* item) original_bbox(SP_LPE_ITEM(item)); } +void +LPECopyRotate::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +{ + processObjects(LPE_VISIBILITY); +} + +void +LPECopyRotate::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //unset "erase_extra_objects" hook on sp-lpe-item.cpp + if (!erase_extra_objects) { + processObjects(LPE_TO_OBJECTS); + return; + } + processObjects(LPE_ERASE); +} + } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-copy_rotate.h b/src/live_effects/lpe-copy_rotate.h index c2ae2daf1..dbec2e1c3 100644 --- a/src/live_effects/lpe-copy_rotate.h +++ b/src/live_effects/lpe-copy_rotate.h @@ -15,6 +15,8 @@ */ #include "live_effects/effect.h" +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/text.h" #include "live_effects/parameter/point.h" #include "live_effects/lpegroupbbox.h" @@ -26,13 +28,20 @@ public: LPECopyRotate(LivePathEffectObject *lpeobject); virtual ~LPECopyRotate(); virtual void doOnApply (SPLPEItem const* lpeitem); + virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); virtual void doBeforeEffect (SPLPEItem const* lpeitem); + virtual void doAfterEffect (SPLPEItem const* lpeitem); virtual void setFusion(Geom::PathVector &path_in, Geom::Path divider, double sizeDivider); virtual void split(Geom::PathVector &path_in, Geom::Path const ÷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-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/parameter/bool.cpp b/src/live_effects/parameter/bool.cpp index af99ef362..813c06b4e 100644 --- a/src/live_effects/parameter/bool.cpp +++ b/src/live_effects/parameter/bool.cpp @@ -72,7 +72,7 @@ BoolParam::param_newWidget() checkwdg->setActive(value); checkwdg->setProgrammatically = false; checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter")); - + param_effect->upd_params = false; return dynamic_cast<Gtk::Widget *> (checkwdg); } else { return NULL; @@ -82,6 +82,7 @@ BoolParam::param_newWidget() void BoolParam::param_setValue(bool newvalue) { + param_effect->upd_params = true; value = newvalue; } diff --git a/src/live_effects/parameter/item-reference.cpp b/src/live_effects/parameter/item-reference.cpp new file mode 100644 index 000000000..a775d93b7 --- /dev/null +++ b/src/live_effects/parameter/item-reference.cpp @@ -0,0 +1,44 @@ +/* + * The reference corresponding to href of LPE Item parameter. + * + * Copyright (C) 2008 Johan Engelen + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include "live_effects/parameter/item-reference.h" + +#include "sp-shape.h" +#include "sp-text.h" +#include "sp-item-group.h" + +namespace Inkscape { +namespace LivePathEffect { + +bool ItemReference::_acceptObject(SPObject * const obj) const +{ + if (SP_IS_SHAPE(obj) || SP_IS_TEXT(obj) || SP_IS_GROUP(obj)) { + /* Refuse references to lpeobject */ + if (obj == getOwner()) { + return false; + } + // TODO: check whether the referred item has this LPE applied, if so: deny deny deny! + return URIReference::_acceptObject(obj); + } else { + return false; + } +} + +} // namespace LivePathEffect +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/item-reference.h b/src/live_effects/parameter/item-reference.h new file mode 100644 index 000000000..91231455a --- /dev/null +++ b/src/live_effects/parameter/item-reference.h @@ -0,0 +1,56 @@ +#ifndef SEEN_LPE_ITEM_REFERENCE_H +#define SEEN_LPE_ITEM_REFERENCE_H + +/* + * Copyright (C) 2008-2012 Authors + * Authors: Johan Engelen + * Abhishek Sharma + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include <uri-references.h> + +class SPItem; +namespace Inkscape { +namespace XML { class Node; } + +namespace LivePathEffect { + +/** + * The reference corresponding to href of LPE ItemParam. + */ +class ItemReference : public Inkscape::URIReference { +public: + ItemReference(SPObject *owner) : URIReference(owner) {} + + SPItem *getObject() const { + return (SPItem *)URIReference::getObject(); + } + +protected: + virtual bool _acceptObject(SPObject * const obj) const; + +private: + ItemReference(const ItemReference&); + ItemReference& operator=(const ItemReference&); +}; + +} // namespace LivePathEffect + +} // namespace Inkscape + + + +#endif /* !SEEN_LPE_PATH_REFERENCE_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/item.cpp b/src/live_effects/parameter/item.cpp new file mode 100644 index 000000000..8caea4e26 --- /dev/null +++ b/src/live_effects/parameter/item.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> + * Abhishek Sharma + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "ui/widget/point.h" +#include <glibmm/i18n.h> + +#include "live_effects/parameter/item.h" +#include "live_effects/effect.h" +#include "svg/svg.h" + +#include "widgets/icon.h" +#include <gtk/gtk.h> +#include "selection-chemistry.h" +#include "xml/repr.h" +#include "desktop.h" +#include "inkscape.h" +#include "message-stack.h" + +// clipboard support +#include "ui/clipboard.h" +// required for linking to other paths +#include "uri.h" + +#include <gtkmm/button.h> +#include <gtkmm/label.h> +#include "ui/icon-names.h" + +namespace Inkscape { + +namespace LivePathEffect { + +ItemParam::ItemParam( const Glib::ustring& label, const Glib::ustring& tip, + const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, + Effect* effect, const gchar * default_value) + : Parameter(label, tip, key, wr, effect), + changed(true), + href(NULL), + ref( (SPObject*)effect->getLPEObj() ) +{ + defvalue = g_strdup(default_value); + ref_changed_connection = ref.changedSignal().connect(sigc::mem_fun(*this, &ItemParam::ref_changed)); +} + +ItemParam::~ItemParam() +{ + remove_link(); + g_free(defvalue); +} + +void +ItemParam::param_set_default() +{ + param_readSVGValue(defvalue); +} + + +void +ItemParam::param_set_and_write_default() +{ + param_write_to_repr(defvalue); +} + +bool +ItemParam::param_readSVGValue(const gchar * strvalue) +{ + if (strvalue) { + remove_link(); + if (strvalue[0] == '#') { + if (href) + g_free(href); + href = g_strdup(strvalue); + try { + ref.attach(Inkscape::URI(href)); + //lp:1299948 + SPItem* i = ref.getObject(); + if (i) { + linked_modified_callback(i, SP_OBJECT_MODIFIED_FLAG); + } // else: document still processing new events. Repr of the linked object not created yet. + } catch (Inkscape::BadURIException &e) { + g_warning("%s", e.what()); + ref.detach(); + } + } + emit_changed(); + return true; + } + + return false; +} + +gchar * +ItemParam::param_getSVGValue() const +{ + return g_strdup(href); +} + +Gtk::Widget * +ItemParam::param_newWidget() +{ + Gtk::HBox * _widget = Gtk::manage(new Gtk::HBox()); + Gtk::Widget* pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-clone"), Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Button * pButton = Gtk::manage(new Gtk::Button()); + Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true); + pLabel->set_tooltip_text(param_tooltip); + pButton->set_relief(Gtk::RELIEF_NONE); + pIcon->show(); + pButton->add(*pIcon); + pButton->show(); + pButton->signal_clicked().connect(sigc::mem_fun(*this, &ItemParam::on_link_button_click)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); + pButton->set_tooltip_text(_("Link to item on clipboard")); + + static_cast<Gtk::HBox*>(_widget)->show_all_children(); + + return dynamic_cast<Gtk::Widget *> (_widget); +} + +void +ItemParam::emit_changed() +{ + changed = true; + signal_item_changed.emit(); +} + + +void +ItemParam::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &hp_vec) +{ +} + + +void +ItemParam::start_listening(SPObject * to) +{ + if ( to == NULL ) { + return; + } + linked_delete_connection = to->connectDelete(sigc::mem_fun(*this, &ItemParam::linked_delete)); + linked_modified_connection = to->connectModified(sigc::mem_fun(*this, &ItemParam::linked_modified)); + if (SP_IS_ITEM(to)) { + linked_transformed_connection = SP_ITEM(to)->connectTransformed(sigc::mem_fun(*this, &ItemParam::linked_transformed)); + } + linked_modified(to, SP_OBJECT_MODIFIED_FLAG); // simulate linked_modified signal, so that path data is updated +} + +void +ItemParam::quit_listening(void) +{ + linked_modified_connection.disconnect(); + linked_delete_connection.disconnect(); + linked_transformed_connection.disconnect(); +} + +void +ItemParam::ref_changed(SPObject */*old_ref*/, SPObject *new_ref) +{ + quit_listening(); + if ( new_ref ) { + start_listening(new_ref); + } +} + +void +ItemParam::remove_link() +{ + if (href) { + ref.detach(); + g_free(href); + href = NULL; + } +} + +void +ItemParam::linked_delete(SPObject */*deleted*/) +{ + quit_listening(); + remove_link(); +} + +void ItemParam::linked_modified(SPObject *linked_obj, guint flags) +{ + linked_modified_callback(linked_obj, flags); +} + +void ItemParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item) +{ + linked_transformed_callback(rel_transf, moved_item); +} + +void +ItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/) +{ + emit_changed(); + SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); +} + +void +ItemParam::on_link_button_click() +{ + Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); + const gchar * iid = cm->getFirstObjectID(); + if (!iid) { + return; + } + + Glib::ustring itemid(iid); + + if (itemid.empty()) { + return; + } + + // add '#' at start to make it an uri. + itemid.insert(itemid.begin(), '#'); + if ( href && strcmp(itemid.c_str(), href) == 0 ) { + // no change, do nothing + return; + } else { + // TODO: + // check if id really exists in document, or only in clipboard document: if only in clipboard then invalid + // check if linking to object to which LPE is applied (maybe delegated to PathReference + + param_write_to_repr(itemid.c_str()); + DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, + _("Link item parameter to path")); + } +} + +} /* namespace LivePathEffect */ + +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/item.h b/src/live_effects/parameter/item.h new file mode 100644 index 000000000..6c719d451 --- /dev/null +++ b/src/live_effects/parameter/item.h @@ -0,0 +1,79 @@ +#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H +#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ITEM_H + +/* + * Inkscape::LivePathEffectParameters + * +* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> + + +#include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/item-reference.h" +#include <stddef.h> +#include <sigc++/sigc++.h> + +namespace Inkscape { + +namespace LivePathEffect { + +class ItemParam : public Parameter { +public: + ItemParam ( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Inkscape::UI::Widget::Registry* wr, + Effect* effect, + const gchar * default_value = ""); + virtual ~ItemParam(); + virtual Gtk::Widget * param_newWidget(); + + virtual bool param_readSVGValue(const gchar * strvalue); + virtual gchar * param_getSVGValue() const; + + virtual void param_set_default(); + void param_set_and_write_default(); + virtual void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector<Geom::PathVector> &hp_vec); + + sigc::signal <void> signal_item_pasted; + sigc::signal <void> signal_item_changed; + bool changed; /* this gets set whenever the path is changed (this is set to true, and then the signal_item_changed signal is emitted). + * the user must set it back to false if she wants to use it sensibly */ +protected: + + gchar * href; // contains link to other object, e.g. "#path2428", NULL if ItemParam contains pathdata itself + ItemReference ref; + sigc::connection ref_changed_connection; + sigc::connection linked_delete_connection; + sigc::connection linked_modified_connection; + sigc::connection linked_transformed_connection; + void ref_changed(SPObject *old_ref, SPObject *new_ref); + void remove_link(); + void start_listening(SPObject * to); + void quit_listening(void); + void linked_delete(SPObject *deleted); + void linked_modified(SPObject *linked_obj, guint flags); + void linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item); + virtual void linked_modified_callback(SPObject *linked_obj, guint flags); + virtual void linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/) {}; + void on_link_button_click(); + + void emit_changed(); + + gchar * defvalue; + +private: + ItemParam(const ItemParam&); + ItemParam& operator=(const ItemParam&); +}; + + +} //namespace LivePathEffect + +} //namespace Inkscape + +#endif diff --git a/src/live_effects/parameter/originalitem.cpp b/src/live_effects/parameter/originalitem.cpp new file mode 100644 index 000000000..053062128 --- /dev/null +++ b/src/live_effects/parameter/originalitem.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gtkmm/box.h> +#include "live_effects/parameter/originalitem.h" + +#include "widgets/icon.h" +#include <glibmm/i18n.h> +#include <gtkmm/button.h> +#include <gtkmm/label.h> + +#include "uri.h" +#include "sp-shape.h" +#include "sp-text.h" +#include "display/curve.h" +#include "live_effects/effect.h" + +#include "inkscape.h" +#include "desktop.h" +#include "selection.h" +#include "ui/icon-names.h" + +namespace Inkscape { + +namespace LivePathEffect { + +OriginalItemParam::OriginalItemParam( const Glib::ustring& label, const Glib::ustring& tip, + const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, + Effect* effect) + : ItemParam(label, tip, key, wr, effect, "") +{ +} + +OriginalItemParam::~OriginalItemParam() +{ + +} + +Gtk::Widget * +OriginalItemParam::param_newWidget() +{ + Gtk::HBox *_widget = Gtk::manage(new Gtk::HBox()); + + { // Label + Gtk::Label *pLabel = Gtk::manage(new Gtk::Label(param_label)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true); + pLabel->set_tooltip_text(param_tooltip); + } + + { // Paste item to link button + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-paste"), Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); + pButton->set_relief(Gtk::RELIEF_NONE); + pIcon->show(); + pButton->add(*pIcon); + pButton->show(); + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_link_button_click)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); + pButton->set_tooltip_text(_("Link to item")); + } + + { // Select original button + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon("edit-select-original", Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); + pButton->set_relief(Gtk::RELIEF_NONE); + pIcon->show(); + pButton->add(*pIcon); + pButton->show(); + pButton->signal_clicked().connect(sigc::mem_fun(*this, &OriginalItemParam::on_select_original_button_click)); + static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true); + pButton->set_tooltip_text(_("Select original")); + } + + static_cast<Gtk::HBox*>(_widget)->show_all_children(); + + return dynamic_cast<Gtk::Widget *> (_widget); +} + +void +OriginalItemParam::linked_modified_callback(SPObject *linked_obj, guint /*flags*/) +{ + if (!inverse) { + emit_changed(); + SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG); + } +} + +void +OriginalItemParam::linked_transformed_callback(Geom::Affine const * /*rel_transf*/, SPItem * /*moved_item*/) +{ +/** \todo find good way to compensate for referenced item transform, like done for normal clones. + * See sp-use.cpp: sp_use_move_compensate */ +} + + +void +OriginalItemParam::on_select_original_button_click() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + SPItem *original = ref.getObject(); + if (desktop == NULL || original == NULL) { + return; + } + Inkscape::Selection *selection = desktop->getSelection(); + selection->clear(); + selection->set(original); +} + +} /* namespace LivePathEffect */ + +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/parameter/originalitem.h b/src/live_effects/parameter/originalitem.h new file mode 100644 index 000000000..58d04e05a --- /dev/null +++ b/src/live_effects/parameter/originalitem.h @@ -0,0 +1,49 @@ +#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H +#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINAL_ITEM_H + +/* + * Inkscape::LiveItemEffectParameters + * +* Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/parameter/item.h" + +namespace Inkscape { + +namespace LivePathEffect { + +class OriginalItemParam: public ItemParam { +public: + OriginalItemParam ( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Inkscape::UI::Widget::Registry* wr, + Effect* effect); + virtual ~OriginalItemParam(); + void setInverse(bool inversed) { inverse = inversed; } + bool linksToItem() const { return (href != NULL); } + SPItem * getObject() const { return ref.getObject(); } + + virtual Gtk::Widget * param_newWidget(); + +protected: + virtual void linked_modified_callback(SPObject *linked_obj, guint flags); + virtual void linked_transformed_callback(Geom::Affine const *rel_transf, SPItem *moved_item); + + void on_select_original_button_click(); + +private: + bool inverse; + OriginalItemParam(const OriginalItemParam&); + OriginalItemParam& operator=(const OriginalItemParam&); +}; + + +} //namespace LivePathEffect + +} //namespace Inkscape + +#endif diff --git a/src/live_effects/parameter/text.cpp b/src/live_effects/parameter/text.cpp index 8cab68ad0..5c4cdf4c6 100644 --- a/src/live_effects/parameter/text.cpp +++ b/src/live_effects/parameter/text.cpp @@ -125,13 +125,14 @@ TextParam::param_newWidget() rsu->setProgrammatically = false; rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change text parameter")); - + param_effect->upd_params = false; return dynamic_cast<Gtk::Widget *> (rsu); } void TextParam::param_setValue(const Glib::ustring newvalue) { + param_effect->upd_params = true; value = newvalue; if (!_hide_canvas_text) { sp_canvastext_set_text (canvas_text, newvalue.c_str()); diff --git a/src/print.cpp b/src/print.cpp index 7cd05ac93..479401995 100644 --- a/src/print.cpp +++ b/src/print.cpp @@ -79,6 +79,7 @@ unsigned int sp_print_text(SPPrintContext *ctx, char const *text, Geom::Point p, void sp_print_document(Gtk::Window& parentWindow, SPDocument *doc) { + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; doc->ensureUpToDate(); // Build arena @@ -88,6 +89,7 @@ sp_print_document(Gtk::Window& parentWindow, SPDocument *doc) Inkscape::UI::Dialog::Print printop(doc,base); Gtk::PrintOperationResult res = printop.run(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG, parentWindow); (void)res; // TODO handle this + doc->getRoot()->c2p *= doc->getRoot()->rotation; } void sp_print_document_to_file(SPDocument *doc, gchar const *filename) diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 7b2507b5e..f2c0d2f2c 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -925,6 +925,15 @@ void SPGroup::update_patheffect(bool write) { } sp_group_perform_patheffect(this, this, write); + + for (PathEffectList::iterator it = this->path_effect_list->begin(); it != this->path_effect_list->end(); ++it) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + + if (lpeobj && lpeobj->get_lpe()) { + lpeobj->get_lpe()->doAfterEffect(this); + } + } } } @@ -950,25 +959,35 @@ sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write) } else { c = subShape->getCurve(); } - + bool success = false; // only run LPEs when the shape has a curve defined if (c) { c->transform(i2anc_affine(subitem, topgroup)); - topgroup->performPathEffect(c); + success = topgroup->performPathEffect(c, subShape); c->transform(i2anc_affine(subitem, topgroup).inverse()); - subShape->setCurve(c, TRUE); - - if (write) { - Inkscape::XML::Node *repr = subitem->getRepr(); - gchar *str = sp_svg_write_path(c->get_pathvector()); - repr->setAttribute("d", str); + Inkscape::XML::Node *repr = subitem->getRepr(); + if (c && success) { + subShape->setCurve(c, TRUE); + if (write) { + gchar *str = sp_svg_write_path(c->get_pathvector()); + repr->setAttribute("d", str); #ifdef GROUP_VERBOSE - g_message("sp_group_perform_patheffect writes 'd' attribute"); + g_message("sp_group_perform_patheffect writes 'd' attribute"); #endif - g_free(str); + g_free(str); + } + c->unref(); + } else { + // LPE was unsuccesfull or doeffect stack return null. Read the old 'd'-attribute. + if (gchar const * value = repr->attribute("d")) { + Geom::PathVector pv = sp_svg_read_pathv(value); + SPCurve *oldcurve = new (std::nothrow) SPCurve(pv); + if (oldcurve) { + subShape->setCurve(oldcurve, TRUE); + oldcurve->unref(); + } + } } - - c->unref(); } } } diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index e2f61bfb5..00671b936 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -24,6 +24,8 @@ #include "live_effects/lpeobject.h" #include "live_effects/lpeobject-reference.h" #include "live_effects/lpe-measure-line.h" +#include "live_effects/lpe-mirror_symmetry.h" +#include "live_effects/lpe-copy_rotate.h" #include "sp-path.h" #include "sp-item-group.h" @@ -126,7 +128,10 @@ void SPLPEItem::set(unsigned int key, gchar const* value) { if (!value) { LivePathEffectObject *lpeobj = (*it)->lpeobject; Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe(); - if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe)){ + if (dynamic_cast<Inkscape::LivePathEffect::LPEMirrorSymmetry *>(lpe) || + dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe) || + dynamic_cast<Inkscape::LivePathEffect::LPECopyRotate *>(lpe) ) + { lpe->doOnRemove(this); } } @@ -209,7 +214,7 @@ Inkscape::XML::Node* SPLPEItem::write(Inkscape::XML::Document *xml_doc, Inkscape /** * returns true when LPE was successful. */ -bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { +bool SPLPEItem::performPathEffect(SPCurve *curve, SPShape *current, bool is_clip_or_mask) { if (!curve) { return false; @@ -241,12 +246,15 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { } if (!is_clip_or_mask || (is_clip_or_mask && lpe->apply_to_clippath_and_mask)) { // Groups have their doBeforeEffect called elsewhere + if (current) { + lpe->setCurrentShape(current); + } if (!SP_IS_GROUP(this)) { lpe->doBeforeEffect_impl(this); } try { - lpe->doEffect(curve); + lpe->doEffect(curve); } catch (std::exception & e) { g_warning("Exception during LPE %s execution. \n %s", lpe->getName().c_str(), e.what()); @@ -257,6 +265,8 @@ bool SPLPEItem::performPathEffect(SPCurve *curve, bool is_clip_or_mask) { return false; } if (!SP_IS_GROUP(this)) { + lpe->pathvector_before_effect = curve->get_pathvector(); + lpe->sp_curve->set_pathvector(lpe->pathvector_before_effect); lpe->doAfterEffect(this); } } @@ -604,7 +614,7 @@ bool SPLPEItem::hasPathEffect() const return true; } -bool SPLPEItem::hasPathEffectOfType(int const type) const +bool SPLPEItem::hasPathEffectOfType(int const type, bool is_ready) const { if (path_effect_list->empty()) { return false; @@ -616,7 +626,9 @@ bool SPLPEItem::hasPathEffectOfType(int const type) const if (lpeobj) { Inkscape::LivePathEffect::Effect const* lpe = lpeobj->get_lpe(); if (lpe && (lpe->effectType() == type)) { - return true; + if (is_ready || lpe->isReady()) { + return true; + } } } } @@ -695,10 +707,10 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) try { if(SP_IS_GROUP(this)){ c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this))); - success = this->performPathEffect(c, true); + success = this->performPathEffect(c, SP_SHAPE(clip_mask), true); c->transform(i2anc_affine(SP_GROUP(item), SP_GROUP(this)).inverse()); } else { - success = this->performPathEffect(c, true); + success = this->performPathEffect(c, SP_SHAPE(clip_mask), true); } } catch (std::exception & e) { g_warning("Exception during LPE execution. \n %s", e.what()); @@ -709,12 +721,13 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) success = false; } Inkscape::XML::Node *repr = clip_mask->getRepr(); - if (success) { + // This c check allow to not apply LPE if curve is NULL after performPathEffect used in clone.obgets LPE + if (success && c) { gchar *str = sp_svg_write_path(c->get_pathvector()); repr->setAttribute("d", str); g_free(str); } else { - // LPE was unsuccesfull. Read the old 'd'-attribute. + // LPE was unsuccesfull or doeffect stack return null.. Read the old 'd'-attribute. if (gchar const * value = repr->attribute("d")) { Geom::PathVector pv = sp_svg_read_pathv(value); SPCurve *oldcurve = new (std::nothrow) SPCurve(pv); @@ -724,7 +737,9 @@ SPLPEItem::apply_to_clip_or_mask(SPItem *clip_mask, SPItem *item) } } } - c->unref(); + if (c) { + c->unref(); + } } } } diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h index 9e5cb3329..0f198c49c 100644 --- a/src/sp-lpe-item.h +++ b/src/sp-lpe-item.h @@ -23,6 +23,7 @@ class LivePathEffectObject; class SPCurve; +class SPShape; class SPDesktop; namespace Inkscape{ @@ -69,11 +70,11 @@ public: virtual void update_patheffect(bool write); - bool performPathEffect(SPCurve *curve, bool is_clip_or_mask = false); + bool performPathEffect(SPCurve *curve, SPShape *current = NULL, bool is_clip_or_mask = false); bool pathEffectsEnabled() const; bool hasPathEffect() const; - bool hasPathEffectOfType(int const type) const; + bool hasPathEffectOfType(int const type, bool is_ready = true) const; bool hasPathEffectRecursive() const; Inkscape::LivePathEffect::Effect* getPathEffectOfType(int type); Inkscape::LivePathEffect::Effect const* getPathEffectOfType(int type) const; diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index b9526433f..c9e71955f 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -19,7 +19,12 @@ #include "event-log.h" #include <2geom/transforms.h> +#include "display/sp-canvas-group.h" +#include "display/canvas-bpath.h" +#include "display/canvas-temporary-item.h" +#include "display/canvas-temporary-item-list.h" #include "display/canvas-grid.h" +#include "display/curve.h" #include "util/units.h" #include "svg/svg-color.h" #include "xml/repr.h" @@ -29,12 +34,15 @@ #include "desktop-events.h" #include "sp-guide.h" +#include "sp-root.h" #include "sp-item-group.h" #include "sp-namedview.h" #include "preferences.h" #include "desktop.h" +#include "selection.h" +#include "object-set.h" +#include "inkscape.h" #include "conn-avoid-ref.h" // for defaultConnSpacing. -#include "sp-root.h" #include <gtkmm/window.h> using Inkscape::DocumentUndo; @@ -72,6 +80,7 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) { this->pagecolor = 0; this->cx = 0; this->pageshadow = 0; + this->document_rotation = 0; this->window_width = 0; this->window_height = 0; this->window_maximized = 0; @@ -92,9 +101,13 @@ SPNamedView::SPNamedView() : SPObjectGroup(), snap_manager(this) { this->default_layer_id = 0; this->connector_spacing = defaultConnSpacing; + this->page_border_rotated = NULL; } SPNamedView::~SPNamedView() { + if(!this->getViewList().empty()) { // >0 Desktops + this->getViewList()[0]->remove_temporary_canvasitem(this->page_border_rotated); + } } static void sp_namedview_generate_old_grid(SPNamedView * /*nv*/, SPDocument *document, Inkscape::XML::Node *repr) { @@ -212,6 +225,7 @@ void SPNamedView::build(SPDocument *document, Inkscape::XML::Node *repr) { this->readAttr( "inkscape:zoom" ); this->readAttr( "inkscape:cx" ); this->readAttr( "inkscape:cy" ); + this->readAttr( "inkscape:document-rotation" ); this->readAttr( "inkscape:window-width" ); this->readAttr( "inkscape:window-height" ); this->readAttr( "inkscape:window-x" ); @@ -409,6 +423,11 @@ void SPNamedView::set(unsigned int key, const gchar* value) { this->cy = value ? g_ascii_strtod(value, NULL) : HUGE_VAL; // HUGE_VAL means not set this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_INKSCAPE_DOCUMENT_ROTATION: + this->document_rotation = value ? g_ascii_strtod(value, NULL) : 0; + sp_namedview_set_document_rotation(this); + this->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; case SP_ATTR_INKSCAPE_WINDOW_WIDTH: this->window_width = value? atoi(value) : -1; // -1 means not set this->requestModified(SP_OBJECT_MODIFIED_FLAG); @@ -939,6 +958,81 @@ static void sp_namedview_lock_guides(SPNamedView *nv) } } +void sp_namedview_doc_rotate_guides(SPNamedView *nv) +{ + bool saved = DocumentUndo::getUndoSensitive(nv->document); + DocumentUndo::setUndoSensitive(nv->document, false); + SPRoot * root = nv->document->getRoot(); + Geom::Point page_center = root->viewBox.midpoint() * root->vbt; + Geom::Affine rot = Geom::identity(); + rot *= Geom::Translate(page_center).inverse(); + rot *= Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); + rot *= Geom::Translate(page_center); + for(std::vector<SPGuide *>::iterator it=nv->guides.begin();it!=nv->guides.end();++it ) { + Geom::Point const on_line = (*it)->getPoint() * rot ; + (*it)->moveto(on_line, true); + Geom::Affine rot_normal_affine = Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); + Geom::Point const rot_normal = (*it)->getNormal() * rot_normal_affine; + (*it)->set_normal(rot_normal, true); + } + DocumentUndo::setUndoSensitive(nv->document, saved); + nv->document->setModifiedSinceSave(); +} + +void sp_namedview_set_document_rotation(SPNamedView *nv) +{ + if ( nv->document->getRoot()->get_rotation() == nv->document_rotation) return; + if(!nv->getViewList().empty()) { // >0 Desktops + SPDesktop *desktop = nv->getViewList()[0]; + desktop->remove_temporary_canvasitem(nv->page_border_rotated); + SPRoot * root = nv->document->getRoot(); + SPCurve *c = new SPCurve(); + c->moveto(root->viewBox.min()); + c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.min()[Geom::Y])); + c->lineto(Geom::Point(root->viewBox.max()[Geom::X],root->viewBox.max()[Geom::Y])); + c->lineto(Geom::Point(root->viewBox.min()[Geom::X],root->viewBox.max()[Geom::Y])); + c->closepath(); + Geom::Point page_center = root->viewBox.midpoint(); + Geom::PathVector const box = c->get_pathvector(); + Geom::Affine rot = Geom::identity(); + rot *= Geom::Translate(page_center).inverse(); + rot *= Geom::Rotate(Geom::rad_from_deg(nv->document_rotation * -1)); + rot *= Geom::Translate(page_center); + if (nv->document_rotation) { + SPCanvasItem *canvas_border = sp_canvas_bpath_new(desktop->getTempGroup(), c, true); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvas_border), 0xFF00009A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvas_border), 0, SP_WIND_RULE_NONZERO); + sp_canvas_item_affine_absolute(canvas_border, rot * root->vbt); + nv->page_border_rotated = desktop->add_temporary_canvasitem(canvas_border, 0); + } + sp_namedview_doc_rotate_guides(nv); + nv->document->getRoot()->set_rotation(nv->document_rotation); + c->unref(); + } + if (nv->document_rotation) { + nv->showborder = FALSE; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + nv->showborder = prefs->getBool("/template/base/showborder", 1.0); + } + + SPDesktop * desktop = SP_ACTIVE_DESKTOP; + if (desktop) { +//TODO: Remove knots of shapes on selected items +// Inkscape::Selection * sel = desktop->getSelection(); +// std::vector<SPItem*> il(sel->items().begin(), sel->items().end()); +// for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ +// SPItem *item = *l; +// sel->remove(item->getRepr()); +// sel->add(item->getRepr()); +// } + SPObject *updated = desktop->getDocument()->getRoot(); + if (updated) { + updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } + } +} + static void sp_namedview_show_single_guide(SPGuide* guide, bool show) { if (show) { @@ -953,6 +1047,8 @@ static void sp_namedview_lock_single_guide(SPGuide* guide, bool locked) guide->set_locked(locked, true); } + + void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr) { unsigned int v; diff --git a/src/sp-namedview.h b/src/sp-namedview.h index d8ac1a77e..9d11c13b7 100644 --- a/src/sp-namedview.h +++ b/src/sp-namedview.h @@ -21,6 +21,7 @@ #include "snap.h" #include "document.h" #include "util/units.h" +#include "display/sp-canvas.h" #include <vector> namespace Inkscape { @@ -28,6 +29,9 @@ namespace Inkscape { namespace Util { class Unit; } + namespace Display { + class TemporaryItem; + } } typedef unsigned int guint32; @@ -38,7 +42,7 @@ enum { SP_BORDER_LAYER_TOP }; -class SPNamedView : public SPObjectGroup { +class SPNamedView : public SPObjectGroup{ public: SPNamedView(); virtual ~SPNamedView(); @@ -54,6 +58,7 @@ public: double zoom; double cx; double cy; + double document_rotation; int window_width; int window_height; int window_x; @@ -66,7 +71,7 @@ public: Inkscape::Util::Unit const *display_units; // Units used for the UI (*not* the same as units of SVG coordinates) Inkscape::Util::Unit const *page_size_units; // Only used in "Custom size" part of Document Properties dialog - + Inkscape::Display::TemporaryItem *page_border_rotated; GQuark default_layer_id; double connector_spacing; @@ -121,7 +126,7 @@ SPNamedView const *sp_document_namedview(SPDocument const *document, char const void sp_namedview_window_from_document(SPDesktop *desktop); void sp_namedview_document_from_window(SPDesktop *desktop); void sp_namedview_update_layers_from_document (SPDesktop *desktop); - +void sp_namedview_set_document_rotation(SPNamedView *nv); void sp_namedview_toggle_guides(SPDocument *doc, Inkscape::XML::Node *repr); void sp_namedview_guides_toggle_lock(SPDocument *doc, Inkscape::XML::Node *repr); void sp_namedview_show_grids(SPNamedView *namedview, bool show, bool dirty_document); diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 75f4657ef..0dc301c49 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -775,6 +775,21 @@ void SPObject::appendChild(Inkscape::XML::Node *child) { repr->appendChild(child); } +SPObject* SPObject::nthChild(unsigned index) { + g_assert(this->repr); + if (hasChildren()) { + std::vector<SPObject*> l; + unsigned counter = 0; + for (auto& child: children) { + if (counter == index) { + return &child; + } + counter++; + } + } + return NULL; +} + void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev) { g_assert(this->repr); diff --git a/src/sp-object.h b/src/sp-object.h index 9abbd324b..d145e966b 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -318,6 +318,9 @@ public: SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); } SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); } + SPObject *nthChild(unsigned index); + SPObject const *nthChild(unsigned index) const; + enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; /** diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index 1bb952de4..b7207fd50 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -978,7 +978,9 @@ void Export::onExport () SPNamedView *nv = desktop->getNamedView(); SPDocument *doc = desktop->getDocument(); - + Geom::Affine rot = doc->getRoot()->c2p; + doc->getRoot()->c2p = doc->getRoot()->rotation.inverse() * doc->getRoot()->c2p; + doc->ensureUpToDate(); bool exportSuccessful = false; bool hide = hide_export.get_active (); @@ -1003,6 +1005,7 @@ void Export::onExport () if (num < 1) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No items selected.")); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1094,6 +1097,7 @@ void Export::onExport () if (filename.empty()) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename.")); sp_ui_error_dialog(_("You have to enter a filename")); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1110,6 +1114,7 @@ void Export::onExport () if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The chosen area to be exported is invalid.")); sp_ui_error_dialog(_("The chosen area to be exported is invalid")); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1132,6 +1137,7 @@ void Export::onExport () g_free(safeDir); g_free(error); + doc->getRoot()->c2p *= doc->getRoot()->rotation; return; } @@ -1281,6 +1287,7 @@ void Export::onExport () } } } + doc->getRoot()->c2p *= doc->getRoot()->rotation; } // end of sp_export_export_clicked() /// Called when Browse button is clicked diff --git a/src/ui/dialog/polar-arrange-tab.cpp b/src/ui/dialog/polar-arrange-tab.cpp index c51881a96..9485b6ba3 100644 --- a/src/ui/dialog/polar-arrange-tab.cpp +++ b/src/ui/dialog/polar-arrange-tab.cpp @@ -43,14 +43,14 @@ PolarArrangeTab::PolarArrangeTab(ArrangeDialog *parent_) anchorPointLabel.set_alignment(Gtk::ALIGN_START); pack_start(anchorPointLabel, false, false); - anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Object's bounding box:")); + anchorBoundingBoxRadio.set_label(C_("Polar arrange tab", "Objects' bounding boxes:")); anchorRadioGroup = anchorBoundingBoxRadio.get_group(); anchorBoundingBoxRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed)); pack_start(anchorBoundingBoxRadio, false, false); pack_start(anchorSelector, false, false); - anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Object's rotational center")); + anchorObjectPivotRadio.set_label(C_("Polar arrange tab", "Objects' rotational centers")); anchorObjectPivotRadio.set_group(anchorRadioGroup); anchorObjectPivotRadio.signal_toggled().connect(sigc::mem_fun(*this, &PolarArrangeTab::on_anchor_radio_changed)); pack_start(anchorObjectPivotRadio, false, false); diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index 33015fe11..083a7d0ba 100644 --- a/src/ui/tool/transform-handle-set.cpp +++ b/src/ui/tool/transform-handle-set.cpp @@ -183,6 +183,11 @@ void TransformHandle::ungrabbed(GdkEventButton *) _setState(_state); endTransform(); _th.signal_commit.emit(getCommitEvent()); + + //updates the positions of the nodes + Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context); + ControlPointSelection* selection = nt->_selected_nodes; + selection->setOriginalPoints(); } diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index 8a35882b9..aab256712 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -94,6 +94,7 @@ ToolBase::ToolBase(gchar const *const *cursor_shape, gint hot_x, gint hot_y, boo , _grdrag(NULL) , shape_editor(NULL) , space_panning(false) + , rotating_mode(false) , _delayed_snap_event(NULL) , _dse_callback_in_process(false) , desktop(NULL) @@ -327,99 +328,130 @@ bool ToolBase::root_handler(GdkEvent* event) { static unsigned int panning = 0; static unsigned int panning_cursor = 0; static unsigned int zoom_rb = 0; + static double angle = 0; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); /// @todo REmove redundant /value in preference keys tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); bool allow_panning = prefs->getBool("/options/spacebarpans/value"); + int rotation_snap = 180.0/prefs->getInt("/options/rotationsnapsperpi/value", 12); gint ret = FALSE; switch (event->type) { - case GDK_2BUTTON_PRESS: - if (panning) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); - ret = TRUE; - } else { - /* sp_desktop_dialog(); */ - } - break; - - case GDK_BUTTON_PRESS: - // save drag origin - xp = (gint) event->button.x; - yp = (gint) event->button.y; - within_tolerance = true; - - button_w = Geom::Point(event->button.x, event->button.y); - - switch (event->button.button) { - case 1: - if (this->space_panning) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - if (_uses_snap) { - sp_event_context_discard_delayed_snap_event(this); - } - panning = 1; - - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK - | GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK, NULL, - event->button.time - 1); - + case GDK_2BUTTON_PRESS: + if (panning) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); ret = TRUE; + } else { + /* sp_desktop_dialog(); */ } break; - case 2: - if (event->button.state & GDK_SHIFT_MASK) { - zoom_rb = 2; - } else { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - if (_uses_snap) { - sp_event_context_discard_delayed_snap_event(this); - } - panning = 2; + case GDK_BUTTON_PRESS: + // save drag origin + xp = (gint) event->button.x; + yp = (gint) event->button.y; + within_tolerance = true; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK, NULL, - event->button.time - 1); + button_w = Geom::Point(event->button.x, event->button.y); + switch (event->button.button) { + case 1: + if (this->space_panning) { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + if (_uses_snap) { + sp_event_context_discard_delayed_snap_event(this); + } + panning = 1; - } + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time - 1); - ret = TRUE; - break; + ret = TRUE; + } + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + break; - case 3: - if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - if (_uses_snap) { - sp_event_context_discard_delayed_snap_event(this); + case 2: + if (event->button.state & GDK_CONTROL_MASK) { + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); + desktop->canvas->startRotateTo(desktop->namedview->document_rotation); + this->rotating_mode = true; + this->message_context->set(Inkscape::INFORMATION_MESSAGE, + _("<b>MMB + mouse move</b> to rotate canvas, use modifiers on screen to change snaps")); + } else { + if (event->button.state & GDK_SHIFT_MASK) { + zoom_rb = 2; + } else { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + if (_uses_snap) { + sp_event_context_discard_delayed_snap_event(this); + } + panning = 2; + + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time - 1); + } + ret = TRUE; } - panning = 3; + ret = TRUE; + break; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK - | GDK_POINTER_MOTION_HINT_MASK, NULL, - event->button.time); + case 3: + if ((event->button.state & GDK_SHIFT_MASK) || (event->button.state & GDK_CONTROL_MASK)) { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + if (_uses_snap) { + sp_event_context_discard_delayed_snap_event(this); + } + panning = 3; + + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time); + ret = TRUE; + } else if( !this->space_panning) { + sp_event_root_menu_popup(desktop, NULL, event); + } ret = TRUE; - } else { - sp_event_root_menu_popup(desktop, NULL, event); - } - break; + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + break; - default: + default: + break; + } break; - } - break; - case GDK_MOTION_NOTIFY: - if (panning) { - if (panning == 4 && !xp && !yp ) { + case GDK_MOTION_NOTIFY: + if (this->rotating_mode) { + button_w = Geom::Point(event->motion.x, event->motion.y); + Geom::Point const motion_dt(desktop->doc2dt(desktop->w2d(button_w))); + Geom::Rect view = desktop->get_display_area(); + Geom::Point view_center = desktop->doc2dt(view.midpoint()); + Geom::Ray center_ray(motion_dt, view_center); + angle = Geom::deg_from_rad(center_ray.angle()) - 90; + if (event->motion.state & GDK_SHIFT_MASK && event->motion.state & GDK_CONTROL_MASK) { + angle = 0; + } else if(event->motion.state & GDK_CONTROL_MASK) { + angle = floor(angle/rotation_snap) * rotation_snap; + } else if (event->motion.state & GDK_SHIFT_MASK) { + angle = desktop->namedview->document_rotation; + } else if (event->motion.state & GDK_MOD1_MASK) { + //Decimal raw angle + } else { + angle = floor(angle); + } + desktop->canvas->rotateTo(angle); + } else if (panning == 4 && !xp && !yp ) { // <Space> + mouse panning started, save location and grab canvas xp = event->motion.x; yp = event->motion.y; @@ -431,401 +463,465 @@ bool ToolBase::root_handler(GdkEvent* event) { | GDK_POINTER_MOTION_HINT_MASK, NULL, event->motion.time - 1); } + if (panning && !this->rotating_mode) { + if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK)) + || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK)) + || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) { + /* Gdk seems to lose button release for us sometimes :-( */ + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); + ret = TRUE; + } else { + if (within_tolerance && (abs((gint) event->motion.x - xp) + < tolerance) && (abs((gint) event->motion.y - yp) + < tolerance)) { + // do not drag if we're within tolerance from origin + break; + } + + // Once the user has moved farther than tolerance from + // the original location (indicating they intend to move + // the object, not click), then always process the motion + // notify coordinates as given (no snapping back to origin) + within_tolerance = false; + + // gobble subsequent motion events to prevent "sticking" + // when scrolling is slow + gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning + == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK)); + + if (panning_cursor == 0) { + panning_cursor = 1; + this->sp_event_context_set_cursor(GDK_FLEUR); + } + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const moved_w(motion_w - button_w); + this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw + ret = TRUE; + } + } else if (zoom_rb) { + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt(desktop->w2d(motion_w)); - if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK)) - || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK)) - || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK))) { - /* Gdk seems to lose button release for us sometimes :-( */ - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); - ret = TRUE; - } else { if (within_tolerance && (abs((gint) event->motion.x - xp) < tolerance) && (abs((gint) event->motion.y - yp) < tolerance)) { - // do not drag if we're within tolerance from origin - break; + break; // do not drag if we're within tolerance from origin } - // Once the user has moved farther than tolerance from - // the original location (indicating they intend to move - // the object, not click), then always process the motion - // notify coordinates as given (no snapping back to origin) + // Once the user has moved farther than tolerance from the original location + // (indicating they intend to move the object, not click), then always process the + // motion notify coordinates as given (no snapping back to origin) within_tolerance = false; - // gobble subsequent motion events to prevent "sticking" - // when scrolling is slow - gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning - == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK)); - - if (panning_cursor == 0) { - panning_cursor = 1; - this->sp_event_context_set_cursor(GDK_FLEUR); + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->move(motion_dt); + } else { + Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt); } - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const moved_w(motion_w - button_w); - this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw - ret = TRUE; - } - } else if (zoom_rb) { - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const motion_dt(desktop->w2d(motion_w)); - - if (within_tolerance && (abs((gint) event->motion.x - xp) - < tolerance) && (abs((gint) event->motion.y - yp) - < tolerance)) { - break; // do not drag if we're within tolerance from origin + if (zoom_rb == 2) { + gobble_motion_events(GDK_BUTTON2_MASK); + } } + break; - // Once the user has moved farther than tolerance from the original location - // (indicating they intend to move the object, not click), then always process the - // motion notify coordinates as given (no snapping back to origin) - within_tolerance = false; - - if (Inkscape::Rubberband::get(desktop)->is_started()) { - Inkscape::Rubberband::get(desktop)->move(motion_dt); + case GDK_BUTTON_RELEASE: + if (this->rotating_mode && event->button.button == 2) { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + ret = TRUE; + if (desktop->canvas->endRotateTo()) { + sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle); + } } else { - Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt); - } + xp = yp = 0; + if (panning_cursor == 1) { + panning_cursor = 0; + GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); + gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); + } - if (zoom_rb == 2) { - gobble_motion_events(GDK_BUTTON2_MASK); - } - } - break; + if (within_tolerance && (panning || zoom_rb)) { + zoom_rb = 0; - case GDK_BUTTON_RELEASE: - xp = yp = 0; + if (panning) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); + } - if (panning_cursor == 1) { - panning_cursor = 0; - GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); - gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); - } + Geom::Point const event_w(event->button.x, event->button.y); + Geom::Point const event_dt(desktop->w2d(event_w)); - if (within_tolerance && (panning || zoom_rb)) { - zoom_rb = 0; + double const zoom_inc = prefs->getDoubleLimited( + "/options/zoomincrement/value", M_SQRT2, 1.01, 10); - if (panning) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - } + desktop->zoom_relative_keep_point(event_dt, (event->button.state + & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc); - Geom::Point const event_w(event->button.x, event->button.y); - Geom::Point const event_dt(desktop->w2d(event_w)); + desktop->updateNow(); + ret = TRUE; + } else if (panning == event->button.button) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); - double const zoom_inc = prefs->getDoubleLimited( - "/options/zoomincrement/value", M_SQRT2, 1.01, 10); + // in slow complex drawings, some of the motion events are lost; + // to make up for this, we scroll it once again to the button-up event coordinates + // (i.e. canvas will always get scrolled all the way to the mouse release point, + // even if few intermediate steps were visible) + Geom::Point const motion_w(event->button.x, event->button.y); + Geom::Point const moved_w(motion_w - button_w); - desktop->zoom_relative_keep_point(event_dt, (event->button.state - & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc); + this->desktop->scroll_world(moved_w); + desktop->updateNow(); + ret = TRUE; + } else if (zoom_rb == event->button.button) { + zoom_rb = 0; - desktop->updateNow(); - ret = TRUE; - } else if (panning == event->button.button) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - - // in slow complex drawings, some of the motion events are lost; - // to make up for this, we scroll it once again to the button-up event coordinates - // (i.e. canvas will always get scrolled all the way to the mouse release point, - // even if few intermediate steps were visible) - Geom::Point const motion_w(event->button.x, event->button.y); - Geom::Point const moved_w(motion_w - button_w); - - this->desktop->scroll_world(moved_w); - desktop->updateNow(); - ret = TRUE; - } else if (zoom_rb == event->button.button) { - zoom_rb = 0; + Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); + Inkscape::Rubberband::get(desktop)->stop(); - Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); - Inkscape::Rubberband::get(desktop)->stop(); + if (b && !within_tolerance) { + desktop->set_display_area(*b, 10); + } - if (b && !within_tolerance) { - desktop->set_display_area(*b, 10); + ret = TRUE; + } + if (this->rotating_mode && event->button.button != 3) { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + ret = TRUE; + desktop->canvas->endRotateTo(); + } } + break; - ret = TRUE; - } - break; - - case GDK_KEY_PRESS: { - double const acceleration = prefs->getDoubleLimited( - "/options/scrollingacceleration/value", 0, 0, 6); - int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", - 10, 0, 1000); - - switch (get_group0_keyval(&event->key)) { - // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets - // in the editing window). So we resteal them back and run our regular shortcut - // invoker on them. - unsigned int shortcut; - case GDK_KEY_Tab: - case GDK_KEY_ISO_Left_Tab: - case GDK_KEY_F1: - shortcut = get_group0_keyval(&event->key); - - if (event->key.state & GDK_SHIFT_MASK) { - shortcut |= SP_SHORTCUT_SHIFT_MASK; + case GDK_KEY_PRESS: { + double const acceleration = prefs->getDoubleLimited( + "/options/scrollingacceleration/value", 0, 0, 6); + int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", + 10, 0, 1000); + + if (this->rotating_mode && + get_group0_keyval(&event->key) != GDK_KEY_space && + get_group0_keyval(&event->key) != GDK_KEY_Shift_L && + get_group0_keyval(&event->key) != GDK_KEY_Shift_R && + get_group0_keyval(&event->key) != GDK_KEY_Control_L && + get_group0_keyval(&event->key) != GDK_KEY_Control_R && + get_group0_keyval(&event->key) != GDK_KEY_Alt_L && + get_group0_keyval(&event->key) != GDK_KEY_Alt_R ) + { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + ret = TRUE; + desktop->canvas->endRotateTo(); + break; } - if (event->key.state & GDK_CONTROL_MASK) { - shortcut |= SP_SHORTCUT_CONTROL_MASK; - } + switch (get_group0_keyval(&event->key)) { + // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets + // in the editing window). So we resteal them back and run our regular shortcut + // invoker on them. + unsigned int shortcut; + case GDK_KEY_Tab: + case GDK_KEY_ISO_Left_Tab: + case GDK_KEY_F1: + shortcut = get_group0_keyval(&event->key); + + if (event->key.state & GDK_SHIFT_MASK) { + shortcut |= SP_SHORTCUT_SHIFT_MASK; + } - if (event->key.state & GDK_MOD1_MASK) { - shortcut |= SP_SHORTCUT_ALT_MASK; - } + if (event->key.state & GDK_CONTROL_MASK) { + shortcut |= SP_SHORTCUT_CONTROL_MASK; + } - ret = sp_shortcut_invoke(shortcut, desktop); - break; + if (event->key.state & GDK_MOD1_MASK) { + shortcut |= SP_SHORTCUT_ALT_MASK; + } - case GDK_KEY_Q: - case GDK_KEY_q: - if (desktop->quick_zoomed()) { - ret = TRUE; - } - if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) { - desktop->zoom_quick(true); - ret = TRUE; - } - break; + ret = sp_shortcut_invoke(shortcut, desktop); + break; - case GDK_KEY_W: - case GDK_KEY_w: - case GDK_KEY_F4: - /* Close view */ - if (MOD__CTRL_ONLY(event)) { - sp_ui_close_view(NULL); - ret = TRUE; - } - break; + case GDK_KEY_Q: + case GDK_KEY_q: + if (desktop->quick_zoomed()) { + ret = TRUE; + } + if (!MOD__SHIFT(event) && !MOD__CTRL(event) && !MOD__ALT(event)) { + desktop->zoom_quick(true); + ret = TRUE; + } + break; - case GDK_KEY_Left: // Ctrl Left - case GDK_KEY_KP_Left: - case GDK_KEY_KP_4: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + case GDK_KEY_W: + case GDK_KEY_w: + case GDK_KEY_F4: + /* Close view */ + if (MOD__CTRL_ONLY(event)) { + sp_ui_close_view(NULL); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(i, 0); - ret = TRUE; - } - break; + case GDK_KEY_Left: // Ctrl Left + case GDK_KEY_KP_Left: + case GDK_KEY_KP_4: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Up: // Ctrl Up - case GDK_KEY_KP_Up: - case GDK_KEY_KP_8: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(i, 0); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(0, i); - ret = TRUE; - } - break; + case GDK_KEY_Up: // Ctrl Up + case GDK_KEY_KP_Up: + case GDK_KEY_KP_8: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Right: // Ctrl Right - case GDK_KEY_KP_Right: - case GDK_KEY_KP_6: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(0, i); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(-i, 0); - ret = TRUE; - } - break; + case GDK_KEY_Right: // Ctrl Right + case GDK_KEY_KP_Right: + case GDK_KEY_KP_6: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Down: // Ctrl Down - case GDK_KEY_KP_Down: - case GDK_KEY_KP_2: - if (MOD__CTRL_ONLY(event)) { - int i = (int) floor(key_scroll * accelerate_scroll(event, - acceleration, desktop->getCanvas())); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(-i, 0); + ret = TRUE; + } + break; - gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(0, -i); - ret = TRUE; - } - break; + case GDK_KEY_Down: // Ctrl Down + case GDK_KEY_KP_Down: + case GDK_KEY_KP_2: + if (MOD__CTRL_ONLY(event)) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, desktop->getCanvas())); - case GDK_KEY_Menu: - sp_event_root_menu_popup(desktop, NULL, event); - ret = TRUE; - break; + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + this->desktop->scroll_world(0, -i); + ret = TRUE; + } + break; - case GDK_KEY_F10: - if (MOD__SHIFT_ONLY(event)) { + case GDK_KEY_Menu: sp_event_root_menu_popup(desktop, NULL, event); ret = TRUE; - } - break; + break; - case GDK_KEY_space: - within_tolerance = true; - xp = yp = 0; - if (!allow_panning) break; - panning = 4; - this->space_panning = true; - this->message_context->set(Inkscape::INFORMATION_MESSAGE, - _("<b>Space+mouse move</b> to pan canvas")); + case GDK_KEY_F10: + if (MOD__SHIFT_ONLY(event)) { + sp_event_root_menu_popup(desktop, NULL, event); + ret = TRUE; + } + break; - ret = TRUE; + case GDK_KEY_space: +// if (event->key.state & GDK_CONTROL_MASK) { +// sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time); +// desktop->canvas->startRotateTo(desktop->namedview->document_rotation); +// this->rotating_mode = true; +// this->message_context->set(Inkscape::INFORMATION_MESSAGE, +// _("<b>Space+mouse move</b> to rotate canvas, use modifiers on screen to change snaps")); +// } else { + within_tolerance = true; + xp = yp = 0; + if (!allow_panning) break; + panning = 4; + this->space_panning = true; + this->message_context->set(Inkscape::INFORMATION_MESSAGE, + _("<b>Space+mouse move</b> to pan canvas")); +// } + ret = TRUE; + break; + + case GDK_KEY_z: + case GDK_KEY_Z: + if (MOD__ALT_ONLY(event)) { + desktop->zoom_grab_focus(); + ret = TRUE; + } + break; + + default: + break; + } + } break; - case GDK_KEY_z: - case GDK_KEY_Z: - if (MOD__ALT_ONLY(event)) { - desktop->zoom_grab_focus(); + case GDK_KEY_RELEASE: + if (this->rotating_mode && + get_group0_keyval(&event->key) != GDK_KEY_space && + get_group0_keyval(&event->key) != GDK_KEY_Shift_L && + get_group0_keyval(&event->key) != GDK_KEY_Shift_R && + get_group0_keyval(&event->key) != GDK_KEY_Control_L && + get_group0_keyval(&event->key) != GDK_KEY_Control_R && + get_group0_keyval(&event->key) != GDK_KEY_Alt_L && + get_group0_keyval(&event->key) != GDK_KEY_Alt_R ) + { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; ret = TRUE; + desktop->canvas->endRotateTo(); + break; } - break; - default: - break; + // Stop panning on any key release + if (this->space_panning) { + this->space_panning = false; + this->message_context->clear(); } - } - break; - case GDK_KEY_RELEASE: - // Stop panning on any key release - if (this->space_panning) { - this->space_panning = false; - this->message_context->clear(); - } + if (panning) { + panning = 0; + xp = yp = 0; - if (panning) { - panning = 0; - xp = yp = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->key.time); - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->key.time); + desktop->updateNow(); + } - desktop->updateNow(); - } + if (panning_cursor == 1) { + panning_cursor = 0; + GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); + gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); + } - if (panning_cursor == 1) { - panning_cursor = 0; - GtkWidget *w = GTK_WIDGET(this->desktop->getCanvas()); - gdk_window_set_cursor(gtk_widget_get_window (w), this->cursor); - } + switch (get_group0_keyval(&event->key)) { + case GDK_KEY_space: +// if (this->rotating_mode) { +// desktop->canvas->clearRotateTo(); +// this->rotating_mode = false; +// ret = TRUE; +// if (desktop->canvas->endRotateTo()) { +// sp_repr_set_svg_double(desktop->namedview->getRepr(), "inkscape:document-rotation", angle); +// } +// } + if (within_tolerance) { + // Space was pressed, but not panned + sp_toggle_selector(desktop); + + // Be careful, sp_toggle_selector will delete ourselves. + // Thus, make sure we return immediately. + return true; + } + break; - switch (get_group0_keyval(&event->key)) { - case GDK_KEY_space: - if (within_tolerance) { - // Space was pressed, but not panned - sp_toggle_selector(desktop); + case GDK_KEY_Q: + case GDK_KEY_q: + if (desktop->quick_zoomed()) { + desktop->zoom_quick(false); + ret = TRUE; + } + break; - // Be careful, sp_toggle_selector will delete ourselves. - // Thus, make sure we return immediately. - return true; + default: + break; } - break; - case GDK_KEY_Q: - case GDK_KEY_q: - if (desktop->quick_zoomed()) { - desktop->zoom_quick(false); - ret = TRUE; + case GDK_SCROLL: { + if (this->rotating_mode) { + desktop->canvas->clearRotateTo(); + this->rotating_mode = false; + desktop->canvas->endRotateTo(); } - break; - - default: - break; - } - break; + bool ctrl = (event->scroll.state & GDK_CONTROL_MASK); + bool wheelzooms = prefs->getBool("/options/wheelzooms/value"); - case GDK_SCROLL: { - bool ctrl = (event->scroll.state & GDK_CONTROL_MASK); - bool wheelzooms = prefs->getBool("/options/wheelzooms/value"); + int const wheel_scroll = prefs->getIntLimited( + "/options/wheelscroll/value", 40, 0, 1000); - int const wheel_scroll = prefs->getIntLimited( - "/options/wheelscroll/value", 40, 0, 1000); + // Size of smooth-scrolls (only used in GTK+ 3) + gdouble delta_x = 0; + gdouble delta_y = 0; - // Size of smooth-scrolls (only used in GTK+ 3) - gdouble delta_x = 0; - gdouble delta_y = 0; - - /* shift + wheel, pan left--right */ - if (event->scroll.state & GDK_SHIFT_MASK) { - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - desktop->scroll_world(wheel_scroll, 0); - break; + /* shift + wheel, pan left--right */ + if (event->scroll.state & GDK_SHIFT_MASK) { + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + desktop->scroll_world(wheel_scroll, 0); + break; - case GDK_SCROLL_DOWN: - desktop->scroll_world(-wheel_scroll, 0); - break; + case GDK_SCROLL_DOWN: + desktop->scroll_world(-wheel_scroll, 0); + break; - default: - break; - } + default: + break; + } - /* ctrl + wheel, zoom in--out */ - } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) { - double rel_zoom; - double const zoom_inc = prefs->getDoubleLimited( - "/options/zoomincrement/value", M_SQRT2, 1.01, 10); + /* ctrl + wheel, zoom in--out */ + } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) { + double rel_zoom; + double const zoom_inc = prefs->getDoubleLimited( + "/options/zoomincrement/value", M_SQRT2, 1.01, 10); - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - rel_zoom = zoom_inc; - break; + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + rel_zoom = zoom_inc; + break; - case GDK_SCROLL_DOWN: - rel_zoom = 1 / zoom_inc; - break; + case GDK_SCROLL_DOWN: + rel_zoom = 1 / zoom_inc; + break; - default: - rel_zoom = 0.0; - break; - } + default: + rel_zoom = 0.0; + break; + } - if (rel_zoom != 0.0) { - Geom::Point const scroll_dt = desktop->point(); - desktop->zoom_relative_keep_point(scroll_dt, rel_zoom); - } + if (rel_zoom != 0.0) { + Geom::Point const scroll_dt = desktop->point(); + desktop->zoom_relative_keep_point(scroll_dt, rel_zoom); + } - /* no modifier, pan up--down (left--right on multiwheel mice?) */ - } else { - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - desktop->scroll_world(0, wheel_scroll); - break; + /* no modifier, pan up--down (left--right on multiwheel mice?) */ + } else { + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + desktop->scroll_world(0, wheel_scroll); + break; - case GDK_SCROLL_DOWN: - desktop->scroll_world(0, -wheel_scroll); - break; + case GDK_SCROLL_DOWN: + desktop->scroll_world(0, -wheel_scroll); + break; - case GDK_SCROLL_LEFT: - desktop->scroll_world(wheel_scroll, 0); - break; + case GDK_SCROLL_LEFT: + desktop->scroll_world(wheel_scroll, 0); + break; - case GDK_SCROLL_RIGHT: - desktop->scroll_world(-wheel_scroll, 0); - break; + case GDK_SCROLL_RIGHT: + desktop->scroll_world(-wheel_scroll, 0); + break; - case GDK_SCROLL_SMOOTH: - gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); - desktop->scroll_world(delta_x, delta_y); - break; + case GDK_SCROLL_SMOOTH: + gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); + desktop->scroll_world(delta_x, delta_y); + break; + } } + break; } - break; - } - default: - break; + default: + break; } - return ret; } diff --git a/src/ui/tools/tool-base.h b/src/ui/tools/tool-base.h index 58eb6f88e..3d22fc66f 100644 --- a/src/ui/tools/tool-base.h +++ b/src/ui/tools/tool-base.h @@ -176,6 +176,7 @@ public: ShapeEditor* shape_editor; bool space_panning; + bool rotating_mode; DelayedSnapEvent *_delayed_snap_event; bool _dse_callback_in_process; diff --git a/src/viewbox.cpp b/src/viewbox.cpp index 1b50fe71c..ebfc04d8a 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -17,6 +17,8 @@ #include "viewbox.h" #include "enums.h" #include "sp-item.h" +#include "inkscape.h" +#include "desktop.h" SPViewBox::SPViewBox() : viewBox_set(false) @@ -25,6 +27,11 @@ SPViewBox::SPViewBox() , aspect_align(SP_ASPECT_XMID_YMID) // Default per spec , aspect_clip(SP_ASPECT_MEET) , c2p(Geom::identity()) + , vbt(Geom::identity()) + , rotation(Geom::identity()) + , angle(0) + , previous_angle(0) + , rotated(false) { } @@ -159,6 +166,16 @@ void SPViewBox::set_preserveAspectRatio(const gchar* value) { } } +double SPViewBox::get_rotation() { + return this->angle; +} + +void SPViewBox::set_rotation(double angle_val) { + this->previous_angle = this->angle; + this->angle = angle_val; + this->rotated = true; +} + // Apply scaling from viewbox void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) { @@ -222,22 +239,41 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) { break; } } - /* Viewbox transform from scale and position */ - Geom::Affine q; - q[0] = scale_x; - q[1] = 0.0; - q[2] = 0.0; - q[3] = scale_y; - q[4] = x - scale_x * this->viewBox.left(); - q[5] = y - scale_y * this->viewBox.top(); - - // std::cout << " q\n" << q << std::endl; - - /* Append viewbox transformation */ - this->c2p = q * this->c2p; + vbt = Geom::identity(); + vbt[0] = scale_x; + vbt[1] = 0.0; + vbt[2] = 0.0; + vbt[3] = scale_y; + vbt[4] = x - scale_x * this->viewBox.left(); + vbt[5] = y - scale_y * this->viewBox.top(); + /* Append viewbox and turn transformation */ + Geom::Point page_center = this->viewBox.midpoint(); + SPDesktop * desktop = SP_ACTIVE_DESKTOP; + if (this->angle > 0.0 || this->angle < 0.0 ) { //!0 + if (desktop) { + rotation = Geom::Translate(page_center).inverse() * Geom::Rotate(Geom::rad_from_deg(angle)) * Geom::Translate(page_center); + this->c2p = rotation * vbt * this->c2p; + } else { + this->c2p = vbt * this->c2p; + } + } else { + this->c2p = vbt * this->c2p; + } + if (desktop && this->rotated) { + Geom::Rect view = desktop->get_display_area(); + Geom::Point view_center = desktop->doc2dt(view.midpoint()); + Geom::Affine center_rotation = Geom::identity(); + center_rotation *= Geom::Translate(page_center * vbt).inverse(); + center_rotation *= Geom::Rotate(Geom::rad_from_deg(this->angle - this->previous_angle)); + center_rotation *= Geom::Translate(page_center * vbt); + view_center = desktop->dt2doc(view_center * center_rotation); + desktop->zoom_relative(view_center[Geom::X], view_center[Geom::Y], 1.0); + this->rotated = false; + } } + SPItemCtx SPViewBox::get_rctx(const SPItemCtx* ictx, double scale_none) { /* Create copy of item context */ diff --git a/src/viewbox.h b/src/viewbox.h index c71abb610..2f107132c 100644 --- a/src/viewbox.h +++ b/src/viewbox.h @@ -36,7 +36,14 @@ public: /* Child to parent additional transform */ Geom::Affine c2p; + Geom::Affine vbt; + Geom::Affine rotation; + double angle; + double previous_angle; + bool rotated; + double get_rotation(); + void set_rotation(double angle_val); void set_viewBox(const gchar* value); void set_preserveAspectRatio(const gchar* value); diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 21bd31299..f0d0541c2 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -54,7 +54,7 @@ #include "ui/uxmanager.h" #include "util/ege-appear-time-tracker.h" #include "sp-root.h" - +#include "attributes.h" // We're in the "widgets" directory, so no need to explicitly prefix these: #include "button.h" #include "gimp/ruler.h" @@ -62,11 +62,11 @@ #include "spw-utilities.h" #include "toolbox.h" #include "widget-sizes.h" - #include "verbs.h" #include <gtkmm/cssprovider.h> #include <gtkmm/paned.h> #include <gtkmm/messagedialog.h> +#include <iomanip> #if defined (SOLARIS) && (SOLARIS == 8) #include "round.h" @@ -105,8 +105,20 @@ static void sp_update_guides_lock( GtkWidget *button, gpointer data ); static void cms_adjust_toggled( GtkWidget *button, gpointer data ); #endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) static void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled ); +static void sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw); static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw); +static gint sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer data); +static bool sp_dtw_rotation_output (GtkSpinButton *spin, gpointer data); +static void sp_dtw_rotation_populate_popup (GtkEntry *entry, GtkMenu *menu, gpointer data); +static void sp_dtw_rotate_minus_180 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_minus_135 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_minus_90 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_minus_45 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_0 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_45 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_90 (GtkMenuItem *item, SPDesktopWidget * data); +static void sp_dtw_rotate_135 (GtkMenuItem *item, SPDesktopWidget * data); static gdouble sp_dtw_zoom_value_to_display (gdouble value); static gdouble sp_dtw_zoom_display_to_value (gdouble value); static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer data); @@ -593,6 +605,34 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw ) g_signal_connect (G_OBJECT (dtw->zoom_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->zoom_status); dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw); dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw); + auto css_provider_spinbutton = Gtk::CssProvider::create(); + css_provider_spinbutton->load_from_data("* { padding-left: 2; padding-right: 2; padding-top: 0; padding-bottom: 0;}"); + auto zoomstat = Glib::wrap(dtw->zoom_status); + zoomstat->set_name("ZoomStatus"); + auto context_zoom = zoomstat->get_style_context(); + context_zoom->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + // Rotate status spinbutton + dtw->rotation_status = gtk_spin_button_new_with_range (-360.0,360.0, 1.0); + gtk_widget_set_tooltip_text (dtw->rotation_status, _("Rotation. Can be interactive with CTRL+MMB")); + gtk_widget_set_size_request (dtw->rotation_status, STATUS_ROTATION_WIDTH, -1); + gtk_entry_set_width_chars (GTK_ENTRY (dtw->rotation_status), 7); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (dtw->rotation_status), FALSE); + gtk_spin_button_set_digits (GTK_SPIN_BUTTON (dtw->rotation_status), 2); + gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (dtw->rotation_status), GTK_UPDATE_ALWAYS); + g_signal_connect (G_OBJECT (dtw->rotation_status), "input", G_CALLBACK (sp_dtw_rotation_input), dtw); + g_signal_connect (G_OBJECT (dtw->rotation_status), "output", G_CALLBACK (sp_dtw_rotation_output), dtw); + g_object_set_data (G_OBJECT (dtw->rotation_status), "dtw", dtw->canvas); + g_signal_connect (G_OBJECT (dtw->rotation_status), "focus-in-event", G_CALLBACK (spinbutton_focus_in), dtw->rotation_status); + g_signal_connect (G_OBJECT (dtw->rotation_status), "key-press-event", G_CALLBACK (spinbutton_keypress), dtw->rotation_status); + dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "value_changed", G_CALLBACK (sp_desktop_widget_rotate_document), dtw); + dtw->rotation_update = g_signal_connect (G_OBJECT (dtw->rotation_status), "populate_popup", G_CALLBACK (sp_dtw_rotation_populate_popup), dtw); + + auto rotstat = Glib::wrap(dtw->rotation_status); + rotstat->set_name("RotationStatus"); + auto context_rotation = rotstat->get_style_context(); + context_rotation->add_provider(css_provider_spinbutton, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + // Cursor coordinates dtw->coord_status = gtk_grid_new(); @@ -619,12 +659,16 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw ) auto label_z = gtk_label_new(_("Z:")); gtk_widget_set_name(label_z, "ZLabel"); + auto label_r = gtk_label_new(_("R:")); + gtk_widget_set_name(label_r, "RLabel"); gtk_widget_set_halign(dtw->coord_status_x, GTK_ALIGN_END); gtk_widget_set_halign(dtw->coord_status_y, GTK_ALIGN_END); gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_x, 2, 0, 1, 1); gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->coord_status_y, 2, 1, 1, 1); gtk_grid_attach(GTK_GRID(dtw->coord_status), label_z, 3, 0, 1, 2); + gtk_grid_attach(GTK_GRID(dtw->coord_status), label_r, 5, 0, 1, 2); gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->zoom_status, 4, 0, 1, 2); + gtk_grid_attach(GTK_GRID(dtw->coord_status), dtw->rotation_status, 6, 0, 1, 2); sp_set_font_size_smaller (dtw->coord_status); @@ -692,6 +736,11 @@ static void sp_desktop_widget_dispose(GObject *object) g_signal_handlers_disconnect_matched (G_OBJECT (dtw->zoom_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->zoom_status); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_value_changed), dtw); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->zoom_status), (gpointer) G_CALLBACK (sp_dtw_zoom_populate_popup), dtw); + g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_input), dtw); + g_signal_handlers_disconnect_by_func(G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK(sp_dtw_rotation_output), dtw); + g_signal_handlers_disconnect_matched (G_OBJECT (dtw->rotation_status), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, dtw->rotation_status); + g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_desktop_widget_rotate_document), dtw); + g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->rotation_status), (gpointer) G_CALLBACK (sp_dtw_rotation_populate_popup), dtw); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas), (gpointer) G_CALLBACK (sp_desktop_widget_event), dtw); g_signal_handlers_disconnect_by_func (G_OBJECT (dtw->canvas_tbl), (gpointer) G_CALLBACK (canvas_tbl_size_allocate), dtw); @@ -1423,7 +1472,7 @@ void SPDesktopWidget::layoutWidgets() } else { gtk_widget_show_all (dtw->menubar); } - + if (!prefs->getBool(pref_root + "commands/state", true)) { gtk_widget_hide (dtw->commands_toolbox); } else { @@ -1629,10 +1678,11 @@ SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview) dtw->menubar = sp_ui_main_menubar (dtw->desktop); gtk_widget_set_name(dtw->menubar, "MenuBar"); gtk_widget_show_all (dtw->menubar); - gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0); - + SPNamedView *nv = dtw->desktop->namedview; + gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, TRUE, TRUE, 0); dtw->layoutWidgets(); - + gtk_spin_button_set_value(GTK_SPIN_BUTTON (dtw->rotation_status), namedview->document_rotation); + sp_namedview_set_document_rotation(namedview); std::vector<GtkWidget *> toolboxes; toolboxes.push_back(dtw->tool_toolbox); toolboxes.push_back(dtw->aux_toolbox); @@ -1672,6 +1722,8 @@ sp_desktop_widget_update_rulers (SPDesktopWidget *dtw) void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) { SPNamedView *nv=SP_NAMEDVIEW(obj); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(this->rotation_status), desktop->namedview->document_rotation); + sp_namedview_set_document_rotation(nv); if (flags & SP_OBJECT_MODIFIED_FLAG) { this->dt2r = 1. / nv->display_units->factor; @@ -1723,6 +1775,18 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) } static void +sp_desktop_widget_rotate_document(GtkSpinButton *spin, SPDesktopWidget *dtw) +{ + SPNamedView *nv = dtw->desktop->namedview; + double value = gtk_spin_button_get_value (spin); + if (!dtw->desktop->getDocument()->getRoot()->rotated && value != nv->document_rotation) { + sp_repr_set_svg_double(nv->getRepr(), "inkscape:document-rotation", value); + } + spinbutton_defocus (GTK_WIDGET(spin)); +} + + +static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment */*adj*/, SPDesktopWidget *dtw) { if (dtw->update) @@ -1802,6 +1866,34 @@ sp_dtw_zoom_output (GtkSpinButton *spin, gpointer /*data*/) return TRUE; } +static gint +sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/) +{ + gdouble new_scrolled = gtk_spin_button_get_value (spin); + const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin)); + gdouble new_typed = atof (b); + + if (new_scrolled == new_typed) { // the new value is set by scrolling + *new_val = new_scrolled; + } else { // the new value is typed in + *new_val = new_typed; + } + + return TRUE; +} + +static bool +sp_dtw_rotation_output (GtkSpinButton *spin, gpointer /*data*/) +{ + gchar b[64]; + double val = gtk_spin_button_get_value (spin); + std::ostringstream s; + s.imbue(std::locale(""));; + s << std::fixed << std::setprecision(2) << val << "º"; + gtk_entry_set_text (GTK_ENTRY (spin), s.str().c_str()); + return TRUE; +} + static void sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data) { @@ -1940,6 +2032,110 @@ sp_dtw_zoom_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data) gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); } + +static void +sp_dtw_rotation_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer data) +{ + GList *children, *iter; + GtkWidget *item; + SPDesktopWidget *dtw = static_cast<SPDesktopWidget*>(data); + children = gtk_container_get_children (GTK_CONTAINER (menu)); + for ( iter = children ; iter ; iter = g_list_next (iter)) { + gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (iter->data)); + } + g_list_free (children); + + item = gtk_menu_item_new_with_label ("-180º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_180), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("-135º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_135), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("-90º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_90), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("-45º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_45), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("0º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_0), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + item = gtk_menu_item_new_with_label ("45º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_45), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + + item = gtk_menu_item_new_with_label ("90º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_90), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + + item = gtk_menu_item_new_with_label ("135º"); + g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_135), dtw); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); +} + +static void +sp_dtw_rotate_minus_180 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status),-180); +} + +static void +sp_dtw_rotate_minus_135 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -135); +} + +static void +sp_dtw_rotate_minus_90 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -90); +} + +static void +sp_dtw_rotate_minus_45 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), -45); +} + +static void +sp_dtw_rotate_0 (GtkMenuItem */*item*/,SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 0); +} + +static void +sp_dtw_rotate_45 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 45); +} + +static void +sp_dtw_rotate_90 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 90); +} + +static void +sp_dtw_rotate_135 (GtkMenuItem */*item*/, SPDesktopWidget * data) +{ + gtk_spin_button_set_value (GTK_SPIN_BUTTON((data)->rotation_status), 135); +} + static void sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor) { diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h index 08966ad5f..61c3b8b37 100644 --- a/src/widgets/desktop-widget.h +++ b/src/widgets/desktop-widget.h @@ -78,7 +78,7 @@ struct SPDesktopWidget { GtkWidget *hbox; - GtkWidget *menubar, *statusbar; + GtkWidget *menubar, *statusbar, *rotatebar; Inkscape::UI::Dialogs::SwatchesPanel *panels; @@ -97,7 +97,9 @@ struct SPDesktopWidget { GtkWidget *select_status; GtkWidget *select_status_eventbox; GtkWidget *zoom_status; + GtkWidget *rotation_status; gulong zoom_update; + gulong rotation_update; Inkscape::UI::Widget::Dock *dock; diff --git a/src/widgets/widget-sizes.h b/src/widgets/widget-sizes.h index 87c7ca2e0..186cf4730 100644 --- a/src/widgets/widget-sizes.h +++ b/src/widgets/widget-sizes.h @@ -28,6 +28,7 @@ #define STATUS_BAR_FONT_SIZE 10000 #define STATUS_ZOOM_WIDTH 57 +#define STATUS_ROTATION_WIDTH 57 #define SELECTED_STYLE_SB_WIDTH 48 #define SELECTED_STYLE_WIDTH 190 |
