diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2010-08-08 17:27:51 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2010-08-08 17:27:51 +0000 |
| commit | 60d3113d1f022a3de7cf04c7979d4751b3fe21f6 (patch) | |
| tree | ca33e2a9a1af6b5911598fa1c6a1d77087b71dd2 /src | |
| parent | Minor cleanups (diff) | |
| parent | Add a constrained snap method that takes multiple constraints. This reduces t... (diff) | |
| download | inkscape-60d3113d1f022a3de7cf04c7979d4751b3fe21f6.tar.gz inkscape-60d3113d1f022a3de7cf04c7979d4751b3fe21f6.zip | |
merge from trunk
(bzr r9508.1.52)
Diffstat (limited to 'src')
150 files changed, 9282 insertions, 1781 deletions
diff --git a/src/2geom/circle.cpp b/src/2geom/circle.cpp index c3cea0ae7..00b91de12 100644 --- a/src/2geom/circle.cpp +++ b/src/2geom/circle.cpp @@ -97,6 +97,23 @@ Circle::arc(Point const& initial, Point const& inner, Point const& final, return e.arc(initial, inner, final, _svg_compliant); } +void +Circle::getPath(std::vector<Path> &path_out) { + Path pb; + + D2<SBasis> B; + Linear bo = Linear(0, 2 * M_PI); + + B[0] = cos(bo,4); + B[1] = sin(bo,4); + + B = B * m_ray + m_centre; + + pb.append(SBasisCurve(B)); + + path_out.push_back(pb); +} + } // end namespace Geom diff --git a/src/2geom/circle.h b/src/2geom/circle.h index 27d4fcc3f..c346b8c8f 100644 --- a/src/2geom/circle.h +++ b/src/2geom/circle.h @@ -38,7 +38,7 @@ #include <2geom/point.h> #include <2geom/exception.h> - +#include <2geom/path.h> namespace Geom { @@ -56,6 +56,11 @@ class Circle { } + Circle(Point center, double r) + : m_centre(center), m_ray(r) + { + } + Circle(double A, double B, double C, double D) { set(A, B, C, D); @@ -86,6 +91,9 @@ class Circle arc(Point const& initial, Point const& inner, Point const& final, bool _svg_compliant = true); + void + getPath(std::vector<Path> &path_out); + Point center() const { return m_centre; diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp index ce5bf89bc..0a5441254 100644 --- a/src/2geom/sbasis-to-bezier.cpp +++ b/src/2geom/sbasis-to-bezier.cpp @@ -99,6 +99,10 @@ int sgn(unsigned int j, unsigned int k) */ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz) { + if (sb.size() == 0) { + THROW_RANGEERROR("size of sb is too small"); + } + size_t q, n; bool even; if (sz == 0) diff --git a/src/Makefile.am b/src/Makefile.am index 3845823f8..ce8113a13 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,6 +45,7 @@ all_libs = \ $(PYTHON_LIBS) \ $(INKBOARD_LIBS) \ $(LIBWPG_LIBS) \ + $(DBUS_LIBS) \ $(IMAGEMAGICK_LIBS) # Add sources common for Inkscape and Inkview to this variable. @@ -53,6 +54,10 @@ ink_common_sources = inkscape_SOURCES = # Add Inkview-only sources here. inkview_SOURCES = +# Add sources that are built from meta files +BUILT_SOURCES = +# Extra files to distribute +EXTRA_DIST = INCLUDES = \ $(PERL_CFLAGS) $(PYTHON_CFLAGS) \ @@ -62,6 +67,7 @@ INCLUDES = \ $(IMAGEMAGICK_CFLAGS) \ $(INKBOARD_CFLAGS) \ $(LIBWPG_CFLAGS) \ + $(DBUS_CFLAGS) \ $(XFT_CFLAGS) \ $(LCMS_CFLAGS) \ $(POPPLER_CFLAGS) \ @@ -72,6 +78,7 @@ INCLUDES = \ $(WIN32_CFLAGS) \ -I$(srcdir)/bind/javainc \ -I$(srcdir)/bind/javainc/linux \ + -I$(builddir)/extension/dbus \ $(AM_CPPFLAGS) CXXTEST_TEMPLATE = $(srcdir)/cxxtest-template.tpl @@ -105,6 +112,7 @@ include dialogs/Makefile_insert include display/Makefile_insert include dom/Makefile_insert include extension/Makefile_insert +include extension/dbus/Makefile_insert include extension/implementation/Makefile_insert include extension/internal/Makefile_insert include extension/script/Makefile_insert @@ -138,7 +146,7 @@ include trace/Makefile_insert include 2geom/Makefile_insert # Extra files not mentioned as sources to include in the source tarball -EXTRA_DIST = \ +EXTRA_DIST += \ $(top_srcdir)/Doxyfile \ sp-skeleton.cpp sp-skeleton.h \ util/makefile.in \ diff --git a/src/attributes.cpp b/src/attributes.cpp index 3cfe04fab..c44a7da4e 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -280,6 +280,7 @@ static SPStyleProp const props[] = { {SP_ATTR_GRADIENTUNITS, "gradientUnits"}, {SP_ATTR_GRADIENTTRANSFORM, "gradientTransform"}, {SP_ATTR_SPREADMETHOD, "spreadMethod"}, + {SP_ATTR_OSB_SWATCH, "osb:paint"}, /* SPRadialGradient */ {SP_ATTR_FX, "fx"}, {SP_ATTR_FY, "fy"}, diff --git a/src/attributes.h b/src/attributes.h index c21087e36..aadb4d165 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -281,6 +281,7 @@ enum SPAttributeEnum { SP_ATTR_GRADIENTUNITS, SP_ATTR_GRADIENTTRANSFORM, SP_ATTR_SPREADMETHOD, + SP_ATTR_OSB_SWATCH, /* SPRadialGradient */ SP_ATTR_FX, SP_ATTR_FY, diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 43709793c..a8238556c 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -2,7 +2,7 @@ # include "config.h" #endif -#define DEBUG_LCMS +#define noDEBUG_LCMS #include <glib/gstdio.h> #include <sys/fcntl.h> diff --git a/src/color.cpp b/src/color.cpp index 07c15ff15..ae1bfa05d 100644 --- a/src/color.cpp +++ b/src/color.cpp @@ -17,7 +17,6 @@ #include <math.h> #include "color.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #include "svg/svg-color.h" #include "svg/css-ostringstream.h" @@ -30,8 +29,7 @@ static bool profileMatches( SVGICCColor const* first, SVGICCColor const* second #define PROFILE_EPSILON 0.00000001 SPColor::SPColor() : - icc(0), - device(0) + icc(0) { v.c[0] = 0; v.c[1] = 0; @@ -39,22 +37,19 @@ SPColor::SPColor() : } SPColor::SPColor( SPColor const& other ) : - icc(0), - device(0) + icc(0) { *this = other; } SPColor::SPColor( float r, float g, float b ) : - icc(0), - device(0) + icc(0) { set( r, g, b ); } SPColor::SPColor( guint32 value ) : - icc(0), - device(0) + icc(0) { set( value ); } @@ -62,16 +57,13 @@ SPColor::SPColor( guint32 value ) : SPColor::~SPColor() { delete icc; - delete device; icc = 0; - device = 0; } SPColor& SPColor::operator= (SPColor const& other) { SVGICCColor* tmp_icc = other.icc ? new SVGICCColor(*other.icc) : 0; - SVGDeviceColor* tmp_device = other.device ? new SVGDeviceColor(*other.device) : 0; v.c[0] = other.v.c[0]; v.c[1] = other.v.c[1]; @@ -81,11 +73,6 @@ SPColor& SPColor::operator= (SPColor const& other) } icc = tmp_icc; - if ( device ) { - delete device; - } - device = tmp_device; - return *this; } @@ -100,7 +87,6 @@ bool SPColor::operator == (SPColor const& other) const && (v.c[2] != other.v.c[2]); match &= profileMatches( icc, other.icc ); -//TODO?: match &= devicecolorMatches( device, other.device ); return match; } @@ -219,38 +205,6 @@ std::string SPColor::toString() const css << ')'; } - if ( device && device->type != DEVICE_COLOR_INVALID) { - if ( !css.str().empty() ) { - css << " "; - } - - switch(device->type){ - case DEVICE_GRAY: - css << "device-gray("; - break; - case DEVICE_RGB: - css << "device-rgb("; - break; - case DEVICE_CMYK: - css << "device-cmyk("; - break; - case DEVICE_NCHANNEL: - css << "device-nchannel("; - break; - case DEVICE_COLOR_INVALID: - //should not be reached - break; - } - - for (vector<double>::const_iterator i(device->colors.begin()), - iEnd(device->colors.end()); - i != iEnd; ++i) { - if (i!=device->colors.begin()) css << ", "; - css << *i; - } - css << ')'; - } - return css.str(); } diff --git a/src/color.h b/src/color.h index 7fd351360..bebeaec60 100644 --- a/src/color.h +++ b/src/color.h @@ -34,7 +34,6 @@ #define SP_RGBA32_F_COMPOSE(r,g,b,a) SP_RGBA32_U_COMPOSE (SP_COLOR_F_TO_U (r), SP_COLOR_F_TO_U (g), SP_COLOR_F_TO_U (b), SP_COLOR_F_TO_U (a)) struct SVGICCColor; -struct SVGDeviceColor; /** * An RGB color with optional icc-color part @@ -60,7 +59,6 @@ struct SPColor { std::string toString() const; SVGICCColor* icc; - SVGDeviceColor* device; union { float c[3]; } v; diff --git a/src/context-fns.cpp b/src/context-fns.cpp index 0ff7bd120..b22cd488d 100644 --- a/src/context-fns.cpp +++ b/src/context-fns.cpp @@ -132,11 +132,11 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item /* Try to snap p[0] (the opposite corner) along the constraint vector */ s[0] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE), - Inkscape::Snapper::ConstraintLine(p[0] - p[1])); + Inkscape::Snapper::SnapConstraint(p[0] - p[1])); /* Try to snap p[1] (the dragged corner) along the constraint vector */ s[1] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), - Inkscape::Snapper::ConstraintLine(p[1] - p[0])); + Inkscape::Snapper::SnapConstraint(p[1] - p[0])); /* Choose the best snap and update points accordingly */ if (s[0].getSnapDistance() < s[1].getSnapDistance()) { @@ -157,7 +157,7 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item /* Our origin is the opposite corner. Snap the drag point along the constraint vector */ p[0] = center; snappoint = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), - Inkscape::Snapper::ConstraintLine(p[1] - p[0])); + Inkscape::Snapper::SnapConstraint(p[1] - p[0])); if (snappoint.getSnapped()) { p[1] = snappoint.getPoint(); } diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index 810f501d7..bb22b0faa 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -296,10 +296,9 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) if (!(event->motion.state & GDK_SHIFT_MASK)) { m.guideConstrainedSnap(motion_dt, *guide); } - } else if (!(event->motion.state & GDK_SHIFT_MASK)) { - if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) { - m.guideFreeSnap(motion_dt, guide->normal_to_line, drag_type); - } + } else if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) { + // cannot use shift here to disable snapping, because we already use it for rotating the guide + m.guideFreeSnap(motion_dt, guide->normal_to_line, drag_type); } switch (drag_type) { @@ -361,10 +360,9 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) if (!(event->button.state & GDK_SHIFT_MASK)) { m.guideConstrainedSnap(event_dt, *guide); } - } else if (!(event->button.state & GDK_SHIFT_MASK)) { - if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) { - m.guideFreeSnap(event_dt, guide->normal_to_line, drag_type); - } + } else if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) { + // cannot use shift here to disable snapping, because we already use it for rotating the guide + m.guideFreeSnap(event_dt, guide->normal_to_line, drag_type); } if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) { diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index f68797d80..55345ee8a 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -44,7 +44,6 @@ #include "desktop-style.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #include "box3d-side.h" /** @@ -436,6 +435,20 @@ stroke_average_width (GSList const *objects) return avgwidth / (g_slist_length ((GSList *) objects) - n_notstroked); } +static bool vectorsClose( std::vector<double> const &lhs, std::vector<double> const &rhs ) +{ + static double epsilon = 1e-6; + bool isClose = false; + if ( lhs.size() == rhs.size() ) { + isClose = true; + for ( size_t i = 0; (i < lhs.size()) && isClose; ++i ) { + isClose = fabs(lhs[i] - rhs[i]) < epsilon; + } + } + return isClose; +} + + /** * Write to style_res the average fill or stroke of list of objects, if applicable. */ @@ -452,7 +465,6 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill paint_res->set = TRUE; SVGICCColor* iccColor = 0; - SVGDeviceColor* devColor = 0; bool iccSeen = false; gfloat c[4]; @@ -536,11 +548,15 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill iccColor = paint->value.color.icc; iccSeen = true; } else { - if (same_color && (prev[0] != d[0] || prev[1] != d[1] || prev[2] != d[2])) + if (same_color && (prev[0] != d[0] || prev[1] != d[1] || prev[2] != d[2])) { same_color = false; - if ( iccSeen ) { - if(paint->value.color.icc) { - // TODO fix this + iccColor = 0; + } + if ( iccSeen && iccColor ) { + if ( !paint->value.color.icc + || (iccColor->colorProfile != paint->value.color.icc->colorProfile) + || !vectorsClose(iccColor->colors, paint->value.color.icc->colors) ) { + same_color = false; iccColor = 0; } } @@ -552,22 +568,6 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill c[2] += d[2]; c[3] += SP_SCALE24_TO_FLOAT (isfill? style->fill_opacity.value : style->stroke_opacity.value); - // average device color - unsigned int it; - if (i==objects /*if this is the first object in the GList*/ - && paint->value.color.device){ - devColor = new SVGDeviceColor(*paint->value.color.device); - for (it=0; it < paint->value.color.device->colors.size(); it++){ - devColor->colors[it] = 0; - } - } - - if (devColor && paint->value.color.device && paint->value.color.device->type == devColor->type){ - for (it=0; it < paint->value.color.device->colors.size(); it++){ - devColor->colors[it] += paint->value.color.device->colors[it]; - } - } - num ++; } @@ -607,14 +607,6 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill paint_res->value.color.icc = tmp; } - // divide and store the device-color - if (devColor){ - for (unsigned int it=0; it < devColor->colors.size(); it++){ - devColor->colors[it] /= num; - } - paint_res->value.color.device = devColor; - } - if (num > 1) { if (same_color) return QUERY_STYLE_MULTIPLE_SAME; diff --git a/src/desktop.cpp b/src/desktop.cpp index 74bf0033a..692cc4112 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -462,9 +462,9 @@ void SPDesktop::displayModeToggle() { _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); break; case Inkscape::RENDERMODE_OUTLINE: - _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); + _setDisplayMode(Inkscape::RENDERMODE_NORMAL); break; - case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW: +// case Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW: default: _setDisplayMode(Inkscape::RENDERMODE_NORMAL); } diff --git a/src/desktop.h b/src/desktop.h index 00f6cfdd5..af2473baf 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -203,9 +203,9 @@ struct SPDesktop : public Inkscape::UI::View::View void setDisplayModeOutline() { _setDisplayMode(Inkscape::RENDERMODE_OUTLINE); } - void setDisplayModePrintColorsPreview() { - _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); - } +// void setDisplayModePrintColorsPreview() { +// _setDisplayMode(Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW); +// } void displayModeToggle(); Inkscape::RenderMode _display_mode; Inkscape::RenderMode getMode() const { return _display_mode; } diff --git a/src/display/nr-arena-item.cpp b/src/display/nr-arena-item.cpp index 9f3863f4d..d9c04ae95 100644 --- a/src/display/nr-arena-item.cpp +++ b/src/display/nr-arena-item.cpp @@ -331,8 +331,11 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area if (!ct) return item->state; #ifdef NR_ARENA_ITEM_VERBOSE - printf ("Invoke render %p: %d %d - %d %d\n", item, area->x0, area->y0, - area->x1, area->y1); + g_message ("Invoke render %p on %p: %d %d - %d %d, %d %d - %d %d", item, pb, + area->x0, area->y0, + area->x1, area->y1, + item->drawbox.x0, item->drawbox.y0, + item->drawbox.x1, item->drawbox.y1); #endif /* If we are invisible, just return successfully */ @@ -487,6 +490,7 @@ nr_arena_item_invoke_render (cairo_t *ct, NRArenaItem *item, NRRectL const *area cairo_pattern_set_matrix(cmask, &m); cairo_mask(ct, cmask); // opacity of masked objects is handled by premultiplying the mask + } else { // opacity of non-masked objects must be rendered explicitly if (needs_opacity) { @@ -763,7 +767,7 @@ nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect &bbox) /** Returns a background image for use with filter effects. */ NRPixBlock * -nr_arena_item_get_background (NRArenaItem const *item, int depth) +nr_arena_item_get_background (NRArenaItem const *item) { return NULL; } diff --git a/src/display/nr-arena-item.h b/src/display/nr-arena-item.h index 752390776..6aa7f9752 100644 --- a/src/display/nr-arena-item.h +++ b/src/display/nr-arena-item.h @@ -171,7 +171,7 @@ void nr_arena_item_set_mask (NRArenaItem *item, NRArenaItem *mask); void nr_arena_item_set_order (NRArenaItem *item, int order); void nr_arena_item_set_item_bbox (NRArenaItem *item, Geom::OptRect &bbox); -NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item, int depth = 0); +NRPixBlock *nr_arena_item_get_background (NRArenaItem const *item); /* Helpers */ diff --git a/src/display/nr-filter-gaussian.cpp b/src/display/nr-filter-gaussian.cpp index fbaa9eaa5..f8a483acb 100644 --- a/src/display/nr-filter-gaussian.cpp +++ b/src/display/nr-filter-gaussian.cpp @@ -551,7 +551,8 @@ void FilterGaussian::render_cairo(FilterSlot &slot) } #if HAVE_OPENMP - int threads = Inkscape::Preferences::get()->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int threads = prefs->getIntLimited("/options/threading/numthreads", omp_get_num_procs(), 1, 256); #else int threads = 1; #endif diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index fe5bd0371..c3198cd37 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -51,7 +51,6 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap g_assert(_desktop != NULL); if (!p.getSnapped()) { - g_warning("No snapping took place, so no snap target will be displayed"); return; // If we haven't snapped, then it is of no use to draw a snapindicator } @@ -229,27 +228,15 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap // Display the snap indicator (i.e. the cross) SPCanvasItem * canvasitem = NULL; - if (p.getTarget() == SNAPTARGET_NODE_SMOOTH || p.getTarget() == SNAPTARGET_NODE_CUSP) { - canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), - SP_TYPE_CTRL, - "anchor", GTK_ANCHOR_CENTER, - "size", 10.0, - "stroked", TRUE, - "stroke_color", pre_snap ? 0x7f7f7fff : 0xff0000ff, - "mode", SP_KNOT_MODE_XOR, - "shape", SP_KNOT_SHAPE_DIAMOND, - NULL ); - } else { - canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), - SP_TYPE_CTRL, - "anchor", GTK_ANCHOR_CENTER, - "size", 10.0, - "stroked", TRUE, - "stroke_color", pre_snap ? 0x7f7f7fff : 0xff0000ff, - "mode", SP_KNOT_MODE_XOR, - "shape", SP_KNOT_SHAPE_CROSS, - NULL ); - } + canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), + SP_TYPE_CTRL, + "anchor", GTK_ANCHOR_CENTER, + "size", 10.0, + "stroked", TRUE, + "stroke_color", pre_snap ? 0x7f7f7fff : 0xff0000ff, + "mode", SP_KNOT_MODE_XOR, + "shape", SP_KNOT_SHAPE_CROSS, + NULL ); const int timeout_val = 1200; // TODO add preference for snap indicator timeout? diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index bfd007cfe..fd3077adb 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -1036,6 +1036,8 @@ sp_canvas_init (SPCanvas *canvas) // See comment at in sp-canvas.h. canvas->gen_all_enter_events = false; + + canvas->drawing_disabled = false; canvas->tiles=NULL; canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0; @@ -2138,6 +2140,9 @@ do_update (SPCanvas *canvas) { if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display! return TRUE; + + if (canvas->drawing_disabled) + return TRUE; /* Cause the update if necessary */ if (canvas->need_update) { diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 6a2ee074e..74476950f 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -180,6 +180,9 @@ struct SPCanvas { // connector tool). If so, they may temporarily set this flag to // 'true'. bool gen_all_enter_events; + + /* For scripting, sometimes we want to delay drawing. */ + bool drawing_disabled; int rendermode; diff --git a/src/document-undo.cpp b/src/document-undo.cpp index ae1c82e71..e63fe8a52 100644 --- a/src/document-undo.cpp +++ b/src/document-undo.cpp @@ -125,11 +125,10 @@ sp_document_done (SPDocument *doc, const unsigned int event_type, Glib::ustring sp_document_maybe_done (doc, NULL, event_type, event_description); } -void -sp_document_reset_key (Inkscape::Application */*inkscape*/, SPDesktop */*desktop*/, GtkObject *base) +void sp_document_reset_key( Inkscape::Application * /*inkscape*/, SPDesktop * /*desktop*/, GtkObject *base ) { - SPDocument *doc = (SPDocument *) base; - doc->actionkey = NULL; + SPDocument *doc = reinterpret_cast<SPDocument *>(base); + doc->actionkey.clear(); } namespace { @@ -171,6 +170,9 @@ sp_document_maybe_done (SPDocument *doc, const gchar *key, const unsigned int ev g_assert (doc != NULL); g_assert (doc->priv != NULL); g_assert (doc->priv->sensitive); + if ( key && !*key ) { + g_warning("Blank undo key specified."); + } Inkscape::Debug::EventTracker<CommitEvent> tracker(doc, key, event_type); @@ -188,7 +190,7 @@ sp_document_maybe_done (SPDocument *doc, const gchar *key, const unsigned int ev return; } - if (key && doc->actionkey && !strcmp (key, doc->actionkey) && doc->priv->undo) { + if (key && !doc->actionkey.empty() && (doc->actionkey == key) && doc->priv->undo) { ((Inkscape::Event *)doc->priv->undo->data)->event = sp_repr_coalesce_log (((Inkscape::Event *)doc->priv->undo->data)->event, log); } else { @@ -198,7 +200,11 @@ sp_document_maybe_done (SPDocument *doc, const gchar *key, const unsigned int ev doc->priv->undoStackObservers.notifyUndoCommitEvent(event); } - doc->actionkey = key; + if ( key ) { + doc->actionkey = key; + } else { + doc->actionkey.clear(); + } doc->virgin = FALSE; doc->setModifiedSinceSave(); @@ -257,7 +263,7 @@ sp_document_undo (SPDocument *doc) doc->priv->sensitive = FALSE; doc->priv->seeking = true; - doc->actionkey = NULL; + doc->actionkey.clear(); finish_incomplete_transaction(*doc); @@ -303,7 +309,7 @@ sp_document_redo (SPDocument *doc) doc->priv->sensitive = FALSE; doc->priv->seeking = true; - doc->actionkey = NULL; + doc->actionkey.clear(); finish_incomplete_transaction(*doc); diff --git a/src/document.cpp b/src/document.cpp index eff6d6e81..3c9f7e5ed 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -92,7 +92,7 @@ SPDocument::SPDocument() : base(0), name(0), priv(0), // reset in ctor - actionkey(0), + actionkey(), modified_id(0), rerouting_handler_id(0), profileManager(0), // deferred until after other initialization @@ -211,7 +211,6 @@ SPDocument::~SPDocument() { inkscape_unref(); keepalive = FALSE; } - //delete this->_whiteboard_session_manager; } @@ -288,7 +287,7 @@ void SPDocument::collectOrphans() { void SPDocument::reset_key (void */*dummy*/) { - actionkey = NULL; + actionkey.clear(); } SPDocument * diff --git a/src/document.h b/src/document.h index e70582006..5810b5358 100644 --- a/src/document.h +++ b/src/document.h @@ -96,7 +96,8 @@ struct SPDocument : public Inkscape::GC::Managed<>, SPDocumentPrivate *priv; /// Last action key - const gchar *actionkey; + Glib::ustring actionkey; + /// Handler ID guint modified_id; diff --git a/src/draw-context.cpp b/src/draw-context.cpp index 4c1c775df..1ecaa5490 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -511,7 +511,7 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p, /* Snap it along best vector */ SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; m.setup(SP_EVENT_CONTEXT_DESKTOP(ec)); - m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::ConstraintLine(best)); + m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::SnapConstraint(best)); } } } @@ -837,7 +837,7 @@ void spdc_create_single_dot(SPEventContext *ec, Geom::Point const &pt, char cons Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Geom::Matrix const i2d (sp_item_i2d_affine (item)); - Geom::Point pp = pt; + Geom::Point pp = pt * i2d.inverse(); double rad = 0.5 * prefs->getDouble(tool_path + "/dot-size", 3.0); if (event_state & GDK_MOD1_MASK) { /* TODO: We vary the dot size between 0.5*rad and 1.5*rad, where rad is the dot size @@ -856,7 +856,6 @@ void spdc_create_single_dot(SPEventContext *ec, Geom::Point const &pt, char cons sp_repr_set_svg_double (repr, "sodipodi:rx", rad * stroke_width); sp_repr_set_svg_double (repr, "sodipodi:ry", rad * stroke_width); item->updateRepr(); - sp_item_set_item_transform(item, i2d.inverse()); sp_desktop_selection(desktop)->set(item); diff --git a/src/extension/dbus/Makefile_insert b/src/extension/dbus/Makefile_insert new file mode 100644 index 000000000..a5eb3fdf4 --- /dev/null +++ b/src/extension/dbus/Makefile_insert @@ -0,0 +1,110 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +if WITH_DBUS + +############################# +# Sources for DBus interface +############################# + +ink_common_sources += \ + extension/dbus/dbus-init.cpp \ + extension/dbus/dbus-init.h \ + extension/dbus/application-interface.cpp \ + extension/dbus/application-interface.h \ + extension/dbus/document-interface.cpp \ + extension/dbus/document-interface.h + +########################### +# Build DBus wrapper files +########################### + +extension/dbus/application-server-glue.h: extension/dbus/application-interface.xml + dbus-binding-tool --mode=glib-server --output=$@ --prefix=application_interface $^ + +extension/dbus/document-server-glue.h: extension/dbus/document-interface.xml + dbus-binding-tool --mode=glib-server --output=$@ --prefix=document_interface $^ + +extension/dbus/document-client-glue.h: extension/dbus/document-interface.xml + dbus-binding-tool --mode=glib-client --output=$@ --prefix=document_interface $^ + +BUILT_SOURCES += \ + extension/dbus/application-server-glue.h \ + extension/dbus/document-server-glue.h \ + extension/dbus/document-client-glue.h + +########################### +# Distribut DBus interface +########################### + +EXTRA_DIST += \ + extension/dbus/application-interface.xml \ + extension/dbus/document-interface.xml + +########################### +# DBus Activation Service +########################### + +# Dbus service file +servicedir = $(DBUSSERVICEDIR) +service_in_files = extension/dbus/org.inkscape.service.in +service_DATA = $(service_in_files:.service.in=.service) + +# Rule to make the service file with bindir expanded +$(service_DATA): $(service_in_files) Makefile + @sed -e "s|bindir|$(prefix)|" $<> $@ + +############################ +# DBus Interface Helper Lib +############################ + +lib_LTLIBRARIES = \ + libinkdbus.la + +libinkdbusincludedir = $(includedir)/libinkdbus-0.48/libinkdbus +libinkdbusinclude_HEADERS = \ + extension/dbus/wrapper/inkscape-dbus-wrapper.h + +libinkdbus_la_SOURCES = \ + extension/dbus/wrapper/inkscape-dbus-wrapper.h \ + extension/dbus/wrapper/inkscape-dbus-wrapper.c + +libinkdbus_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined \ + -export-symbols-regex "^[^_d].*" + +libinkdbus_la_CFLAGS = \ + $(DBUS_CFLAGS) \ + $(INKSCAPE_CFLAGS) \ + -I$(builddir)/extension/dbus \ + -Wall -Werror + +libinkdbus_la_LIBADD = \ + $(DBUS_LIBS) \ + $(INKSCAPE_LIBS) + +############################ +# DBus Pkgconfig file +############################ + +pkgconfig_DATA = extension/dbus/wrapper/inkdbus.pc +pkgconfigdir = $(libdir)/pkgconfig + +else # WITH_DBUS + +EXTRA_DIST += \ + extension/dbus/dbus-init.cpp \ + extension/dbus/dbus-init.h \ + extension/dbus/application-interface.cpp \ + extension/dbus/application-interface.h \ + extension/dbus/document-interface.cpp \ + extension/dbus/document-interface.h \ + extension/dbus/wrapper/inkscape-dbus-wrapper.h \ + extension/dbus/wrapper/inkscape-dbus-wrapper.c \ + extension/dbus/wrapper/inkdbus.pc \ + extension/dbus/org.inkscape.service.in \ + extension/dbus/application-interface.xml \ + extension/dbus/document-interface.xml + +endif + diff --git a/src/extension/dbus/Notes.txt b/src/extension/dbus/Notes.txt new file mode 100644 index 000000000..25c3f35b5 --- /dev/null +++ b/src/extension/dbus/Notes.txt @@ -0,0 +1,79 @@ +INTRO: +For people that are interested in improving the DBus API here is a +intro to how everything is laid out. + +First read the documentation for a general idea of how the different interfaces +fit together and how Dbus is used in this application. + +Here are short descriptions of the relevant files: + +document-interface.cpp: This has most of the "meat" of the interface, this is where +most functions are implemented. + +application-interface.cpp: This is where the application interface is implemented. + +(document/application)-interface.xml: These files are the master record of the interfaces. +All of the documentation is generated from these files as is a lot of glue code. +Any changes MUST be reflected here. + +dbus-init.cpp: This is where the interface is exposed when Inkscape starts up. +Here is where the names given to the various interfaces are set. The application interface is constant but the document interfaces are generated on the fly. + +org.inkscape.service.in: This sets where DBus looks for the Inkscape executable +if it is not running when someone tries to connect. + +pytester.py: A python script that tests a lot of dbus functions. + +doc/builddocs.sh: builds documentation out of the XML files and some others. + +config.xsl, dbus-introspect-docs.dtd, spec-to-docbook.xsl, docbook.css: I borrowed +these files, they set how the documentation looks, I have no idea how to edit them. + +doc/inkscapeDbusRef.xml: This is the top level file for laying out the documentation, +it also includes the introduction. + +doc/inkscapeDbusTerms.xml: This contains the terms sections of the documentation. +Also the overview and all the tutorials. + +*.ref.xml: These are intermediate files, do not edit. + +wrapper/inkscape-dbus-wrapper.c: This is actually completely separate from inkscape. +It has a wrapper for each function in the document interface and includes the +client generated bindings. It is used to create a shared object that will allow +people to use the interface without even knowing anything about Dbus. + +BUGS: + *Inkscape crashes if widow is closed while code is running, + need better error handling. + + *Pause updates needs work. + + *Default style for new shapes is occasionally strange. + + *The following methods are broken: + -document_interface_selection_move_to_layer + + *The following are not implemented: + -document_interface_layer_get_all + -document_interface_selection_box + -document_interface_get_node_coordinates + + *The following do not behave like the documentation: + -document_interface_transform + -document_interface_text + +EFFICIENCY: + *Need better way to retrieve objects by name. + Switch to GQuark codes for object retrieval? + + *Rethink how often activate_desktop needs to be called. + +FEATURES: + *Find out more about extension API. + *API compatibility for plugins? + +CLEANUP: + + + + diff --git a/src/extension/dbus/application-interface.cpp b/src/extension/dbus/application-interface.cpp new file mode 100644 index 000000000..b183be93c --- /dev/null +++ b/src/extension/dbus/application-interface.cpp @@ -0,0 +1,129 @@ +/* + * This is where the implementation of the DBus based application API lives. + * All the methods in here are designed to be called remotly via DBus. + * document-interface.cpp has all of the actual manipulation methods. + * This interface is just for creating new document interfaces. + * + * Documentation for these methods is in application-interface.xml + * which is the "gold standard" as to how the interface should work. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "application-interface.h" +#include <string.h> +#include "dbus-init.h" + +G_DEFINE_TYPE(ApplicationInterface, application_interface, G_TYPE_OBJECT) + +static void +application_interface_finalize (GObject *object) +{ + G_OBJECT_CLASS (application_interface_parent_class)->finalize (object); +} + + +static void +application_interface_class_init (ApplicationInterfaceClass *klass) +{ + GObjectClass *object_class; + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = application_interface_finalize; +} + +static void +application_interface_init (ApplicationInterface *object) +{ +} + + +ApplicationInterface * +application_interface_new (void) +{ + return (ApplicationInterface*)g_object_new (TYPE_APPLICATION_INTERFACE, NULL); +} + +/**************************************************************************** + DESKTOP FUNCTIONS +****************************************************************************/ + +gchar* +application_interface_desktop_new (ApplicationInterface *object, + GError **error) +{ + return (gchar*)Inkscape::Extension::Dbus::init_desktop(); +} + +gchar** +application_interface_get_desktop_list (ApplicationInterface *object) +{ + return NULL; +} + +gchar* +application_interface_get_active_desktop (ApplicationInterface *object, + GError **error) +{ + return NULL; +} + +gboolean +application_interface_set_active_desktop (ApplicationInterface *object, + gchar* document_name, + GError **error) +{ + return TRUE; +} + +gboolean +application_interface_desktop_close_all (ApplicationInterface *object, + GError **error) +{ + return TRUE; +} + +gboolean +application_interface_exit (ApplicationInterface *object, GError **error) +{ + return TRUE; +} + +/**************************************************************************** + DOCUMENT FUNCTIONS +****************************************************************************/ + +gchar* application_interface_document_new (ApplicationInterface *object, + GError **error) +{ + return (gchar*)Inkscape::Extension::Dbus::init_document(); +} + +gchar** +application_interface_get_document_list (ApplicationInterface *object) +{ + return NULL; +} + +gboolean +application_interface_document_close_all (ApplicationInterface *object, + GError **error) +{ + return TRUE; +} + +/* INTERESTING FUNCTIONS + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + g_assert(desktop != NULL); + + SPDocument *doc = sp_desktop_document(desktop); + g_assert(doc != NULL); + + Inkscape::XML::Node *repr = sp_document_repr_root(doc); + g_assert(repr != NULL); +*/ + diff --git a/src/extension/dbus/application-interface.h b/src/extension/dbus/application-interface.h new file mode 100644 index 000000000..e782bd1ad --- /dev/null +++ b/src/extension/dbus/application-interface.h @@ -0,0 +1,101 @@ +/* + * This is where the implementation of the DBus based application API lives. + * All the methods in here are designed to be called remotly via DBus. + * document-interface.cpp has all of the actual manipulation methods. + * This interface is just for creating new document interfaces. + * + * Documentation for these methods is in application-interface.xml + * which is the "gold standard" as to how the interface should work. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_APPLICATION_INTERFACE_H_ +#define INKSCAPE_EXTENSION_APPLICATION_INTERFACE_H_ + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> +#include <dbus/dbus-glib-lowlevel.h> + +#define DBUS_APPLICATION_INTERFACE_PATH "/org/inkscape/application" + +#define TYPE_APPLICATION_INTERFACE (application_interface_get_type ()) +#define APPLICATION_INTERFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_APPLICATION_INTERFACE, ApplicationInterface)) +#define APPLICATION_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_APPLICATION_INTERFACE, ApplicationInterfaceClass)) +#define IS_APPLICATION_INTERFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_APPLICATION_INTERFACE)) +#define IS_APPLICATION_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_APPLICATION_INTERFACE)) +#define APPLICATION_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_APPLICATION_INTERFACE, ApplicationInterfaceClass)) + +G_BEGIN_DECLS + +typedef struct _ApplicationInterface ApplicationInterface; +typedef struct _ApplicationInterfaceClass ApplicationInterfaceClass; + +struct _ApplicationInterface { + GObject parent; +}; + +struct _ApplicationInterfaceClass { + GObjectClass parent; +}; + +/**************************************************************************** + DESKTOP FUNCTIONS +****************************************************************************/ + +gchar* +application_interface_desktop_new (ApplicationInterface *object, + GError **error); + +gchar** +application_interface_get_desktop_list (ApplicationInterface *object); + +gchar* +application_interface_get_active_desktop (ApplicationInterface *object, + GError **error); + +gboolean +application_interface_set_active_desktop (ApplicationInterface *object, + gchar* document_name, + GError **error); + +gboolean +application_interface_desktop_close_all (ApplicationInterface *object, + GError **error); + +gboolean +application_interface_exit (ApplicationInterface *object, GError **error); + +/**************************************************************************** + DOCUMENT FUNCTIONS +****************************************************************************/ + +gchar* +application_interface_document_new (ApplicationInterface *object, + GError **error); + +gchar** +application_interface_get_document_list (ApplicationInterface *object); + +gboolean +application_interface_document_close_all (ApplicationInterface *object, + GError **error); + + +/**************************************************************************** + SETUP +****************************************************************************/ + +ApplicationInterface *application_interface_new (void); +GType application_interface_get_type (void); + + +G_END_DECLS + +#endif // INKSCAPE_EXTENSION_APPLICATION_INTERFACE_H_ diff --git a/src/extension/dbus/application-interface.xml b/src/extension/dbus/application-interface.xml new file mode 100644 index 000000000..ee2c4837b --- /dev/null +++ b/src/extension/dbus/application-interface.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + * This is the master description of the DBus application interface. + * The interface is mostly just for creating new document instances. + * + * This file is used to generate both glue code and documentation. + * The methods are in the same order as the .cpp/.h and the sections are labeled. + * + * Any change to method prototypes in application-interface.cpp MUST be reflected here. + * + * This file is the proverbial gold standard for the application interface. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information +--> +<node name="/org/inkscape/application" + xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" +> + <interface name="org.inkscape.application"> + + <!-- DESKTOP FUNCTIONS --> + <method name="desktop_new"> + <arg type="s" name="desktop_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>This string can be used to connect to the new interface that was created.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Create a new document interface and return it's location.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="get_desktop_list"> + <arg type="as" name="document_list" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> + <doc:doc> + <doc:summary>A list of interfaces being provided by Inkscape.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>List all the interfaces that it is possible to connect to.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="desktop_close_all"> + <doc:doc> + <doc:description> + <doc:para>Close all document interfaces without saving.</doc:para> + </doc:description> + </doc:doc> + </method> + <method name="exit"> + <doc:doc> + <doc:description> + <doc:para>Exit Inkscape without saving. Fairly straightforward. </doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- DOCUMENT FUNCTIONS --> + <method name="document_new"> + <arg type="s" name="document_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>This string can be used to connect to the new interface that was created.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Originally, there were going to be two interfaces. A desktop and a document. Desktops would be used when the user wanted to see the result of their code and documents would be used when less overhead was desired. Unfortunately as more and more of the code can to rely on the desktop and it's associated support code (including selections and verbs) the document interface was looking more and more limited. Ultimately I decided to just go with the desktop interface since I didn't have a compelling reason for keeping the other one and having two similar interfaces could be very confusing. The desktop interface inherited the document name because I believe it's more familiar to people.</doc:para> + <doc:para>Perhaps it would be best to have an option as to whether or not to create a window and fail with a good error message when they call a function that requires one. Or have a second interface for different use cases but have it be completely different, rather than a subset of the first if there are use cases that support it.</doc:para> + </doc:description> + </doc:doc> + </method> + </interface> +</node> diff --git a/src/extension/dbus/builddocs.sh b/src/extension/dbus/builddocs.sh new file mode 100755 index 000000000..2c16daf41 --- /dev/null +++ b/src/extension/dbus/builddocs.sh @@ -0,0 +1,6 @@ +xsltproc doc/spec-to-docbook.xsl application-interface.xml > doc/org.inkscape.application.ref.xml && +xsltproc doc/spec-to-docbook.xsl document-interface.xml > doc/org.inkscape.document.ref.xml && +xsltproc doc/spec-to-docbook.xsl proposed-interface.xml > doc/org.inkscape.proposed.ref.xml && +xmlto --skip-validation xhtml-nochunks -o doc -m doc/config.xsl doc/inkscapeDbusRef.xml && +firefox doc/inkscapeDbusRef.html + diff --git a/src/extension/dbus/dbus-init.cpp b/src/extension/dbus/dbus-init.cpp new file mode 100644 index 000000000..253d6b938 --- /dev/null +++ b/src/extension/dbus/dbus-init.cpp @@ -0,0 +1,170 @@ +/* + * This is where Inkscape connects to the DBus when it starts and + * registers the main interface. + * + * Also where new interfaces are registered when a new document is created. + * (Not called directly by application-interface but called indirectly.) + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <dbus/dbus-glib.h> +#include "dbus-init.h" + +#include "application-interface.h" +#include "application-server-glue.h" + +#include "document-interface.h" +#include "document-server-glue.h" + +#include "inkscape.h" +#include "document.h" +#include "desktop.h" +#include "file.h" +#include "verbs.h" +#include "helper/action.h" + +#include <algorithm> +#include <iostream> +#include <sstream> + + + + +namespace Inkscape { +namespace Extension { +namespace Dbus { + +/* PRIVATE get a connection to the session bus */ +DBusGConnection * +dbus_get_connection() { + GError *error = NULL; + DBusGConnection *connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error) { + fprintf(stderr, "Failed to get connection"); + return NULL; + } + else + return connection; +} + +/* PRIVATE create a proxy object for a bus.*/ +DBusGProxy * +dbus_get_proxy(DBusGConnection *connection) { + return dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); +} + +/* PRIVATE register an object on a bus */ +static gpointer +dbus_register_object (DBusGConnection *connection, + DBusGProxy *proxy, + GType object_type, + const DBusGObjectInfo *info, + const gchar *path) +{ + GObject *object = (GObject*)g_object_new (object_type, NULL); + dbus_g_object_type_install_info (object_type, info); + dbus_g_connection_register_g_object (connection, path, object); + return object; +} + +/* Initialize a Dbus service */ +void +init (void) +{ + guint result; + GError *error = NULL; + DBusGConnection *connection; + DBusGProxy *proxy; + DocumentInterface *obj; + connection = dbus_get_connection(); + proxy = dbus_get_proxy(connection); + org_freedesktop_DBus_request_name (proxy, + "org.inkscape", + DBUS_NAME_FLAG_DO_NOT_QUEUE, &result, &error); + //create interface for application + dbus_register_object (connection, + proxy, + TYPE_APPLICATION_INTERFACE, + &dbus_glib_application_interface_object_info, + DBUS_APPLICATION_INTERFACE_PATH); +} //init + +gchar * +init_document (void) { + guint result; + GError *error = NULL; + DBusGConnection *connection; + DBusGProxy *proxy; + SPDocument *doc; + + doc = sp_document_new(NULL, 1, TRUE); + + std::string name("/org/inkscape/"); + name.append(doc->name); + std::replace(name.begin(), name.end(), ' ', '_'); + + connection = dbus_get_connection(); + proxy = dbus_get_proxy(connection); + + dbus_register_object (connection, + proxy, + TYPE_DOCUMENT_INTERFACE, + &dbus_glib_document_interface_object_info, + name.c_str()); + return strdup(name.c_str()); +} //init_document + +gchar * +dbus_init_desktop_interface (SPDesktop * dt) +{ + DBusGConnection *connection; + DBusGProxy *proxy; + DocumentInterface *obj; + dbus_g_error_domain_register (INKSCAPE_ERROR, + NULL, + INKSCAPE_TYPE_ERROR); + + std::string name("/org/inkscape/desktop_"); + std::stringstream out; + out << dt->dkey; + name.append(out.str()); + + //printf("DKEY: %d\n, NUMBER %d\n NAME: %s\n", dt->dkey, dt->number, name.c_str()); + + connection = dbus_get_connection(); + proxy = dbus_get_proxy(connection); + + obj = (DocumentInterface*) dbus_register_object (connection, + proxy, TYPE_DOCUMENT_INTERFACE, + &dbus_glib_document_interface_object_info, name.c_str()); + obj->desk = dt; + obj->updates = TRUE; + + return strdup(name.c_str()); +} + +gchar * +init_desktop (void) { + //this function will create a new desktop and call + //dbus_init_desktop_interface. + SPDesktop * dt = sp_file_new_default(); + + std::string name("/org/inkscape/desktop_"); + std::stringstream out; + out << dt->dkey; + name.append(out.str()); + return strdup(name.c_str()); +} //init_desktop + + + +} } } /* namespace Inkscape::Extension::Dbus */ diff --git a/src/extension/dbus/dbus-init.h b/src/extension/dbus/dbus-init.h new file mode 100644 index 000000000..4b07acfb4 --- /dev/null +++ b/src/extension/dbus/dbus-init.h @@ -0,0 +1,42 @@ +/* + * Authors: + * Soren Berg <glimmer07@gmail.com> + * + * Copyright (C) 2009 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_DBUS_INIT_H__ +#define INKSCAPE_EXTENSION_DBUS_INIT_H__ + +#include "desktop.h" + +namespace Inkscape { +namespace Extension { +namespace Dbus { + +/** \brief Dbus stuff. For registering objects on the bus. */ + +void init (void); + +gchar * init_document (void); + +gchar * init_desktop (void); + +gchar * dbus_init_desktop_interface (SPDesktop * dt); + +} } } /* namespace Dbus, Extension, Inkscape */ + +#endif /* INKSCAPE_EXTENSION_DBUS_INIT_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/extension/dbus/doc/config.xsl b/src/extension/dbus/doc/config.xsl new file mode 100644 index 000000000..26949c4ef --- /dev/null +++ b/src/extension/dbus/doc/config.xsl @@ -0,0 +1,7 @@ +<?xml version='1.0'?> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:fo="http://www.w3.org/1999/XSL/Format" + version="1.0"> + <xsl:param name="html.stylesheet" select="'docbook.css'"/> +</xsl:stylesheet> + diff --git a/src/extension/dbus/doc/dbus-introspect-docs.dtd b/src/extension/dbus/doc/dbus-introspect-docs.dtd new file mode 100644 index 000000000..47816713e --- /dev/null +++ b/src/extension/dbus/doc/dbus-introspect-docs.dtd @@ -0,0 +1,33 @@ +<!-- DTD for D-Bus Introspection Documentation --> + +<!ELEMENT doc (summary?,description?,errors?,permission?,since?,deprecated,seealso?)> + +<!ELEMENT summary (#PCDATA|ref)*> +<!ELEMENT description (#PCDATA|para|example)*> +<!ELEMENT errors (error)*> +<!ELEMENT permission (#PCDATA|ref|para)*> +<!ELEMENT since EMPTY> +<!ATTLIST since version CDATA #REQUIRED> +<!ELEMENT deprecated (#PCDATA|ref)> +<!ATTLIST deprecated version CDATA #REQUIRED> +<!ATTLIST deprecated instead CDATA #REQUIRED> +<!ELEMENT seealso (ref+)> + +<!ELEMENT error (#PCDATA|para)*> +<!ATTLIST error name CDATA #REQUIRED> +<!ELEMENT para (#PCDATA|example|code|list|ref)*> +<!ELEMENT example (#PCDATA|para|code|ref)*> +<!ATTLIST language (c|glib|python|shell) #REQUIRED> +<!ATTLIST title CDATA #IMPLIED> +<!ELEMENT list (item*)> +<!ATTLIST list type (bullet|number) #REQUIRED> +<!ELEMENT item (term|definition)*> +<!ELEMENT term (#PCDATA|ref)*> +<!ELEMENT definition (#PCDATA|para)*> + +<!ELEMENT code (#PCDATA)> +<!ATTLIST code lang CDATA #IMPLIED> +<!ELEMENT ref CDATA> +<!ATTLIST ref type (parameter|arg|signal|method|interface) #REQUIRED> +<!ATTLIST ref to CDATA #REQUIRED> + diff --git a/src/extension/dbus/doc/docbook.css b/src/extension/dbus/doc/docbook.css new file mode 100644 index 000000000..aed08dce1 --- /dev/null +++ b/src/extension/dbus/doc/docbook.css @@ -0,0 +1,79 @@ +body +{ + font-family: sans-serif; +} +h1.title +{ +} +.permission +{ + color: #ee0000; + text-decoration: underline; +} +.synopsis, .classsynopsis +{ + background: #eeeeee; + border: solid 1px #aaaaaa; + padding: 0.5em; +} +.programlisting +{ + background: #eeeeff; + border: solid 1px #aaaaff; + padding: 0.5em; +} +.variablelist +{ + padding: 4px; + margin-left: 3em; +} +.variablelist td:first-child +{ + vertical-align: top; +} +td.shortcuts +{ + color: #770000; + font-size: 80%; +} +div.refnamediv +{ + margin-top: 2em; +} +div.toc +{ + border: 2em; +} +a +{ + text-decoration: none; +} +a:hover +{ + text-decoration: underline; + color: #FF0000; +} + +div.table table +{ + border-collapse: collapse; + border-spacing: 0px; + border-style: solid; + border-color: #777777; + border-width: 1px; +} + +div.table table td, div.table table th +{ + border-style: solid; + border-color: #777777; + border-width: 1px; + padding: 3px; + vertical-align: top; +} + +div.table table th +{ + background-color: #eeeeee; +} + diff --git a/src/extension/dbus/doc/inkscapeDbusRef.xml b/src/extension/dbus/doc/inkscapeDbusRef.xml new file mode 100644 index 000000000..6bf134a60 --- /dev/null +++ b/src/extension/dbus/doc/inkscapeDbusRef.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +<!ENTITY dbus-Application SYSTEM "org.inkscape.application.ref.xml"> +<!ENTITY dbus-Document SYSTEM "org.inkscape.document.ref.xml"> +<!ENTITY dbus-Proposed SYSTEM "org.inkscape.proposed.ref.xml"> +<!ENTITY Terms SYSTEM "inkscapeDbusTerms.xml"> + +]> + +<book id="index"> + <bookinfo> + <title>Inkscape Dbus Documentation</title> + <releaseinfo>Version 0.0</releaseinfo> + <date>7 July, 2009</date> + <authorgroup> + <author> + <firstname>Soren</firstname> + <surname>Berg</surname> + <affiliation> + <address> + <email>glimmer07@gmail.com</email> + </address> + </affiliation> + </author> + </authorgroup> + </bookinfo> + + <preface> + <title>Introduction</title> + <para> +This is the documentation for scripting Inkscape using Dbus. This framework was developed to let users quickly and easily write scripts to create or manipulate images in a variety of languages. Once the API has stabilized there will also be a C library that encapsulates the Dbus functionality. + </para> + <para> +The guiding principles behind the design of this API were: + </para> + <para> +<emphasis>Easy to use:</emphasis> Use of insider terms was limited where possible, and many functions have been simplified to provide a easy entry point for beginning users. Ideally one should not need any experience with Inkscape or even vector graphics to begin using the interface. At the same time, functions that can call arbitrary verbs or manipulate nodes and their attributes directly give knowledgeable users some flexibility. + </para> + <para> +<emphasis>Interactive:</emphasis> Since Dbus ties in with the main loop, users can mix scripting and mouse driven actions seamlessly. This allows for some unique uses but more importantly makes it easier for people to learn the API since they can play around with it in a scripting console, or even a simple python shell. + </para> + <para> +<emphasis>Responsive:</emphasis> Since one of the advantages of scripting is the ability to repeat actions many times with great precision it is obvious that the system would have to be fairly fast. The amount of overhead has been limited where possible and functions have been tested for speed. A system to pause updates and only refresh the display after a large number of operations have been completed, ensures that even very complicated renders will not take too long. + </para> + </preface> + + <part> + <title>Concepts</title> + + &Terms; + + </part> + + <part> + <title>Reference</title> + + <reference id="dbus-reference"> + <title>D-Bus API Reference</title> + + <partintro> + <para> + Inkscape provides a D-Bus API for programs to interactivly script vector graphics. + </para> + <para> + This API is not yet stable and is likely to change in the future. + </para> + </partintro> + + &dbus-Application; + &dbus-Document; + &dbus-Proposed; + + </reference> + </part> + + <index> + <title>Index</title> + </index> + +</book> + diff --git a/src/extension/dbus/doc/inkscapeDbusTerms.xml b/src/extension/dbus/doc/inkscapeDbusTerms.xml new file mode 100644 index 000000000..507fcbf24 --- /dev/null +++ b/src/extension/dbus/doc/inkscapeDbusTerms.xml @@ -0,0 +1,142 @@ +<chapter id="connecting"> + <title>Connecting to the API</title> + + <sect1> + <title>Overview</title> + <para> +There are really two Dbus interfaces provided by Inkscape. One is the application interface, which is constant, and allows one to control the Inkscape application as a whole and create new documents or windows. The second is the document interface. A document interface is automatically generated for every open window, and the commands sent to that interface will affect that particular window. + </para> + <para> +So the basic way of connecting goes like this: Connect to the session bus. Connect to the application interface. Request a new document. Connect to the newly created document interface using the name returned in the last step. Manipulate the document however you want (load files, create shapes, save, etc.) After the connection example there is a shortcut that will shorten this process somewhat in certain circumstances. + </para> + </sect1> + <sect1> + <title>Connection example</title> + <para> +Here is a basic example of connecting to the Bus and getting a new document. (In python for now because it's easy to read.) + </para> + <informalexample language="python" title="simple example"> + <programlisting> +import dbus + +#get the session bus. +bus = dbus.SessionBus() + +#get the object for the application. +inkapp = bus.get_object('org.inkscape', + '/org/inkscape/application') + +#request a new desktop. +desk2 = inkapp.desktop_new(dbus_interface='org.inkscape.application') + +#get the object for that desktop. +inkdoc1 = bus.get_object('org.inkscape', desk2) + +#tell it what interface it is using so we don't have to type it for every method. +doc1 = dbus.Interface(inkdoc1, dbus_interface="org.inkscape.document") + +#use! +doc1.rectangle (0,0,100,100) + </programlisting> + </informalexample> + </sect1> + + <sect1> + <title>Shortcut</title> + <para> +Here is a quicker way if you don't need multiple documents open at once. Since Inkscape starts automatically, and it always creates a blank document we can just connect to that. + </para> + <warning><para> +WARNING: This may not always work, it also might connect you to a document that is in use if Inkscape was already running. Only recommended for testing/experimenting. + </para></warning> + <informalexample language="python" title="simple example"> + <programlisting> +import dbus + +#get the session bus. +bus = dbus.SessionBus() + +#get object +inkdoc1 = bus.get_object('org.inkscape', '/org/inkscape/desktop_0') + +#get interface +doc1 = dbus.Interface(inkdoc1, dbus_interface="org.inkscape.document") + +#ta-da +doc1.rectangle (0,0,100,100) + </programlisting> + </informalexample> + </sect1> + +</chapter> + +<chapter id="terms"> + <title>Terminology</title> + + <anchor id="Coordinate System"/> + <sect1> + <title>Coordinate System</title> + <para> +The coordinate system used by this API may be different than what you are used to (although it is standard in the computer graphics industry.) Simply put the origin (0,0) is in the upper left hand corner of the document. X increases to the right and Y increases downwards. Therefore everything with positive coordinates is in the document. + </para> + <para> +For example: (100,100) would be just below and to the right of the top left corner of the document. + </para> + </sect1> + + <anchor id="Selections"/> + <sect1> + <title>Selections</title> + <para> +Selections are extremely useful ways of managing groups of objects and applying effects to all of them at once. Since much of Inkscapes core functionality is built around manipulating selections they are the key to much of this APIs utility. Manipulate the list of selected objects with <link linkend="document.selection_set">selection_set()</link>, <link linkend="document.selection_add">selection_add()</link>, and <link linkend="document.selection_box">selection_box()</link> and then call whatever selection function you need. + </para> + </sect1> + + <anchor id="Groups"/> + <sect1> + <title>Groups</title> + <para> +Groups are collections of objects that are treated as a single object. Groups have their own id and can be passed to any function that accepts an object, though some will not have any effect (groups ignore style for instance.) Groups can be transformed and occupy a single level in their layer. Objects within a group can still be modified using their ids, but this will not have any affect on the other group members. Functions like move_to may not work as expected if used on an object that is part of a group that has a transformation applied. + </para> + </sect1> + + <anchor id="Layers and Levels"/> + <sect1> + <title>Layers and Levels</title> + <para> +The basic idea is that things on top cover up things beneath them. The potentially confusing part is that Inkscape implements this in two ways: layers and levels. Levels are what order objects are in within a single layer. So the highest level object is still below all of the objects in the layer above it. <link linkend="document.layer_change_level">layer_change_level()</link> changes the order of layers and <link linkend="document.selection_change_level">selection_change_level()</link> changes the order of objects within a layer. + </para> + <para> +Changing the level of a selection also deserves some explanation. The <link linkend="document.selection_change_level">selection_change_level()</link> function can work in two ways. It can be absolute, "to_top" and "to_bottom" work like you'd expect, sending the entire selection to the top or bottom of that layer. But it can also be relative. "raise" and "lower" only work if there is another shape overlaping above or beneath the selection at the moment. Also if you have two objects selected and they are both occluded by a third, raising the selection once will only raise the first object in the selection above the third object. In other words selections don't move as a group. + </para> + </sect1> + + <anchor id="Style Strings"/> + <sect1> + <title>Style Strings</title> + <para> +Style strings look something like this: "fill:#ff0000;fill-opacity:1;stroke:#0000ff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none". It is a string of key value pairs that determines the style of a particular object. Style strings affect most objects. They can be set all at once or specific key value pairs can be added one by one. Style strings can also be merged, with the new string replacing key/value pairs that it contains and leaving the rest as they were. One could also think of it as the new string taking any attributes it does not have and adopting them from the old string. + </para> + </sect1> + + <anchor id="Paths"/> + <sect1> + <title>Paths</title> + <para> +A path is a string representing a series of points, and how the line curves between the points. It looks something like this: "m 351.42857,296.64789 a 54.285713,87.14286 0 1 1 -108.57143,0 54.285713,87.14286 0 1 1 108.57143,0 z" and is usually found as an attribute of a shape with the label "d". All shapes except rectangles have this "d" attribute. + </para> + <para> +Just because a shape has a path does not mean it IS a path however. A path object has no attributes except the path and a style. Calling <link linkend="document.object_to_path">object_to_path()</link> will convert any object to a path, stripping away any other attributes except id and style which stay the same. This will not change the visual appearance but you will no longer be able to use shape handles or affect it by changing any attributes except for "style", "d", and "transform". Some functions may require paths. + </para> + </sect1> + + <anchor id="Nodes"/> + <sect1> + <title>Nodes</title> + <para> +To be written. + </para> + </sect1> + +</chapter> + diff --git a/src/extension/dbus/doc/spec-to-docbook.xsl b/src/extension/dbus/doc/spec-to-docbook.xsl new file mode 100644 index 000000000..e200a05e0 --- /dev/null +++ b/src/extension/dbus/doc/spec-to-docbook.xsl @@ -0,0 +1,544 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" + exclude-result-prefixes="doc"> +<!-- + Convert D-Bus Glib xml into DocBook refentries + Copyright (C) 2007 William Jon McCann + License: GPL +--> +<xsl:output method="xml" indent="yes" encoding="UTF-8"/> + +<xsl:template match="/"> + +<xsl:variable name="interface" select="//interface/@name"/> +<xsl:variable name="basename"> + <xsl:call-template name="interface-basename"> + <xsl:with-param name="str" select="$interface"/> + </xsl:call-template> +</xsl:variable> + +<refentry><xsl:attribute name="id"><xsl:value-of select="$basename"/></xsl:attribute> + <refmeta> + <refentrytitle role="top_of_page"><xsl:value-of select="//interface/@name"/></refentrytitle> + </refmeta> + + <refnamediv> + <refname><xsl:value-of select="//interface/@name"/></refname> + <refpurpose><xsl:value-of select="$basename"/> interface</refpurpose> + </refnamediv> + + <refsynopsisdiv role="synopsis"> + <title role="synopsis.title">Methods</title> + <synopsis> + <xsl:call-template name="methods-synopsis"> + <xsl:with-param name="basename" select="$basename"/> + </xsl:call-template> + </synopsis> + </refsynopsisdiv> + + <xsl:choose> + <xsl:when test="count(///signal) > 0"> + <refsect1 role="signal_proto"> + <title role="signal_proto.title">Signals</title> + <synopsis> + <xsl:call-template name="signals-synopsis"> + <xsl:with-param name="basename" select="$basename"/> + </xsl:call-template> + </synopsis> + </refsect1> + </xsl:when> + </xsl:choose> + + <refsect1 role="impl_interfaces"> + <title role="impl_interfaces.title">Implemented Interfaces</title> + <para> + Objects implementing <xsl:value-of select="$interface"/> also implements + org.freedesktop.DBus.Introspectable, + org.freedesktop.DBus.Properties + </para> + </refsect1> + + <xsl:choose> + <xsl:when test="count(///property) > 0"> + <refsect1 role="properties"> + <title role="properties.title">Properties</title> + <synopsis> + <xsl:call-template name="properties-synopsis"> + <xsl:with-param name="basename" select="$basename"/> + </xsl:call-template> + </synopsis> + </refsect1> + </xsl:when> + </xsl:choose> + + <refsect1 role="desc"> + <title role="desc.title">Description</title> + <para> + <xsl:apply-templates select="//interface/doc:doc"/> + </para> + </refsect1> + + <refsect1 role="details"> + <title role="details.title">Details</title> + <xsl:call-template name="method-details"> + <xsl:with-param name="basename" select="$basename"/> + </xsl:call-template> + </refsect1> + + <xsl:choose> + <xsl:when test="count(///signal) > 0"> + <refsect1 role="signals"> + <title role="signals.title">Signal Details</title> + <xsl:call-template name="signal-details"> + <xsl:with-param name="basename" select="$basename"/> + </xsl:call-template> + </refsect1> + </xsl:when> + </xsl:choose> + + <xsl:choose> + <xsl:when test="count(///property) > 0"> + <refsect1 role="property_details"> + <title role="property_details.title">Property Details</title> + <xsl:call-template name="property-details"> + <xsl:with-param name="basename" select="$basename"/> + </xsl:call-template> + </refsect1> + </xsl:when> + </xsl:choose> + +</refentry> +</xsl:template> + + +<xsl:template name="property-doc"> + <xsl:apply-templates select="doc:doc/doc:description"/> + + <variablelist role="params"> + <xsl:for-each select="arg"> +<varlistentry><term><parameter><xsl:value-of select="@name"/></parameter>:</term> +<listitem><simpara><xsl:value-of select="doc:doc/doc:summary"/></simpara></listitem> +</varlistentry> + </xsl:for-each> + </variablelist> + + <xsl:apply-templates select="doc:doc/doc:since"/> + <xsl:apply-templates select="doc:doc/doc:deprecated"/> + <xsl:apply-templates select="doc:doc/doc:permission"/> + <xsl:apply-templates select="doc:doc/doc:seealso"/> +</xsl:template> + + +<xsl:template name="property-details"> + <xsl:param name="basename"/> + <xsl:variable name="longest"> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="@name"/> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="///property"> + <refsect2> + <title><anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$basename"/>:<xsl:value-of select="@name"/></xsl:attribute></anchor>The "<xsl:value-of select="@name"/>" property</title> +<indexterm><primary><xsl:value-of select="@name"/></primary><secondary><xsl:value-of select="$basename"/></secondary></indexterm> +<programlisting>'<xsl:value-of select="@name"/>'<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="2"/></xsl:call-template> +<xsl:call-template name="property-args"><xsl:with-param name="indent" select="string-length(@name) + 2"/></xsl:call-template></programlisting> + </refsect2> + + <xsl:call-template name="property-doc"/> + + </xsl:for-each> +</xsl:template> + +<xsl:template name="signal-doc"> + <xsl:apply-templates select="doc:doc/doc:description"/> + + <variablelist role="params"> + <xsl:for-each select="arg"> +<varlistentry><term><parameter><xsl:value-of select="@name"/></parameter>:</term> +<listitem><simpara><xsl:value-of select="doc:doc/doc:summary"/></simpara></listitem> +</varlistentry> + </xsl:for-each> + </variablelist> + + <xsl:apply-templates select="doc:doc/doc:since"/> + <xsl:apply-templates select="doc:doc/doc:deprecated"/> + <xsl:apply-templates select="doc:doc/doc:permission"/> + <xsl:apply-templates select="doc:doc/doc:seealso"/> +</xsl:template> + +<xsl:template name="signal-details"> + <xsl:param name="basename"/> + <xsl:variable name="longest"> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="@name"/> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="///signal"> + <refsect2> + <title><anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$basename"/>::<xsl:value-of select="@name"/></xsl:attribute></anchor>The <xsl:value-of select="@name"/> signal</title> +<indexterm><primary><xsl:value-of select="@name"/></primary><secondary><xsl:value-of select="$basename"/></secondary></indexterm> +<programlisting><xsl:value-of select="@name"/> (<xsl:call-template name="signal-args"><xsl:with-param name="indent" select="string-length(@name) + 2"/><xsl:with-param name="prefix" select="."/></xsl:call-template>)</programlisting> + </refsect2> + + <xsl:call-template name="signal-doc"/> + + </xsl:for-each> +</xsl:template> + +<xsl:template match="doc:code"> +<programlisting> +<xsl:apply-templates /> +</programlisting> +</xsl:template> + +<xsl:template match="doc:tt"> + <literal> + <xsl:apply-templates /> + </literal> +</xsl:template> + +<xsl:template match="doc:i"> + <emphasis> + <xsl:apply-templates /> + </emphasis> +</xsl:template> + +<xsl:template match="doc:b"> + <emphasis role="bold"> + <xsl:apply-templates /> + </emphasis> +</xsl:template> + +<xsl:template match="doc:ulink"> + <ulink> + <xsl:attribute name="url"><xsl:value-of select="@url"/></xsl:attribute> + <xsl:value-of select="."/> + </ulink> +</xsl:template> + +<xsl:template match="doc:summary"> + <xsl:apply-templates /> +</xsl:template> + +<xsl:template match="doc:example"> +<informalexample> +<xsl:apply-templates /> +</informalexample> +</xsl:template> + +<xsl:template name="listitems-do-term"> + <xsl:param name="str"/> + <xsl:choose> + <xsl:when test="string-length($str) > 0"> + <emphasis role="bold"><xsl:value-of select="$str"/>: </emphasis> + </xsl:when> + </xsl:choose> +</xsl:template> + +<xsl:template name="do-listitems"> + <xsl:for-each select="doc:item"> + <listitem> + <xsl:call-template name="listitems-do-term"><xsl:with-param name="str" select="doc:term"/></xsl:call-template> + <xsl:apply-templates select="doc:definition"/> + </listitem> + </xsl:for-each> +</xsl:template> + +<xsl:template match="doc:list"> + <para> + <xsl:choose> + <xsl:when test="contains(@type,'number')"> + <orderedlist> + <xsl:call-template name="do-listitems"/> + </orderedlist> + </xsl:when> + <xsl:otherwise> + <itemizedlist> + <xsl:call-template name="do-listitems"/> + </itemizedlist> + </xsl:otherwise> + </xsl:choose> + </para> +</xsl:template> + +<xsl:template match="doc:para"> +<para> +<xsl:apply-templates /> +</para> +</xsl:template> + +<xsl:template match="doc:description"> +<xsl:apply-templates /> +</xsl:template> + +<xsl:template match="doc:since"> +<para role="since">Since <xsl:value-of select="@version"/> +</para> +</xsl:template> + +<xsl:template match="doc:deprecated"> + <xsl:variable name="name" select="../../@name"/> + <xsl:variable name="parent"> + <xsl:call-template name="interface-basename"> + <xsl:with-param name="str" select="../../../@name"/>/> + </xsl:call-template> + </xsl:variable> + + <xsl:variable name="type" select="name(../..)"/> + + <para role="deprecated"> + <warning><para><literal><xsl:value-of select="$name"/></literal> is deprecated since version <xsl:value-of select="@version"/> and should not be used in newly-written code. Use + + <xsl:variable name="to"> + <xsl:choose> + <xsl:when test="contains($type,'property')"> + <xsl:value-of select="$parent"/>:<xsl:value-of select="@instead"/> + </xsl:when> + <xsl:when test="contains($type,'signal')"> + <xsl:value-of select="$parent"/>::<xsl:value-of select="@instead"/> + </xsl:when> + <xsl:when test="contains($type,'method')"> + <xsl:value-of select="$parent"/>.<xsl:value-of select="@instead"/> + </xsl:when> + <xsl:when test="contains($type,'interface')"> + <xsl:value-of select="@instead"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="@instead"/> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> + + <xsl:call-template name="create-link"> + <xsl:with-param name="type" select="$type"/> + <xsl:with-param name="to" select="$to"/> + <xsl:with-param name="val" select="@instead"/> + </xsl:call-template> +instead.</para></warning> +</para> +</xsl:template> + +<xsl:template match="doc:permission"> +<para role="permission"> +<xsl:apply-templates /> +</para> +</xsl:template> + +<xsl:template match="doc:errors"> +<para role="errors"> +<xsl:apply-templates /> +</para> +</xsl:template> + +<xsl:template match="doc:seealso"> +<para> +See also: +<xsl:apply-templates /> + +</para> +</xsl:template> + +<xsl:template name="create-link"> + <xsl:param name="type"/> + <xsl:param name="to"/> + <xsl:param name="val"/> + + <xsl:choose> + <xsl:when test="contains($type,'property')"> + <link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><literal><xsl:value-of select="$val"/></literal></link> + </xsl:when> + <xsl:when test="contains($type,'signal')"> + <link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><literal><xsl:value-of select="$val"/></literal></link> + </xsl:when> + <xsl:when test="contains($type,'method')"> + <link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><function><xsl:value-of select="$val"/></function></link> + </xsl:when> + <xsl:when test="contains($type,'interface')"> + <link><xsl:attribute name="linkend"><xsl:value-of select="$to"/></xsl:attribute><xsl:value-of select="$val"/></link> + </xsl:when> + </xsl:choose> +</xsl:template> + +<xsl:template match="doc:ref"> + <xsl:call-template name="create-link"> + <xsl:with-param name="type" select="@type"/> + <xsl:with-param name="to" select="@to"/> + <xsl:with-param name="val" select="."/> + </xsl:call-template> +</xsl:template> + +<xsl:template name="method-doc"> + <xsl:apply-templates select="doc:doc/doc:description"/> + + <variablelist role="params"> + <xsl:for-each select="arg"> +<varlistentry><term><parameter><xsl:value-of select="@name"/></parameter>:</term> +<listitem><simpara><xsl:apply-templates select="doc:doc/doc:summary"/></simpara></listitem> +</varlistentry> + </xsl:for-each> + </variablelist> + + <xsl:apply-templates select="doc:doc/doc:since"/> + <xsl:apply-templates select="doc:doc/doc:deprecated"/> + + <xsl:choose> + <xsl:when test="count(doc:doc/doc:errors) > 0"> + <refsect3> + <title>Errors</title> + <variablelist role="errors"> + <xsl:for-each select="doc:doc/doc:errors/doc:error"> + <varlistentry> + <term><parameter><xsl:value-of select="@name"/></parameter>:</term> + <listitem><simpara><xsl:apply-templates select="."/></simpara></listitem> + </varlistentry> + </xsl:for-each> + </variablelist> + </refsect3> + </xsl:when> + </xsl:choose> + + <xsl:choose> + <xsl:when test="count(doc:doc/doc:permission) > 0"> + <refsect3> + <title>Permissions</title> + <xsl:apply-templates select="doc:doc/doc:permission"/> + </refsect3> + </xsl:when> + </xsl:choose> + + <xsl:apply-templates select="doc:doc/doc:seealso"/> +</xsl:template> + +<xsl:template name="method-details"> + <xsl:param name="basename"/> + <xsl:variable name="longest"> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="@name"/> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="///method"> + <refsect2> + <title><anchor role="function"><xsl:attribute name="id"><xsl:value-of select="$basename"/>.<xsl:value-of select="@name"/></xsl:attribute></anchor><xsl:value-of select="@name"/> ()</title> +<indexterm><primary><xsl:value-of select="@name"/></primary><secondary><xsl:value-of select="$basename"/></secondary></indexterm> +<programlisting><xsl:value-of select="@name"/> (<xsl:call-template name="method-args"><xsl:with-param name="indent" select="string-length(@name) + 2"/><xsl:with-param name="prefix" select="."/></xsl:call-template>)</programlisting> + </refsect2> + + <xsl:call-template name="method-doc"/> + + </xsl:for-each> +</xsl:template> + + +<xsl:template name="properties-synopsis"> + <xsl:param name="basename"/> + <xsl:variable name="longest"> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="///property/@name"/> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="///property"> +<link><xsl:attribute name="linkend"><xsl:value-of select="$basename"/>:<xsl:value-of select="@name"/></xsl:attribute>'<xsl:value-of select="@name"/>'</link><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@name) + 1"/></xsl:call-template> <xsl:call-template name="property-args"><xsl:with-param name="indent" select="$longest + 2"/></xsl:call-template> +</xsl:for-each> +</xsl:template> + + +<xsl:template name="signals-synopsis"> + <xsl:param name="basename"/> + <xsl:variable name="longest"> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="///signal/@name"/> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="///signal"> +<link><xsl:attribute name="linkend"><xsl:value-of select="$basename"/>::<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></link><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@name) + 1"/></xsl:call-template>(<xsl:call-template name="signal-args"><xsl:with-param name="indent" select="$longest + 2"/><xsl:with-param name="prefix" select="///signal"/></xsl:call-template>) +</xsl:for-each> +</xsl:template> + + +<xsl:template name="methods-synopsis"> + <xsl:param name="basename"/> + <xsl:variable name="longest"> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="///method/@name"/> + </xsl:call-template> + </xsl:variable> + <xsl:for-each select="///method"> +<link><xsl:attribute name="linkend"><xsl:value-of select="$basename"/>.<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></link><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@name) + 1"/></xsl:call-template>(<xsl:call-template name="method-args"><xsl:with-param name="indent" select="$longest + 2"/><xsl:with-param name="prefix" select="///method"/></xsl:call-template>) +</xsl:for-each> +</xsl:template> + + +<xsl:template name="method-args"><xsl:param name="indent"/><xsl:param name="prefix"/><xsl:variable name="longest"><xsl:call-template name="find-longest"><xsl:with-param name="set" select="$prefix/arg/@type"/></xsl:call-template></xsl:variable><xsl:for-each select="arg"><xsl:value-of select="@direction"/> +<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="4 - string-length(@direction)"/></xsl:call-template>'<xsl:value-of select="@type"/>'<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@type) + 1"/></xsl:call-template> +<xsl:value-of select="@name"/><xsl:if test="not(position() = last())">, +<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$indent"/></xsl:call-template></xsl:if> +</xsl:for-each> +</xsl:template> + + +<xsl:template name="signal-args"><xsl:param name="indent"/><xsl:param name="prefix"/><xsl:variable name="longest"><xsl:call-template name="find-longest"><xsl:with-param name="set" select="$prefix/arg/@type"/></xsl:call-template></xsl:variable><xsl:for-each select="arg">'<xsl:value-of select="@type"/>'<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$longest - string-length(@type) + 1"/></xsl:call-template> +<xsl:value-of select="@name"/><xsl:if test="not(position() = last())">, +<xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="$indent"/></xsl:call-template></xsl:if> +</xsl:for-each> +</xsl:template> + + +<xsl:template name="property-args"><xsl:param name="indent"/> +<xsl:value-of select="@access"/><xsl:call-template name="pad-spaces"><xsl:with-param name="width" select="9 - string-length(@access) + 1"/></xsl:call-template>'<xsl:value-of select="@type"/>' +</xsl:template> + + +<xsl:template name="pad-spaces"> + <xsl:param name="width"/> + <xsl:variable name="spaces" xml:space="preserve"> </xsl:variable> + <xsl:value-of select="substring($spaces,1,$width)"/> +</xsl:template> + + +<xsl:template name="find-longest"> + <xsl:param name="set"/> + <xsl:param name="index" select="1"/> + <xsl:param name="longest" select="0"/> + + <xsl:choose> + <xsl:when test="$index > count($set)"> + <!--finished looking--> + <xsl:value-of select="$longest"/> + </xsl:when> + <xsl:when test="string-length($set[$index])>$longest"> + <!--found new longest--> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="$set"/> + <xsl:with-param name="index" select="$index + 1"/> + <xsl:with-param name="longest" select="string-length($set[$index])"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <!--this isn't any longer--> + <xsl:call-template name="find-longest"> + <xsl:with-param name="set" select="$set"/> + <xsl:with-param name="index" select="$index + 1"/> + <xsl:with-param name="longest" select="$longest"/> + </xsl:call-template> + </xsl:otherwise> + </xsl:choose> +</xsl:template> + + +<xsl:template name="interface-basename"> + <xsl:param name="str"/> + <xsl:choose> + <xsl:when test="contains($str,'.')"> + <xsl:call-template name="interface-basename"> + <xsl:with-param name="str" select="substring-after($str,'.')"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$str"/> + </xsl:otherwise> + </xsl:choose> +</xsl:template> + +</xsl:stylesheet> + diff --git a/src/extension/dbus/document-interface.cpp b/src/extension/dbus/document-interface.cpp new file mode 100644 index 000000000..b8b0c2ade --- /dev/null +++ b/src/extension/dbus/document-interface.cpp @@ -0,0 +1,1336 @@ +/* + * This is where the implementation of the DBus based document API lives. + * All the methods in here (except in the helper section) are + * designed to be called remotly via DBus. application-interface.cpp + * has the methods used to connect to the bus and get a document instance. + * + * Documentation for these methods is in document-interface.xml + * which is the "gold standard" as to how the interface should work. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "document-interface.h" +#include <string.h> + +#include "verbs.h" +#include "helper/action.h" //sp_action_perform + +#include "inkscape.h" //inkscape_find_desktop_by_dkey, activate desktops + +#include "desktop-handles.h" //sp_desktop_document() +#include "xml/repr.h" //sp_repr_document_new + +#include "sp-object.h" + +#include "document.h" // sp_document_repr_doc + +#include "desktop-style.h" //sp_desktop_get_style + +#include "selection.h" //selection struct +#include "selection-chemistry.h"// lots of selection functions + +#include "sp-ellipse.h" + +#include "layer-fns.h" //LPOS_BELOW + +#include "style.h" //style_write + +#include "file.h" //IO + +#include "extension/system.h" //IO + +#include "extension/output.h" //IO + +#include "print.h" //IO + +#include "live_effects/parameter/text.h" //text +#include "display/canvas-text.h" //text + +//#include "2geom/svg-path-parser.h" //get_node_coordinates + +/**************************************************************************** + HELPER / SHORTCUT FUNCTIONS +****************************************************************************/ + +/* + * This function or the one below it translates the user input for an object + * into Inkscapes internal representation. It is called by almost every + * method so it should be as fast as possible. + * + * (eg turns "rect2234" to an SPObject or Inkscape::XML::Node) + * + * If the internal representation changes (No more 'id' attributes) this is the + * place to adjust things. + */ +Inkscape::XML::Node * +get_repr_by_name (SPDesktop *desk, gchar *name, GError **error) +{ + /* ALTERNATIVE (is this faster if only repr is needed?) + Inkscape::XML::Node *node = sp_repr_lookup_name((doc->root)->repr, name); + */ + Inkscape::XML::Node * node = sp_desktop_document(desk)->getObjectById(name)->repr; + if (!node) + { + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name); + return NULL; + } + return node; +} + +/* + * See comment for get_repr_by_name, above. + */ +SPObject * +get_object_by_name (SPDesktop *desk, gchar *name, GError **error) +{ + SPObject * obj = sp_desktop_document(desk)->getObjectById(name); + if (!obj) + { + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object '%s' not found in document.", name); + return NULL; + } + return obj; +} + +/* + * Tests for NULL strings and throws an appropriate error. + * Every method that takes a string parameter (other than the + * name of an object, that's tested seperatly) should call this. + */ +gboolean +dbus_check_string (gchar *string, GError ** error, const gchar * errorstr) +{ + if (string == NULL) + { + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "%s", errorstr); + return FALSE; + } + return TRUE; +} + +/* + * This is used to return object values to the user + */ +const gchar * +get_name_from_object (SPObject * obj) +{ + return obj->repr->attribute("id"); +} + +/* + * Some verbs (cut, paste) only work on the active layer. + * This makes sure that the document that is about to recive a command is active. + */ +void +desktop_ensure_active (SPDesktop* desk) { + if (desk != SP_ACTIVE_DESKTOP) + inkscape_activate_desktop (desk); + return; +} + +gdouble +selection_get_center_x (Inkscape::Selection *sel){ + NRRect *box = g_new(NRRect, 1);; + box = sel->boundsInDocument(box); + return box->x0 + ((box->x1 - box->x0)/2); +} + +gdouble +selection_get_center_y (Inkscape::Selection *sel){ + NRRect *box = g_new(NRRect, 1);; + box = sel->boundsInDocument(box); + return box->y0 + ((box->y1 - box->y0)/2); +} + +/* + * This function is used along with selection_restore to + * take advantage of functionality provided by a selection + * for a single object. + * + * It saves the current selection and sets the selection to + * the object specified. Any selection verb can be used on the + * object and then selection_restore is called, restoring the + * original selection. + * + * This should be mostly transparent to the user who need never + * know we never bothered to implement it seperatly. Although + * they might see the selection box flicker if used in a loop. + */ +const GSList * +selection_swap(SPDesktop *desk, gchar *name, GError **error) +{ + Inkscape::Selection *sel = sp_desktop_selection(desk); + const GSList *oldsel = g_slist_copy((GSList *)sel->list()); + + sel->set(get_object_by_name(desk, name, error)); + return oldsel; +} + +/* + * See selection_swap, above + */ +void +selection_restore(SPDesktop *desk, const GSList * oldsel) +{ + Inkscape::Selection *sel = sp_desktop_selection(desk); + sel->setList(oldsel); +} + +/* + * Shortcut for creating a Node. + */ +Inkscape::XML::Node * +dbus_create_node (SPDesktop *desk, const gchar *type) +{ + SPDocument * doc = sp_desktop_document (desk); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); + + return xml_doc->createElement(type); +} + +/* + * Called by the shape creation functions. Gets the default style for the doc + * or sets it arbitrarily if none. + * + * There is probably a better way to do this (use the shape tools default styles) + * but I'm not sure how. + */ +gchar * +finish_create_shape (DocumentInterface *object, GError **error, Inkscape::XML::Node *newNode, gchar *desc) +{ + + SPCSSAttr *style = sp_desktop_get_style(object->desk, TRUE); + + if (style) { + newNode->setAttribute("style", sp_repr_css_write_string(style), TRUE); + } + else { + newNode->setAttribute("style", "fill:#0000ff;fill-opacity:1;stroke:#c900b9;stroke-width:0;stroke-miterlimit:0;stroke-opacity:1;stroke-dasharray:none", TRUE); + } + + object->desk->currentLayer()->appendChildRepr(newNode); + object->desk->currentLayer()->updateRepr(); + + if (object->updates) + sp_document_done(sp_desktop_document(object->desk), 0, (gchar *)desc); + //else + //document_interface_pause_updates(object, error); + + return strdup(newNode->attribute("id")); +} + +/* + * This is the code used internally to call all the verbs. + * + * It handles error reporting and update pausing (which needs some work.) + * This is a good place to improve efficiency as it is called a lot. + * + * document_interface_call_verb is similar but is called by the user. + */ +gboolean +dbus_call_verb (DocumentInterface *object, int verbid, GError **error) +{ + SPDesktop *desk2 = object->desk; + desktop_ensure_active (desk2); + + if ( desk2 ) { + Inkscape::Verb *verb = Inkscape::Verb::get( verbid ); + if ( verb ) { + SPAction *action = verb->get_action(desk2); + if ( action ) { + //if (!object->updates) + //document_interface_pause_updates (object, error); + sp_action_perform( action, NULL ); + if (object->updates) + sp_document_done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip())); + //if (!object->updates) + //document_interface_pause_updates (object, error); + return TRUE; + } + } + } + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb failed to execute"); + return FALSE; +} + +/**************************************************************************** + DOCUMENT INTERFACE CLASS STUFF +****************************************************************************/ + +G_DEFINE_TYPE(DocumentInterface, document_interface, G_TYPE_OBJECT) + +static void +document_interface_finalize (GObject *object) +{ + G_OBJECT_CLASS (document_interface_parent_class)->finalize (object); +} + + +static void +document_interface_class_init (DocumentInterfaceClass *klass) +{ + GObjectClass *object_class; + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = document_interface_finalize; +} + +static void +document_interface_init (DocumentInterface *object) +{ + object->desk = NULL; +} + + +DocumentInterface * +document_interface_new (void) +{ + return (DocumentInterface*)g_object_new (TYPE_DOCUMENT_INTERFACE, NULL); +} + +/* + * Error stuff... + * + * To add a new error type, edit here and in the .h InkscapeError enum. + */ +GQuark +inkscape_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("inkscape_error"); + + return quark; +} + +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GType +inkscape_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + + ENUM_ENTRY (INKSCAPE_ERROR_SELECTION, "Incompatible_Selection"), + ENUM_ENTRY (INKSCAPE_ERROR_OBJECT, "Incompatible_Object"), + ENUM_ENTRY (INKSCAPE_ERROR_VERB, "Failed_Verb"), + ENUM_ENTRY (INKSCAPE_ERROR_OTHER, "Generic_Error"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("InkscapeError", values); + } + + return etype; +} + +/**************************************************************************** + MISC FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_delete_all (DocumentInterface *object, GError **error) +{ + sp_edit_clear_all (object->desk); + return TRUE; +} + +gboolean +document_interface_call_verb (DocumentInterface *object, gchar *verbid, GError **error) +{ + SPDesktop *desk2 = object->desk; + desktop_ensure_active (object->desk); + if ( desk2 ) { + Inkscape::Verb *verb = Inkscape::Verb::getbyid( verbid ); + if ( verb ) { + SPAction *action = verb->get_action(desk2); + if ( action ) { + sp_action_perform( action, NULL ); + if (object->updates) { + sp_document_done(sp_desktop_document(desk2), verb->get_code(), g_strdup(verb->get_tip())); + } + } + } + } + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_VERB, "Verb '%s' failed to execute or was not found.", verbid); + return FALSE; +} + + +/**************************************************************************** + CREATION FUNCTIONS +****************************************************************************/ + +gchar* +document_interface_rectangle (DocumentInterface *object, int x, int y, + int width, int height, GError **error) +{ + + + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:rect"); + sp_repr_set_int(newNode, "x", x); //could also use newNode->setAttribute() + sp_repr_set_int(newNode, "y", y); + sp_repr_set_int(newNode, "width", width); + sp_repr_set_int(newNode, "height", height); + return finish_create_shape (object, error, newNode, (gchar *)"create rectangle"); +} + +gchar* +document_interface_ellipse_center (DocumentInterface *object, int cx, int cy, + int rx, int ry, GError **error) +{ + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path"); + newNode->setAttribute("sodipodi:type", "arc"); + sp_repr_set_int(newNode, "sodipodi:cx", cx); + sp_repr_set_int(newNode, "sodipodi:cy", cy); + sp_repr_set_int(newNode, "sodipodi:rx", rx); + sp_repr_set_int(newNode, "sodipodi:ry", ry); + return finish_create_shape (object, error, newNode, (gchar *)"create circle"); +} + +gchar* +document_interface_polygon (DocumentInterface *object, int cx, int cy, + int radius, int rotation, int sides, + GError **error) +{ + gdouble rot = ((rotation / 180.0) * 3.14159265) - ( 3.14159265 / 2.0); + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path"); + newNode->setAttribute("inkscape:flatsided", "true"); + newNode->setAttribute("sodipodi:type", "star"); + sp_repr_set_int(newNode, "sodipodi:cx", cx); + sp_repr_set_int(newNode, "sodipodi:cy", cy); + sp_repr_set_int(newNode, "sodipodi:r1", radius); + sp_repr_set_int(newNode, "sodipodi:r2", radius); + sp_repr_set_int(newNode, "sodipodi:sides", sides); + sp_repr_set_int(newNode, "inkscape:randomized", 0); + sp_repr_set_svg_double(newNode, "sodipodi:arg1", rot); + sp_repr_set_svg_double(newNode, "sodipodi:arg2", rot); + sp_repr_set_svg_double(newNode, "inkscape:rounded", 0); + + return finish_create_shape (object, error, newNode, (gchar *)"create polygon"); +} + +gchar* +document_interface_star (DocumentInterface *object, int cx, int cy, + int r1, int r2, int sides, gdouble rounded, + gdouble arg1, gdouble arg2, GError **error) +{ + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path"); + newNode->setAttribute("inkscape:flatsided", "false"); + newNode->setAttribute("sodipodi:type", "star"); + sp_repr_set_int(newNode, "sodipodi:cx", cx); + sp_repr_set_int(newNode, "sodipodi:cy", cy); + sp_repr_set_int(newNode, "sodipodi:r1", r1); + sp_repr_set_int(newNode, "sodipodi:r2", r2); + sp_repr_set_int(newNode, "sodipodi:sides", sides); + sp_repr_set_int(newNode, "inkscape:randomized", 0); + sp_repr_set_svg_double(newNode, "sodipodi:arg1", arg1); + sp_repr_set_svg_double(newNode, "sodipodi:arg2", arg2); + sp_repr_set_svg_double(newNode, "inkscape:rounded", rounded); + + return finish_create_shape (object, error, newNode, (gchar *)"create star"); +} + +gchar* +document_interface_ellipse (DocumentInterface *object, int x, int y, + int width, int height, GError **error) +{ + int rx = width/2; + int ry = height/2; + return document_interface_ellipse_center (object, x+rx, y+ry, rx, ry, error); +} + +gchar* +document_interface_line (DocumentInterface *object, int x, int y, + int x2, int y2, GError **error) +{ + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path"); + std::stringstream out; + // Not sure why this works. + out << "m " << x << "," << y << " " << x2 - x << "," << y2 - y; + newNode->setAttribute("d", out.str().c_str()); + return finish_create_shape (object, error, newNode, (gchar *)"create line"); +} + +gchar* +document_interface_spiral (DocumentInterface *object, int cx, int cy, + int r, int revolutions, GError **error) +{ + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:path"); + newNode->setAttribute("sodipodi:type", "spiral"); + sp_repr_set_int(newNode, "sodipodi:cx", cx); + sp_repr_set_int(newNode, "sodipodi:cy", cy); + sp_repr_set_int(newNode, "sodipodi:radius", r); + sp_repr_set_int(newNode, "sodipodi:revolution", revolutions); + sp_repr_set_int(newNode, "sodipodi:t0", 0); + sp_repr_set_int(newNode, "sodipodi:argument", 0); + sp_repr_set_int(newNode, "sodipodi:expansion", 1); + gchar * retval = finish_create_shape (object, error, newNode, (gchar *)"create spiral"); + //Makes sure there is no fill for spirals by default. + gchar* newString = g_strconcat(newNode->attribute("style"), ";fill:none", NULL); + newNode->setAttribute("style", newString); + g_free(newString); + return retval; +} + +gboolean +document_interface_text (DocumentInterface *object, int x, int y, gchar *text, GError **error) +{ + //FIXME: Not selectable (aka broken). Needs to be rewritten completely. + + SPDesktop *desktop = object->desk; + SPCanvasText * canvas_text = (SPCanvasText *) sp_canvastext_new(sp_desktop_tempgroup(desktop), desktop, Geom::Point(0,0), ""); + sp_canvastext_set_text (canvas_text, text); + sp_canvastext_set_coords (canvas_text, x, y); + + return TRUE; +} + +gchar * +document_interface_image (DocumentInterface *object, int x, int y, gchar *filename, GError **error) +{ + gchar * uri = g_filename_to_uri (filename, FALSE, error); + if (!uri) + return FALSE; + + Inkscape::XML::Node *newNode = dbus_create_node(object->desk, "svg:image"); + sp_repr_set_int(newNode, "x", x); + sp_repr_set_int(newNode, "y", y); + newNode->setAttribute("xlink:href", uri); + + object->desk->currentLayer()->appendChildRepr(newNode); + object->desk->currentLayer()->updateRepr(); + + if (object->updates) + sp_document_done(sp_desktop_document(object->desk), 0, "Imported bitmap."); + + //g_free(uri); + return strdup(newNode->attribute("id")); +} + +gchar* +document_interface_node (DocumentInterface *object, gchar *type, GError **error) +{ + SPDocument * doc = sp_desktop_document (object->desk); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); + + Inkscape::XML::Node *newNode = xml_doc->createElement(type); + + object->desk->currentLayer()->appendChildRepr(newNode); + object->desk->currentLayer()->updateRepr(); + + if (object->updates) + sp_document_done(sp_desktop_document(object->desk), 0, (gchar *)"created empty node"); + //else + //document_interface_pause_updates(object, error); + + return strdup(newNode->attribute("id")); +} + +/**************************************************************************** + ENVIORNMENT FUNCTIONS +****************************************************************************/ +gdouble +document_interface_document_get_width (DocumentInterface *object) +{ + return sp_document_width(sp_desktop_document(object->desk)); +} + +gdouble +document_interface_document_get_height (DocumentInterface *object) +{ + return sp_document_height(sp_desktop_document(object->desk)); +} + +gchar * +document_interface_document_get_css (DocumentInterface *object, GError **error) +{ + SPCSSAttr *current = (object->desk)->current; + return sp_repr_css_write_string(current); +} + +gboolean +document_interface_document_merge_css (DocumentInterface *object, + gchar *stylestring, GError **error) +{ + SPCSSAttr * style = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string (style, stylestring); + sp_desktop_set_style (object->desk, style); + return TRUE; +} + +gboolean +document_interface_document_set_css (DocumentInterface *object, + gchar *stylestring, GError **error) +{ + SPCSSAttr * style = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string (style, stylestring); + //Memory leak? + object->desk->current = style; + return TRUE; +} + +gboolean +document_interface_document_resize_to_fit_selection (DocumentInterface *object, + GError **error) +{ + return dbus_call_verb (object, SP_VERB_FIT_CANVAS_TO_SELECTION, error); + return TRUE; +} + +/**************************************************************************** + OBJECT FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_set_attribute (DocumentInterface *object, char *shape, + char *attribute, char *newval, GError **error) +{ + Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error); + + /* ALTERNATIVE (is this faster?) + Inkscape::XML::Node *newnode = sp_repr_lookup_name((doc->root)->repr, name); + */ + if (!dbus_check_string(newval, error, "New value string was empty.")) + return FALSE; + + if (!newNode) + return FALSE; + + newNode->setAttribute(attribute, newval, TRUE); + return TRUE; +} + +gboolean +document_interface_set_int_attribute (DocumentInterface *object, + char *shape, char *attribute, + int newval, GError **error) +{ + Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error); + if (!newNode) + return FALSE; + + sp_repr_set_int (newNode, attribute, newval); + return TRUE; +} + + +gboolean +document_interface_set_double_attribute (DocumentInterface *object, + char *shape, char *attribute, + double newval, GError **error) +{ + Inkscape::XML::Node *newNode = get_repr_by_name (object->desk, shape, error); + + if (!dbus_check_string (attribute, error, "New value string was empty.")) + return FALSE; + if (!newNode) + return FALSE; + + sp_repr_set_svg_double (newNode, attribute, newval); + return TRUE; +} + +gchar * +document_interface_get_attribute (DocumentInterface *object, char *shape, + char *attribute, GError **error) +{ + Inkscape::XML::Node *newNode = get_repr_by_name(object->desk, shape, error); + + if (!dbus_check_string (attribute, error, "Attribute name empty.")) + return NULL; + if (!newNode) + return NULL; + + return g_strdup(newNode->attribute(attribute)); +} + +gboolean +document_interface_move (DocumentInterface *object, gchar *name, gdouble x, + gdouble y, GError **error) +{ + const GSList *oldsel = selection_swap(object->desk, name, error); + if (!oldsel) + return FALSE; + sp_selection_move (object->desk, x, 0 - y); + selection_restore(object->desk, oldsel); + return TRUE; +} + +gboolean +document_interface_move_to (DocumentInterface *object, gchar *name, gdouble x, + gdouble y, GError **error) +{ + const GSList *oldsel = selection_swap(object->desk, name, error); + if (!oldsel) + return FALSE; + Inkscape::Selection * sel = sp_desktop_selection(object->desk); + sp_selection_move (object->desk, x - selection_get_center_x(sel), + 0 - (y - selection_get_center_y(sel))); + selection_restore(object->desk, oldsel); + return TRUE; +} + +gboolean +document_interface_object_to_path (DocumentInterface *object, + char *shape, GError **error) +{ + const GSList *oldsel = selection_swap(object->desk, shape, error); + if (!oldsel) + return FALSE; + dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error); + selection_restore(object->desk, oldsel); + return TRUE; +} + +gchar * +document_interface_get_path (DocumentInterface *object, char *pathname, GError **error) +{ + Inkscape::XML::Node *node = get_repr_by_name(object->desk, pathname, error); + + if (!node) + return NULL; + + if (node->attribute("d") == NULL) + { + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OBJECT, "Object is not a path."); + return NULL; + } + return strdup(node->attribute("d")); +} + +gboolean +document_interface_transform (DocumentInterface *object, gchar *shape, + gchar *transformstr, GError **error) +{ + //FIXME: This should merge transformations. + gchar trans[] = "transform"; + document_interface_set_attribute (object, shape, trans, transformstr, error); + return TRUE; +} + +gchar * +document_interface_get_css (DocumentInterface *object, gchar *shape, + GError **error) +{ + gchar style[] = "style"; + return document_interface_get_attribute (object, shape, style, error); +} + +gboolean +document_interface_modify_css (DocumentInterface *object, gchar *shape, + gchar *cssattrb, gchar *newval, GError **error) +{ + // Doesn't like non-variable strings for some reason. + gchar style[] = "style"; + Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error); + + if (!dbus_check_string (cssattrb, error, "Attribute string empty.")) + return FALSE; + if (!node) + return FALSE; + + SPCSSAttr * oldstyle = sp_repr_css_attr (node, style); + sp_repr_css_set_property(oldstyle, cssattrb, newval); + node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE); + return TRUE; +} + +gboolean +document_interface_merge_css (DocumentInterface *object, gchar *shape, + gchar *stylestring, GError **error) +{ + gchar style[] = "style"; + + Inkscape::XML::Node *node = get_repr_by_name(object->desk, shape, error); + + if (!dbus_check_string (stylestring, error, "Style string empty.")) + return FALSE; + if (!node) + return FALSE; + + SPCSSAttr * newstyle = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string (newstyle, stylestring); + + SPCSSAttr * oldstyle = sp_repr_css_attr (node, style); + + sp_repr_css_merge(oldstyle, newstyle); + node->setAttribute (style, sp_repr_css_write_string (oldstyle), TRUE); + return TRUE; +} + +gboolean +document_interface_set_color (DocumentInterface *object, gchar *shape, + int r, int g, int b, gboolean fill, GError **error) +{ + gchar style[15]; + if (r<0 || r>255 || g<0 || g>255 || b<0 || b>255) + { + g_set_error(error, INKSCAPE_ERROR, INKSCAPE_ERROR_OTHER, "Given (%d,%d,%d). All values must be between 0-255 inclusive.", r, g, b); + return FALSE; + } + + if (fill) + snprintf(style, 15, "fill:#%.2x%.2x%.2x", r, g, b); + else + snprintf(style, 15, "stroke:#%.2x%.2x%.2x", r, g, b); + + if (strcmp(shape, "document") == 0) + return document_interface_document_merge_css (object, style, error); + + return document_interface_merge_css (object, shape, style, error); +} + +gboolean +document_interface_move_to_layer (DocumentInterface *object, gchar *shape, + gchar *layerstr, GError **error) +{ + const GSList *oldsel = selection_swap(object->desk, shape, error); + if (!oldsel) + return FALSE; + + document_interface_selection_move_to_layer(object, layerstr, error); + selection_restore(object->desk, oldsel); + return TRUE; +} + +GArray * +document_interface_get_node_coordinates (DocumentInterface *object, gchar *shape) +{ + //FIXME: Needs lot's of work. +/* + Inkscape::XML::Node *shapenode = get_repr_by_name (object->desk, shape, error); + if (shapenode == NULL || shapenode->attribute("d") == NULL) { + return FALSE; + } + char * path = strdup(shapenode->attribute("d")); + printf("PATH: %s\n", path); + + Geom::parse_svg_path (path); + return NULL; + */ + return NULL; +} + + +/**************************************************************************** + FILE I/O FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_save (DocumentInterface *object, GError **error) +{ + SPDocument * doc = sp_desktop_document(object->desk); + printf("1: %s\n2: %s\n3: %s\n", doc->uri, doc->base, doc->name); + if (doc->uri) + return document_interface_save_as (object, doc->uri, error); + return FALSE; +} + +gboolean +document_interface_load (DocumentInterface *object, + gchar *filename, GError **error) +{ + desktop_ensure_active (object->desk); + const Glib::ustring file(filename); + sp_file_open(file, NULL, TRUE, TRUE); + if (object->updates) + sp_document_done(sp_desktop_document(object->desk), SP_VERB_FILE_OPEN, "Opened File"); + return TRUE; +} + +gboolean +document_interface_save_as (DocumentInterface *object, + gchar *filename, GError **error) +{ + SPDocument * doc = sp_desktop_document(object->desk); + #ifdef WITH_GNOME_VFS + const Glib::ustring file(filename); + return file_save_remote(doc, file, NULL, TRUE, TRUE); + #endif + if (!doc || strlen(filename)<1) //Safety check + return false; + + try { + Inkscape::Extension::save(NULL, doc, filename, + false, false, true, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS); + } catch (...) { + //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved.")); + return false; + } + + //SP_ACTIVE_DESKTOP->event_log->rememberFileSave(); + //SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, "Document saved."); + return true; +} + +gboolean +document_interface_mark_as_unmodified (DocumentInterface *object, GError **error) +{ + SPDocument * doc = sp_desktop_document(object->desk); + if (doc) + doc->modified_since_save = FALSE; + return TRUE; +} + +/* +gboolean +document_interface_print_to_file (DocumentInterface *object, GError **error) +{ + SPDocument * doc = sp_desktop_document(object->desk); + sp_print_document_to_file (doc, g_strdup("/home/soren/test.pdf")); + + return TRUE; +} +*/ +/**************************************************************************** + PROGRAM CONTROL FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_close (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_FILE_CLOSE_VIEW, error); +} + +gboolean +document_interface_exit (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_FILE_QUIT, error); +} + +gboolean +document_interface_undo (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_EDIT_UNDO, error); +} + +gboolean +document_interface_redo (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_EDIT_REDO, error); +} + + + +/**************************************************************************** + UPDATE FUNCTIONS + FIXME: This would work better by adding a flag to SPDesktop to prevent + updating but that would be very intrusive so for now there is a workaround. + Need to make sure it plays well with verbs because they are used so much. +****************************************************************************/ + +void +document_interface_pause_updates (DocumentInterface *object, GError **error) +{ + object->updates = FALSE; + object->desk->canvas->drawing_disabled = 1; + //object->desk->canvas->need_redraw = 0; + //object->desk->canvas->need_repick = 0; + //sp_desktop_document(object->desk)->root->uflags = FALSE; + //sp_desktop_document(object->desk)->root->mflags = FALSE; +} + +void +document_interface_resume_updates (DocumentInterface *object, GError **error) +{ + object->updates = TRUE; + object->desk->canvas->drawing_disabled = 0; + //object->desk->canvas->need_redraw = 1; + //object->desk->canvas->need_repick = 1; + //sp_desktop_document(object->desk)->root->uflags = TRUE; + //sp_desktop_document(object->desk)->root->mflags = TRUE; + //sp_desktop_document(object->desk)->_updateDocument(); + //FIXME: use better verb than rect. + sp_document_done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions"); +} + +void +document_interface_update (DocumentInterface *object, GError **error) +{ + sp_desktop_document(object->desk)->root->uflags = TRUE; + sp_desktop_document(object->desk)->root->mflags = TRUE; + object->desk->enableInteraction(); + sp_desktop_document(object->desk)->_updateDocument(); + object->desk->disableInteraction(); + sp_desktop_document(object->desk)->root->uflags = FALSE; + sp_desktop_document(object->desk)->root->mflags = FALSE; + //sp_document_done(sp_desktop_document(object->desk), SP_VERB_CONTEXT_RECT, "Multiple actions"); +} + +/**************************************************************************** + SELECTION FUNCTIONS FIXME: use call_verb where appropriate (once update system is tested.) +****************************************************************************/ + +gboolean +document_interface_selection_get (DocumentInterface *object, char ***out, GError **error) +{ + Inkscape::Selection * sel = sp_desktop_selection(object->desk); + GSList const *oldsel = sel->list(); + + int size = g_slist_length((GSList *) oldsel); + + *out = g_new0 (char *, size + 1); + + int i = 0; + for (GSList const *iter = oldsel; iter != NULL; iter = iter->next) { + (*out)[i] = g_strdup(SP_OBJECT(iter->data)->repr->attribute("id")); + i++; + } + (*out)[i] = NULL; + + return TRUE; +} + +gboolean +document_interface_selection_add (DocumentInterface *object, char *name, GError **error) +{ + SPObject * obj = get_object_by_name(object->desk, name, error); + if (!obj) + return FALSE; + + Inkscape::Selection *selection = sp_desktop_selection(object->desk); + + selection->add(obj); + return TRUE; +} + +gboolean +document_interface_selection_add_list (DocumentInterface *object, + char **names, GError **error) +{ + int i; + for (i=0;names[i] != NULL;i++) { + document_interface_selection_add(object, names[i], error); + } + return TRUE; +} + +gboolean +document_interface_selection_set (DocumentInterface *object, char *name, GError **error) +{ + SPDocument * doc = sp_desktop_document (object->desk); + Inkscape::Selection *selection = sp_desktop_selection(object->desk); + selection->set(doc->getObjectById(name)); + return TRUE; +} + +gboolean +document_interface_selection_set_list (DocumentInterface *object, + gchar **names, GError **error) +{ + sp_desktop_selection(object->desk)->clear(); + int i; + for (i=0;names[i] != NULL;i++) { + document_interface_selection_add(object, names[i], error); + } + return TRUE; +} + +gboolean +document_interface_selection_rotate (DocumentInterface *object, int angle, GError **error) +{ + Inkscape::Selection *selection = sp_desktop_selection(object->desk); + sp_selection_rotate(selection, angle); + return TRUE; +} + +gboolean +document_interface_selection_delete (DocumentInterface *object, GError **error) +{ + //sp_selection_delete (object->desk); + return dbus_call_verb (object, SP_VERB_EDIT_DELETE, error); +} + +gboolean +document_interface_selection_clear (DocumentInterface *object, GError **error) +{ + sp_desktop_selection(object->desk)->clear(); + return TRUE; +} + +gboolean +document_interface_select_all (DocumentInterface *object, GError **error) +{ + //sp_edit_select_all (object->desk); + return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL, error); +} + +gboolean +document_interface_select_all_in_all_layers(DocumentInterface *object, + GError **error) +{ + //sp_edit_select_all_in_all_layers (object->desk); + return dbus_call_verb (object, SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS, error); +} + +gboolean +document_interface_selection_box (DocumentInterface *object, int x, int y, + int x2, int y2, gboolean replace, + GError **error) +{ + //FIXME: implement. + return FALSE; +} + +gboolean +document_interface_selection_invert (DocumentInterface *object, GError **error) +{ + //sp_edit_invert (object->desk); + return dbus_call_verb (object, SP_VERB_EDIT_INVERT, error); +} + +gboolean +document_interface_selection_group (DocumentInterface *object, GError **error) +{ + //sp_selection_group (object->desk); + return dbus_call_verb (object, SP_VERB_SELECTION_GROUP, error); +} +gboolean +document_interface_selection_ungroup (DocumentInterface *object, GError **error) +{ + //sp_selection_ungroup (object->desk); + return dbus_call_verb (object, SP_VERB_SELECTION_UNGROUP, error); +} + +gboolean +document_interface_selection_cut (DocumentInterface *object, GError **error) +{ + //desktop_ensure_active (object->desk); + //sp_selection_cut (object->desk); + return dbus_call_verb (object, SP_VERB_EDIT_CUT, error); +} + +gboolean +document_interface_selection_copy (DocumentInterface *object, GError **error) +{ + //desktop_ensure_active (object->desk); + //sp_selection_copy (); + return dbus_call_verb (object, SP_VERB_EDIT_COPY, error); +} +/* +gboolean +document_interface_selection_paste (DocumentInterface *object, GError **error) +{ + desktop_ensure_active (object->desk); + if (!object->updates) + document_interface_pause_updates (object, error); + sp_selection_paste (object->desk, TRUE); + if (!object->updates) + document_interface_pause_updates (object, error); + return TRUE; + //return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error); +} +*/ +gboolean +document_interface_selection_paste (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_EDIT_PASTE, error); +} + +gboolean +document_interface_selection_scale (DocumentInterface *object, gdouble grow, GError **error) +{ + Inkscape::Selection *selection = sp_desktop_selection(object->desk); + if (!selection) + { + return FALSE; + } + sp_selection_scale (selection, grow); + return TRUE; +} + +gboolean +document_interface_selection_move (DocumentInterface *object, gdouble x, gdouble y, GError **error) +{ + sp_selection_move (object->desk, x, 0 - y); //switching coordinate systems. + return TRUE; +} + +gboolean +document_interface_selection_move_to (DocumentInterface *object, gdouble x, gdouble y, GError **error) +{ + Inkscape::Selection * sel = sp_desktop_selection(object->desk); + + Geom::OptRect sel_bbox = sel->bounds(); + if (sel_bbox) { + Geom::Point m( x - selection_get_center_x(sel) , 0 - (y - selection_get_center_y(sel)) ); + sp_selection_move_relative(sel, m, true); + } + return TRUE; +} + +//FIXME: does not paste in new layer. +// This needs to use lower level cut_impl and paste_impl (messy) +// See the built-in sp_selection_to_next_layer and duplicate. +gboolean +document_interface_selection_move_to_layer (DocumentInterface *object, + gchar *layerstr, GError **error) +{ + SPDesktop * dt = object->desk; + + Inkscape::Selection *selection = sp_desktop_selection(dt); + + // check if something is selected + if (selection->isEmpty()) + return FALSE; + + SPObject *next = get_object_by_name(object->desk, layerstr, error); + + if (!next) + return FALSE; + + if (strcmp("layer", (next->repr)->attribute("inkscape:groupmode")) == 0) { + + sp_selection_cut(dt); + + dt->setCurrentLayer(next); + + sp_selection_paste(dt, TRUE); + } + return TRUE; +} + +GArray * +document_interface_selection_get_center (DocumentInterface *object) +{ + Inkscape::Selection * sel = sp_desktop_selection(object->desk); + + if (sel) + { + gdouble x = selection_get_center_x(sel); + gdouble y = selection_get_center_y(sel); + GArray * intArr = g_array_new (TRUE, TRUE, sizeof(double)); + + g_array_append_val (intArr, x); + g_array_append_val (intArr, y); + return intArr; + } + + return NULL; +} + +gboolean +document_interface_selection_to_path (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_OBJECT_TO_CURVE, error); +} + + +gchar * +document_interface_selection_combine (DocumentInterface *object, gchar *cmd, + GError **error) +{ + if (strcmp(cmd, "union") == 0) + dbus_call_verb (object, SP_VERB_SELECTION_UNION, error); + else if (strcmp(cmd, "intersection") == 0) + dbus_call_verb (object, SP_VERB_SELECTION_INTERSECT, error); + else if (strcmp(cmd, "difference") == 0) + dbus_call_verb (object, SP_VERB_SELECTION_DIFF, error); + else if (strcmp(cmd, "exclusion") == 0) + dbus_call_verb (object, SP_VERB_SELECTION_SYMDIFF, error); + else + return NULL; + + if (sp_desktop_selection(object->desk)->singleRepr() != NULL) + return g_strdup((sp_desktop_selection(object->desk)->singleRepr())->attribute("id")); + return NULL; +} + +gboolean +document_interface_selection_divide (DocumentInterface *object, char ***out, GError **error) +{ + dbus_call_verb (object, SP_VERB_SELECTION_CUT, error); + + return document_interface_selection_get (object, out, error); +} + +gboolean +document_interface_selection_change_level (DocumentInterface *object, gchar *cmd, + GError **error) +{ + if (strcmp(cmd, "raise") == 0) + return dbus_call_verb (object, SP_VERB_SELECTION_RAISE, error); + if (strcmp(cmd, "lower") == 0) + return dbus_call_verb (object, SP_VERB_SELECTION_LOWER, error); + if ((strcmp(cmd, "to_top") == 0) || (strcmp(cmd, "to_front") == 0)) + return dbus_call_verb (object, SP_VERB_SELECTION_TO_FRONT, error); + if ((strcmp(cmd, "to_bottom") == 0) || (strcmp(cmd, "to_back") == 0)) + return dbus_call_verb (object, SP_VERB_SELECTION_TO_BACK, error); + return TRUE; +} + +/**************************************************************************** + LAYER FUNCTIONS +****************************************************************************/ + +gchar * +document_interface_layer_new (DocumentInterface *object, GError **error) +{ + SPDesktop * dt = object->desk; + SPObject *new_layer = Inkscape::create_layer(dt->currentRoot(), dt->currentLayer(), Inkscape::LPOS_BELOW); + dt->setCurrentLayer(new_layer); + return g_strdup(get_name_from_object (new_layer)); +} + +gboolean +document_interface_layer_set (DocumentInterface *object, + gchar *layerstr, GError **error) +{ + SPObject * obj = get_object_by_name (object->desk, layerstr, error); + + if (!obj) + return FALSE; + + object->desk->setCurrentLayer (obj); + return TRUE; +} + +gchar ** +document_interface_layer_get_all (DocumentInterface *object) +{ + //FIXME: implement. + return NULL; +} + +gboolean +document_interface_layer_change_level (DocumentInterface *object, + gchar *cmd, GError **error) +{ + if (strcmp(cmd, "raise") == 0) + return dbus_call_verb (object, SP_VERB_LAYER_RAISE, error); + if (strcmp(cmd, "lower") == 0) + return dbus_call_verb (object, SP_VERB_LAYER_LOWER, error); + if ((strcmp(cmd, "to_top") == 0) || (strcmp(cmd, "to_front") == 0)) + return dbus_call_verb (object, SP_VERB_LAYER_TO_TOP, error); + if ((strcmp(cmd, "to_bottom") == 0) || (strcmp(cmd, "to_back") == 0)) + return dbus_call_verb (object, SP_VERB_LAYER_TO_BOTTOM, error); + return TRUE; +} + +gboolean +document_interface_layer_next (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_LAYER_NEXT, error); +} + +gboolean +document_interface_layer_previous (DocumentInterface *object, GError **error) +{ + return dbus_call_verb (object, SP_VERB_LAYER_PREV, error); +} + + + + + + diff --git a/src/extension/dbus/document-interface.h b/src/extension/dbus/document-interface.h new file mode 100644 index 000000000..8cf9b7ec1 --- /dev/null +++ b/src/extension/dbus/document-interface.h @@ -0,0 +1,399 @@ +/* + * This is where the implementation of the DBus based document API lives. + * All the methods in here (except in the helper section) are + * designed to be called remotly via DBus. application-interface.cpp + * has the methods used to connect to the bus and get a document instance. + * + * Documentation for these methods is in document-interface.xml + * which is the "gold standard" as to how the interface should work. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_EXTENSION_DOCUMENT_INTERFACE_H_ +#define INKSCAPE_EXTENSION_DOCUMENT_INTERFACE_H_ + +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> +#include <dbus/dbus-glib-lowlevel.h> +#include "desktop.h" + +#define DBUS_DOCUMENT_INTERFACE_PATH "/org/inkscape/document" + +#define TYPE_DOCUMENT_INTERFACE (document_interface_get_type ()) +#define DOCUMENT_INTERFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_DOCUMENT_INTERFACE, DocumentInterface)) +#define DOCUMENT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_DOCUMENT_INTERFACE, DocumentInterfaceClass)) +#define IS_DOCUMENT_INTERFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_DOCUMENT_INTERFACE)) +#define IS_DOCUMENT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_DOCUMENT_INTERFACE)) +#define DOCUMENT_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_DOCUMENT_INTERFACE, DocumentInterfaceClass)) + +G_BEGIN_DECLS + +typedef struct _DocumentInterface DocumentInterface; +typedef struct _DocumentInterfaceClass DocumentInterfaceClass; + +struct _DocumentInterface { + GObject parent; + SPDesktop *desk; + gboolean updates; +}; + +struct _DocumentInterfaceClass { + GObjectClass parent; +}; + +typedef enum +{ + INKSCAPE_ERROR_SELECTION, + INKSCAPE_ERROR_OBJECT, + INKSCAPE_ERROR_VERB, + INKSCAPE_ERROR_OTHER +} InkscapeError; + +#define INKSCAPE_ERROR (inkscape_error_quark ()) +#define INKSCAPE_TYPE_ERROR (inkscape_error_get_type ()) + +GQuark inkscape_error_quark (void); +GType inkscape_error_get_type (void); + +struct DBUSPoint { + int x; + int y; +}; +/**************************************************************************** + MISC FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_delete_all (DocumentInterface *object, GError **error); + +gboolean +document_interface_call_verb (DocumentInterface *object, + gchar *verbid, GError **error); + +/**************************************************************************** + CREATION FUNCTIONS +****************************************************************************/ + +gchar* +document_interface_rectangle (DocumentInterface *object, int x, int y, + int width, int height, GError **error); + +gchar* +document_interface_ellipse (DocumentInterface *object, int x, int y, + int width, int height, GError **error); + +gchar* +document_interface_polygon (DocumentInterface *object, int cx, int cy, + int radius, int rotation, int sides, + GError **error); + +gchar* +document_interface_star (DocumentInterface *object, int cx, int cy, + int r1, int r2, int sides, gdouble rounded, + gdouble arg1, gdouble arg2, GError **error); + +gchar* +document_interface_spiral (DocumentInterface *object, int cx, int cy, + int r, int revolutions, GError **error); + +gchar* +document_interface_line (DocumentInterface *object, int x, int y, + int x2, int y2, GError **error); + +gboolean +document_interface_text (DocumentInterface *object, int x, int y, + gchar *text, GError **error); + +gchar * +document_interface_image (DocumentInterface *object, int x, int y, + gchar *filename, GError **error); + +gchar* +document_interface_node (DocumentInterface *object, gchar *svgtype, + GError **error); + + +/**************************************************************************** + ENVIORNMENT FUNCTIONS +****************************************************************************/ +gdouble +document_interface_document_get_width (DocumentInterface *object); + +gdouble +document_interface_document_get_height (DocumentInterface *object); + +gchar * +document_interface_document_get_css (DocumentInterface *object, GError **error); + +gboolean +document_interface_document_merge_css (DocumentInterface *object, + gchar *stylestring, GError **error); + +gboolean +document_interface_document_set_css (DocumentInterface *object, + gchar *stylestring, GError **error); + +gboolean +document_interface_document_resize_to_fit_selection (DocumentInterface *object, + GError **error); + +/**************************************************************************** + OBJECT FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_set_attribute (DocumentInterface *object, + char *shape, char *attribute, + char *newval, GError **error); + +gboolean +document_interface_set_int_attribute (DocumentInterface *object, + char *shape, char *attribute, + int newval, GError **error); + +gboolean +document_interface_set_double_attribute (DocumentInterface *object, + char *shape, char *attribute, + double newval, GError **error); + +gchar * +document_interface_get_attribute (DocumentInterface *object, + char *shape, char *attribute, GError **error); + +gboolean +document_interface_move (DocumentInterface *object, gchar *name, + gdouble x, gdouble y, GError **error); + +gboolean +document_interface_move_to (DocumentInterface *object, gchar *name, + gdouble x, gdouble y, GError **error); + +gboolean +document_interface_object_to_path (DocumentInterface *object, + char *shape, GError **error); + +gchar * +document_interface_get_path (DocumentInterface *object, + char *pathname, GError **error); + +gboolean +document_interface_transform (DocumentInterface *object, gchar *shape, + gchar *transformstr, GError **error); + +gchar * +document_interface_get_css (DocumentInterface *object, gchar *shape, + GError **error); + +gboolean +document_interface_modify_css (DocumentInterface *object, gchar *shape, + gchar *cssattrb, gchar *newval, GError **error); + +gboolean +document_interface_merge_css (DocumentInterface *object, gchar *shape, + gchar *stylestring, GError **error); + +gboolean +document_interface_set_color (DocumentInterface *object, gchar *shape, + int r, int g, int b, gboolean fill, GError **error); + +gboolean +document_interface_move_to_layer (DocumentInterface *object, gchar *shape, + gchar *layerstr, GError **error); + + +GArray * +document_interface_get_node_coordinates (DocumentInterface *object, gchar *shape); + +/**************************************************************************** + FILE I/O FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_save (DocumentInterface *object, GError **error); + +gboolean +document_interface_load (DocumentInterface *object, + gchar *filename, GError **error); + +gboolean +document_interface_save_as (DocumentInterface *object, + gchar *filename, GError **error); + +gboolean +document_interface_mark_as_unmodified (DocumentInterface *object, GError **error); +/* +gboolean +document_interface_print_to_file (DocumentInterface *object, GError **error); +*/ + +/**************************************************************************** + PROGRAM CONTROL FUNCTIONS +****************************************************************************/ + +gboolean +document_interface_close (DocumentInterface *object, GError **error); + +gboolean +document_interface_exit (DocumentInterface *object, GError **error); + +gboolean +document_interface_undo (DocumentInterface *object, GError **error); + +gboolean +document_interface_redo (DocumentInterface *object, GError **error); + + +/**************************************************************************** + UPDATE FUNCTIONS +****************************************************************************/ +void +document_interface_pause_updates (DocumentInterface *object, GError **error); + +void +document_interface_resume_updates (DocumentInterface *object, GError **error); + +void +document_interface_update (DocumentInterface *object, GError **error); + +/**************************************************************************** + SELECTION FUNCTIONS +****************************************************************************/ +gboolean +document_interface_selection_get (DocumentInterface *object, char ***out, GError **error); + +gboolean +document_interface_selection_add (DocumentInterface *object, + char *name, GError **error); + +gboolean +document_interface_selection_add_list (DocumentInterface *object, + char **names, GError **error); + +gboolean +document_interface_selection_set (DocumentInterface *object, + char *name, GError **error); + +gboolean +document_interface_selection_set_list (DocumentInterface *object, + gchar **names, GError **error); + +gboolean +document_interface_selection_rotate (DocumentInterface *object, + int angle, GError **error); + +gboolean +document_interface_selection_delete(DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_clear(DocumentInterface *object, GError **error); + +gboolean +document_interface_select_all(DocumentInterface *object, GError **error); + +gboolean +document_interface_select_all_in_all_layers(DocumentInterface *object, + GError **error); + +gboolean +document_interface_selection_box (DocumentInterface *object, int x, int y, + int x2, int y2, gboolean replace, + GError **error); + +gboolean +document_interface_selection_invert (DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_group(DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_ungroup(DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_cut(DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_copy(DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_paste(DocumentInterface *object, GError **error); + +gboolean +document_interface_selection_scale (DocumentInterface *object, + gdouble grow, GError **error); + +gboolean +document_interface_selection_move (DocumentInterface *object, gdouble x, + gdouble y, GError **error); + +gboolean +document_interface_selection_move_to (DocumentInterface *object, gdouble x, + gdouble y, GError **error); + +gboolean +document_interface_selection_move_to_layer (DocumentInterface *object, + gchar *layerstr, GError **error); + +GArray * +document_interface_selection_get_center (DocumentInterface *object); + +gboolean +document_interface_selection_to_path (DocumentInterface *object, GError **error); + +gchar * +document_interface_selection_combine (DocumentInterface *object, gchar *cmd, + GError **error); + +gboolean +document_interface_selection_divide (DocumentInterface *object, + char ***out, GError **error); + + +gboolean +document_interface_selection_change_level (DocumentInterface *object, gchar *cmd, + GError **error); + +/**************************************************************************** + LAYER FUNCTIONS +****************************************************************************/ + +gchar * +document_interface_layer_new (DocumentInterface *object, GError **error); + +gboolean +document_interface_layer_set (DocumentInterface *object, + gchar *layerstr, GError **error); + +gchar ** +document_interface_layer_get_all (DocumentInterface *object); + +gboolean +document_interface_layer_change_level (DocumentInterface *object, + gchar *cmd, GError **error); + +gboolean +document_interface_layer_next (DocumentInterface *object, GError **error); + +gboolean +document_interface_layer_previous (DocumentInterface *object, GError **error); + + + + + + + + +DocumentInterface *document_interface_new (void); +GType document_interface_get_type (void); + + +G_END_DECLS + +#endif // INKSCAPE_EXTENSION_DOCUMENT_INTERFACE_H_ diff --git a/src/extension/dbus/document-interface.xml b/src/extension/dbus/document-interface.xml new file mode 100644 index 000000000..8b0252765 --- /dev/null +++ b/src/extension/dbus/document-interface.xml @@ -0,0 +1,1355 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + * This is the master description of the DBus document interface. + * + * This file is used to generate both glue code and documentation. + * The methods are in the same order as the .cpp/.h and the sections are labeled. + * + * Any change to method prototypes in document-interface.cpp MUST be reflected here. + * + * This file is the proverbial gold standard for the document interface. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information +--> + + +<node name="/org/inkscape/document" + xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" +> + + <interface name="org.inkscape.document"> + + <!-- MISC FUNCTIONS --> + + <method name="delete_all" > + </method> + + <method name="call_verb"> + <arg type="s" name="verbid" direction="in"> + <doc:doc> + <doc:summary>The string id of a verb. For example: "EditSelectAll".</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method allows you to call any Inkscape verb using it's associated string. Every button and menu item has an associated verb, so this allows access to some extra functionality if one is willing to do the prerequisite research. The list of verbs can be found at:</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- CREATION FUNCTIONS --> + + <method name="rectangle"> + <arg type="i" name="x" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the top left corner of the rectangle.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the top left corner of the rectangle.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="width" direction="in" > + <doc:doc> + <doc:summary>Width of the rectangle.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="height" direction="in" > + <doc:doc> + <doc:summary>Height of the rectangle.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new rectangle.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates a rectangle in the current layer using the current document style.</doc:para> + <doc:para>It is recommended that you save the return value if you will want to modify this particular shape later.</doc:para> + <doc:para>Additional variables include:</doc:para> + <doc:para>cx and cy: set these anywhere from zero to half the width or height respectively of the rectangle to give it rounded corners.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="ellipse"> + <arg type="i" name="x" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the top left corner of the ellipse.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the top left corner of the ellipse.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="width" direction="in" > + <doc:doc> + <doc:summary>Width of the ellipse.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="height" direction="in" > + <doc:doc> + <doc:summary>Height of the ellipse.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new ellipse.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates a ellipse in the current layer using the current document style.</doc:para> + <doc:para>It is recommended that you save the return value if you will want to modify this particular shape later.</doc:para> + <doc:para>Additional variables include:</doc:para> + <doc:para>"sodipodi:start" and "sodipodi:end": set these between 0 and Pi to create wedges or Pacman like shapes.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="polygon"> + <arg type="i" name="cx" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the center of the polygon.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="cy" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the center of the polygon.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="radius" direction="in" > + <doc:doc> + <doc:summary>Radius from the center to one of the points.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="rotation" direction="in" > + <doc:doc> + <doc:summary>Angle in degrees to rotate. 0 will have the first point pointing straight up.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="sides" direction="in" > + <doc:doc> + <doc:summary>Number of sides of the polygon.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new polygon.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates a polygon in the current layer using the current document style.</doc:para> + <doc:para>It is recommended that you save the return value if you will want to modify this particular shape later.</doc:para> + <doc:para>Note: this is actually a <doc:ref type="method" to="document.star">star</doc:ref> with "sodipodi:flatsided" set to true, which causes it to ignore the arg2 and r2 values.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="star"> + <arg type="i" name="cx" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the center of the star.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="cy" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the center of the star.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="r1" direction="in" > + <doc:doc> + <doc:summary>distance from the center for the first point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="r2" direction="in" > + <doc:doc> + <doc:summary>distance from the center for the second point.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="arg1" direction="in" > + <doc:doc> + <doc:summary>Angle in radians for the first point. 0 is 90 degrees to the right of straight up.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="arg2" direction="in" > + <doc:doc> + <doc:summary>Angle in radians for the second point. 0 is 90 degrees to the right of straight up.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="sides" direction="in" > + <doc:doc> + <doc:summary>Number of times to repeat the points around the star.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="rounded" direction="in" > + <doc:doc> + <doc:summary>How rounded to make the star. 0 to 1 recommended for moderate to medium curves. 10 for extreme curves.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new star.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates a star in the current layer using the current document style.</doc:para> + <doc:para>It is recommended that you save the return value if you will want to modify this particular shape later.</doc:para> + <doc:para>Stars are quite complicated. Here is how they are represented: There are two points, represented by sodipodi:arg1 and sodipodi:arg2 for angle in radians and sodipodi:r1 and sodipodi:r2 for respective radius from the center point. The further one is a point of the star, the shorter one one of the valleys. This point and valley are repeated according to sodipodi:sides. sodipodi:rounded controls their control handles.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="spiral"> + <arg type="i" name="cx" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the center of the spiral.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="cy" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the center of the spiral.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="r" direction="in" > + <doc:doc> + <doc:summary>Radius of the spiral.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="revolutions" direction="in" > + <doc:doc> + <doc:summary>Number of revolutions.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new spiral.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates a spiral in the current layer using the current document style. However, fill is automatically set to "none". Stroke is unmodified.</doc:para> + <doc:para>It is recommended that you save the return value if you will want to modify this particular shape later.</doc:para> + <doc:para>Additional variables include:</doc:para> + <doc:para>"sodipodi:expansion": at 1 the spiral gets bigger at a constant rate. Less than one and the loops get tighter and tighter as it goes. More than one and they get looser and looser. This affects the number of revolutions so that it might not actually match the "sodipodi:revolutions" argument.</doc:para> + <doc:para>"sodipodi:t0": at 0 the entire spiral is drawn, at 0.5 it is only drawn %50 of the way (starting from the outside) etc.</doc:para> + <doc:para>"sodipodi:argument": Rotates the spiral. In radians.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="line"> + <arg type="i" name="x" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the first point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the first point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="x2" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the second point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y2" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the second point.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new line.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates a line in the current layer using the current document style. It's a path, so the only attribute it will pay any attention to is "transform".</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="text"> + <arg type="i" name="x" direction="in" > + <doc:doc> + <doc:summary>The x coordinate to put the text at.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y" direction="in" > + <doc:doc> + <doc:summary>The y coordinate to put the text at.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="text" direction="in" > + <doc:doc> + <doc:summary>The text you want.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method creates some text in the current layer.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="image"> + <arg type="i" name="x" direction="in" > + <doc:doc> + <doc:summary>The x coordinate to put the image at.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y" direction="in" > + <doc:doc> + <doc:summary>The y coordinate to put the image at.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="text" direction="in" > + <doc:doc> + <doc:summary>The full path of the image you want.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="object_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new image.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method imports a non-vector image (such as a jpeg, png, etc.) and places it at the given coordinates. The resulting shape has no style or path but can be treated like a rectangle. With and height can be set explicitly (will deform image) or transform strings or <doc:ref type="method" to="document.selection_scale">selection_scale()</doc:ref> can scale it relatively.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="node"> + <arg type="s" name="svgtype" direction="in" > + <doc:doc> + <doc:summary>The type of node, probably "svg:path"</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="node_name" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new node.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Make any kind of node you want. Mostly for making paths. (May need to allow updateRepr to be called for it to show up.)</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- ENVIRONMENT FUNCTIONS --> + + <method name="document_get_width"> + <arg type="d" name="val" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> + <doc:doc> + <doc:summary>Document width.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Retrieve the width of the current document. anything outside the boundary will not be printed or exported but will be saved.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="document_get_height"> + <arg type="d" name="val" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> + <doc:doc> + <doc:summary>Document height.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Retrieve the height of the current document. anything outside the boundary will not be printed or exported but will be saved.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="document_get_css"> + <arg type="s" name="css" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>CSS attribute string for the document.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Get the current style for the document. All new shapes will use this style if it exists.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Style Strings">Style Strings</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="document_set_css"> + <arg type="s" name="stylestring" direction="in" > + <doc:doc> + <doc:summary>A new CSS attribute string for the document.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set the current style for the document. All new shapes will use this style if it exists.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Style Strings">Style Strings</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="document_merge_css"> + <arg type="s" name="stylestring" direction="in" > + <doc:doc> + <doc:summary>A new CSS attribute string for the document.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Merge this this string with the current style for the document. All new shapes will use this style if it exists.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Style Strings">Style Strings</doc:ref>, <doc:ref type="method" to="document.merge_css">merge_css()</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="document_resize_to_fit_selection"> + <doc:doc> + <doc:description> + <doc:para>Resize the document to contain all of the currently selected objects.</doc:para> + <doc:para>This ensures that the image is not clipped when printing or exporting.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- OBJECT FUNCTIONS --> + + <method name="set_attribute"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="attribute" direction="in" > + <doc:doc> + <doc:summary>The name of the attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="newval" direction="in" > + <doc:doc> + <doc:summary>The new value of the attribute. This will overwrite anything already set. To merge styles, see <doc:ref type="method" to="document.merge_css">merge_css()</doc:ref>. </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set any attribute, the available attributes depend on what kind of shape the object node represents. See <doc:ref type="method" to="document.rectangle">shape creation functions</doc:ref> for more details.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="set_int_attribute"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="attribute" direction="in" > + <doc:doc> + <doc:summary>The name of the attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="newval" direction="in" > + <doc:doc> + <doc:summary>The new value of the attribute. This will overwrite anything already set.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set any attribute, the available attributes depend on what kind of shape the object node represents. See <doc:ref type="method" to="document.rectangle">shape creation functions</doc:ref> for more details.</doc:para> + <doc:para>This is a convenience function for <doc:ref type="method" to="document.set_attribute">set_attribute()</doc:ref>.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="set_double_attribute"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="attribute" direction="in" > + <doc:doc> + <doc:summary>The name of the attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="newval" direction="in" > + <doc:doc> + <doc:summary>The new value of the attribute. This will overwrite anything already set.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set any attribute, the available attributes depend on what kind of shape the node represents. See <doc:ref type="method" to="document.rectangle">shape creation functions</doc:ref> for more details.</doc:para> + <doc:para>This is a convenience function for <doc:ref type="method" to="document.set_attribute">set_attribute()</doc:ref>.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="get_attribute"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="attribute" direction="in" > + <doc:doc> + <doc:summary>The name of the attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="val" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The current value of the attribute. String is a copy and must be freed.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Get the value of any attribute. Not all objects will have every attribute their type supports, some are optional. See <doc:ref type="method" to="document.rectangle">shape creation functions</doc:ref> for more details.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="move"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="x" direction="in" > + <doc:doc> + <doc:summary>Distance to move along the x axis.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="y" direction="in" > + <doc:doc> + <doc:summary>Distance to move along the y axis.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This will move a shape (or any object) relative to it's current location.</doc:para> + <doc:para>This may be accomplished with transformation attributes or by changing x and y attributes depending on the state of the object.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="move_to"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="x" direction="in" > + <doc:doc> + <doc:summary>the x coordinate of the desired location.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="y" direction="in" > + <doc:doc> + <doc:summary>the y coordinate of the desired location.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This will move a shape (or any object) to an absolute location. The point moved is the center of the bounding box, which is usually similar to the center of the shape.</doc:para> + <doc:para>Note that creating a rectangle or ellipse at 100,100 and calling move_to to move it to 100,100 will not produce the same results.</doc:para> + <doc:para>This may be accomplished with transformation attributes or by changing x and y attributes depending on the state of the object.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="object_to_path"> + <arg type="s" name="objectname" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Turns an object into a path. Most objects contain paths (except rectangles) but are not paths themselves. </doc:para> + <doc:para>This will remove every attribute except d (the path attribute) style and id. id will not change. The appearance will be the same as well, it essentially encodes all information about the shape into the path.</doc:para> + <doc:para>After doing this you will no longer be able to modify the shape using shape specific attributes (cx, radius etc.) except transform</doc:para> + <doc:para>Required for certain functions that work on paths (not yet present in this API.)</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Paths">Paths</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="get_path"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="val" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The path of the object. NULL if the object has no path.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Get the path value of an object. Equivilent to calling <doc:ref type="method" to="document.get_attribute">get_attribte()</doc:ref> with argument "d". Will not turn object into a path if it is not already.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Paths">Paths</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="transform"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>The id of any node or object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="transformstr" direction="in" > + <doc:doc> + <doc:summary>A string that represents a transformation.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Takes a transformation string ("matrix(0.96629885,0.25742286,-0.25742286,0.96629885,0,0)" or "rotate(45)") and applies it to any shape or path.</doc:para> + <doc:para>Will merge with existing transformations.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="get_css"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>Any object with a style attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="css" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>A CSS Style string</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Retrieve the style of a object. Equivalent to calling <doc:ref type="method" to="document.get_attribute">get_attribute()</doc:ref> for "style".</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Style Strings">Style Strings</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="modify_css"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>Any object with a style attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="cssattrib" direction="in" > + <doc:doc> + <doc:summary>An attribute such as "fill" or "stroke-width".</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="newval" direction="in" > + <doc:doc> + <doc:summary>The new value.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Set a particular attribute of a style string. Overwrites just that part of the style.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Style Strings">Style Strings</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="merge_css"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>Any object with a style attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="stylestring" direction="in" > + <doc:doc> + <doc:summary>A full or partial CSS Style string.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Takes a CSS Style string and merges it with the objects current style, overwriting only the elements present in stylestring.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Style Strings">Style Strings</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="set_color"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>Any object, or 'document' to apply to document style.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="red" direction="in" > + <doc:doc> + <doc:summary>The red component.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="green" direction="in" > + <doc:doc> + <doc:summary>The green component.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="blue" direction="in" > + <doc:doc> + <doc:summary>The blue component.</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="fill" direction="in" > + <doc:doc> + <doc:summary>True to change fill color, false for stroke color.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Modifies the fill or stroke color of an object (or the document style) based on RGB values.</doc:para> + <doc:para>Red green and blue must be between 0-255 inclusive.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="move_to_layer"> + <arg type="s" name="objectname" direction="in" > + <doc:doc> + <doc:summary>The id of an object.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="layername" direction="in" > + <doc:doc> + <doc:summary>A layer name.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Moves an object to a different layer.</doc:para> + <doc:para>Will error if layer does not exist.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="document.layer_new">layer_new()</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="get_node_coordinates"> + <arg type="s" name="shape" direction="in" > + <doc:doc> + <doc:summary>A object that contains a path ("d") attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="ai" name="points" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> + <doc:doc> + <doc:summary>An array of points.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Returns an array of all of the X,Y coordinates of the points in the objects path.</doc:para> + <doc:para>If the path is a closed loop the first point is repeated at the end.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- FILE I/O FUNCTIONS --> + + <method name="save" > + <doc:doc> + <doc:description> + <doc:para>Saves the current document with current name or a default name if has not been saved before.</doc:para> + <doc:para>Will overwrite without confirmation.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="save_as"> + <arg type="s" name="pathname" direction="in" > + <doc:doc> + <doc:summary>The path for the file to be saved as.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Saves the current document as pathname.</doc:para> + <doc:para>Will overwrite without confirmation.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="load"> + <arg type="s" name="pathname" direction="in" > + <doc:doc> + <doc:summary>The path to a valid svg file.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Loads the file at pathname.</doc:para> + <doc:para>Will lose all unsaved work in current document.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="mark_as_unmodified" > + <doc:doc> + <doc:description> + <doc:para>Marks the document as unmodified/saved.</doc:para> + <doc:para>Will prevent save confirmation on close if called at end of script.</doc:para> + </doc:description> + </doc:doc> + </method> + <!-- + <method name="print_to_file" > + <doc:doc> + <doc:description> + <doc:para>Prints the current document with default settings.</doc:para> + <doc:para>Will only print things visible within the document boundaries.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="document.document_resize_to_fit_selection">document_resize_to_fit_selection()</doc:ref></doc:seealso> + </doc:doc> + </method> + --> + + <!-- PROGRAM CONTROL FUNCTIONS --> + + <method name="close" > + <doc:doc> + <doc:description> + <doc:para>Close this document.</doc:para> + <doc:para>You will not be able to send any more commands on this interface.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="exit" > + <doc:doc> + <doc:description> + <doc:para>Exit Inkscape.</doc:para> + <doc:para>You will not be able to send any more commands on any interface.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="undo" > + <doc:doc> + <doc:description> + <doc:para>Undo the last action.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="redo" > + <doc:doc> + <doc:description> + <doc:para>Redo the last undone action.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- UPDATE FUNCTIONS --> + + <method name="pause_updates" > + <doc:doc> + <doc:description> + <doc:para>When updates are paused Inkscape will not draw every change as it is made. Also you will not be able to undo individual actions made while updates were paused and will only be able to undo them in a group. Inkscape may refresh the screen every couple of seconds even with updates off.</doc:para> + <doc:para>The advantage is a 2-5x speed increase, depending on the type of functions being called. This is most useful when creating large numbers of shapes.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="resume_updates" > + <doc:doc> + <doc:description> + <doc:para>Resume updates after they have been paused. If undo is called at this point it will undo everything that happened since pause_updates() was called.</doc:para> + <doc:para>This will update the display to show any changes that happened while updates were paused, a separate call to update() is not necessary.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="update" > + <doc:doc> + <doc:description> + <doc:para>This will update the document once if updates are paused but it will not resume updates.</doc:para> + <doc:para>This could be used to check on the progress of a complex drawing function, or to add in undo steps at certain points in a render.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- SELECTION FUNCTIONS --> + + <method name="selection_get"> + <arg type="as" name="listy" direction="out" > + <!-- <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> --> + <doc:doc> + <doc:summary>List of the ids of currently selected objects.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Returns the current selection in the form of a list of ids of selected objects.</doc:para> + <doc:para>Manipulating this list will not affect the selection.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_add"> + <arg type="s" name="name" direction="in" > + <doc:doc> + <doc:summary>A object to add to the selection.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Adds a single object to the selection.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_add_list"> + <arg type="as" name="name" direction="in" > + <doc:doc> + <doc:summary>An array of object ids to add to the selection.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Adds a list of objects to the selection.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_set"> + <arg type="s" name="name" direction="in" > + <doc:doc> + <doc:summary>A object to select.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Replaces the selection with one containing just this object.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_set_list"> + <arg type="as" name="name" direction="in" > + <doc:doc> + <doc:summary>A list of objects to select.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Replaces the selection with one containing just these objects.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_rotate"> + <arg type="i" name="angle" direction="in" > + <doc:doc> + <doc:summary>Angle in degrees to rotate.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Rotates the selection around the center of it's bounding box.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_delete" > + <doc:doc> + <doc:description> + <doc:para>Delete all objects in the selection.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_clear" > + <doc:doc> + <doc:description> + <doc:para>Deselect everything. Selection will be empty.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="select_all" > + <doc:doc> + <doc:description> + <doc:para>Select all objects in current layer.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="select_all_in_all_layers" > + <doc:doc> + <doc:description> + <doc:para>Select all objects in every layer.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_box"> + <arg type="i" name="x" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the first point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the first point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="x2" direction="in" > + <doc:doc> + <doc:summary>X coordinate for the second point.</doc:summary> + </doc:doc> + </arg> + <arg type="i" name="y2" direction="in" > + <doc:doc> + <doc:summary>Y coordinate for the second point.</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="replace" direction="in" > + <doc:doc> + <doc:summary>True to replace selection, false to add to selection.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This method finds all of the objects inside the box and adds them to the current selection. If replace is true it will clear the old selection first.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_invert" > + <doc:doc> + <doc:description> + <doc:para>Invert the selection in the current layer.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_group" > + <doc:doc> + <doc:description> + <doc:para>Group the selection.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Groups">Groups</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_ungroup" > + <doc:doc> + <doc:description> + <doc:para>Ungroup the selection.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Groups">Groups</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_cut" > + <doc:doc> + <doc:description> + <doc:para>Cut the current selection.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_copy" > + <doc:doc> + <doc:description> + <doc:para>Copy the current selection.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_paste" > + <doc:doc> + <doc:description> + <doc:para>Paste the current selection at the same location it was cut from.</doc:para> + <doc:para>To paste to a particular location, simply use selection_paste() followed by <doc:ref type="method" to="document.selection_move_to">selection_move_to()</doc:ref>.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_scale"> + <arg type="d" name="grow" direction="in" > + <doc:doc> + <doc:summary>The amount to scale the selection, 1 has no effect. Between 0 and 1 will shrink it proportionally. Greater than one will grow it proportionally.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Scale the selection relative to it's current size.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_move"> + <arg type="d" name="x" direction="in" > + <doc:doc> + <doc:summary>Amount to move in the x direction.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="y" direction="in" > + <doc:doc> + <doc:summary>Amount to move in the y direction.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This will move the selection relative to it's current location.</doc:para> + <doc:para>This may be accomplished with transformation attributes or by changing x and y attributes depending on the state of the objects in the selection.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_move_to"> + <arg type="d" name="x" direction="in" > + <doc:doc> + <doc:summary>X coordinate to move to.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="y" direction="in" > + <doc:doc> + <doc:summary>Y coordinate to move to.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This will move the center of the selection to a specific location.</doc:para> + <doc:para>This may be accomplished with transformation attributes or by changing x and y attributes depending on the state of the objects in the selection.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_move_to_layer"> + <arg type="s" name="layer" direction="in" > + <doc:doc> + <doc:summary>layer to move the selection to.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Move every item in the selection to a different layer.</doc:para> + <doc:para>Will error if layer does not exist.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Layers and Levels">Layers and Levels</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_get_center"> + <arg type="ad" name="centerpoint" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> + <doc:doc> + <doc:summary>Center of the selection.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Gets the center of the selections bounding box in X,Y coordinates.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Coordinate System">Coordinate System</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_to_path" > + <doc:doc> + <doc:description> + <doc:para>Turns all the objects in the selection into paths.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="method" to="document.object_to_path">object_to_path()</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="selection_combine" > + <arg type="s" name="type" direction="in" > + <doc:doc> + <doc:summary>Type of combination.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="newpath" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The new path created, if there is one. NULL otherwise.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Will erase all objects in the selection and replace with a single aggregate path.</doc:para> + <doc:para>There are 5 types that can be passed in:</doc:para> + <doc:para>Union: The new shape is all of the other shapes put together, even if they don't overlap (paths can have multiple non-contiguous areas.)</doc:para> + <doc:para>Intersection: The new shape is composed of the area where ALL the objects in the selection overlap. If there is no area where all shapes overlap the new shape will be empty.</doc:para> + <doc:para>Difference: The area of the second shape is subtracted from the first, only works with two objects.</doc:para> + <doc:para>Exclusion: The new shape is the area(s) where none of the objects in the selection overlaped. Only works with two objects.</doc:para> + <doc:para>Division: the first object is split into multiple segments by the second object. Only works with two objects and if multiple paths result they are grouped and the group id is returned.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_divide"> + <arg type="as" name="pieces" direction="out" > + <!-- <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> --> + <doc:doc> + <doc:summary>List of the ids of resulting paths.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Returns the result of cutting the bottom object by all other intersecting paths.</doc:para> + <doc:para>This may make many seperate layers.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_change_level" > + <arg type="s" name="command" direction="in" > + <doc:doc> + <doc:summary>How to change the level</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="objectsmoved" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>True if the objects changed levels. False if they don't(if they were already on top when being raised for example.)</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Will change the level of a selection, respective of other objects in the same layer. Will not affect the overlap of objects in different layers. Will do nothing if the selection contains objects in multiple layers.</doc:para> + <doc:para>There are 4 commands that can be passed in:</doc:para> + <doc:para>"raise" or "lower": Move the selection one level up, or one level down.</doc:para> + <doc:para>"to_top" of "to_bottom": Move the selection above all other objects or below all other objects.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- LAYER FUNCTIONS --> + + <method name="layer_new"> + <arg type="s" name="layername" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The name of the new layer.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Turns all the objects in the selection into paths.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Layers and Levels">Layers and Levels</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="layer_set"> + <arg type="s" name="layer" direction="in" > + <doc:doc> + <doc:summary>The name of any layer.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Sets the layer given as the current layer</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Layers and Levels">Layers and Levels</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="layer_get_all"> + <arg type="as" name="layers" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> + <doc:doc> + <doc:summary>list of layers.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Get a list of all the layers in this document.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Layers and Levels">Layers and Levels</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="layer_change_level" > + <arg type="s" name="command" direction="in" > + <doc:doc> + <doc:summary>How to change the level</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="layermoved" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>True if the layer was moved. False if it was not (if it was already on top when being raised for example.)</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Will change the level of a layer, respective of other layers. Will not affect the relative level of objects within the layer.</doc:para> + <doc:para>There are 4 commands that can be passed in:</doc:para> + <doc:para>"raise" or "lower": Move the layer one level up, or one level down.</doc:para> + <doc:para>"to_top" of "to_bottom": Move the layer above all other layers or below all other layers.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="layer_next" > + <doc:doc> + <doc:description> + <doc:para>Sets the next (or higher) layer as active.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Layers and Levels">Layers and Levels</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="layer_previous" > + <doc:doc> + <doc:description> + <doc:para>Sets the previous (or lower) layer as active.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="Layers and Levels">Layers and Levels</doc:ref></doc:seealso> + </doc:doc> + </method> + + + </interface> +</node> diff --git a/src/extension/dbus/org.inkscape.service.in b/src/extension/dbus/org.inkscape.service.in new file mode 100644 index 000000000..484c8e516 --- /dev/null +++ b/src/extension/dbus/org.inkscape.service.in @@ -0,0 +1,5 @@ +[D-BUS Service] +Name=org.inkscape +Exec=bindir/bin/inkscape + + diff --git a/src/extension/dbus/proposed-interface.xml b/src/extension/dbus/proposed-interface.xml new file mode 100644 index 000000000..c281aff96 --- /dev/null +++ b/src/extension/dbus/proposed-interface.xml @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + * These are some of the proposed functions for the document interface. + * + * It is only used in generating documentation. + * + * None of these methods are implemented. If someone does code one of + * these methods, remove it from here and add it to document-interface.xml. + * + * Authors: + * Soren Berg <Glimmer07@gmail.com> + * + * Copyright (C) 2009 Soren Berg + * + * Released under GNU GPL, read the file 'COPYING' for more information +--> +<node name="/org/inkscape/proposed" + xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd" +> + <interface name="org.inkscape.proposed"> + + <signal name="Signals_Proposal"> + <doc:doc> + <doc:description> + <doc:para>Signals would undoubtedly be a useful thing to have in many circumstances. They are in proposed for two reasons: One, they complicate things for script writers and may conflict with the proposed C wrapper library. Two, I'm not sure how much coding it would take to implement them because I am familiar with neither Dbus signals or Inkscape events. Until I have done more experimenting I don't want to promise anything I'm not sure can be implemented in a timely fashion.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="ObjectResized"> + <arg name="object_name" type="s"> + <doc:doc> + <doc:summary>The id of the object.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an object has been resized.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="ObjectMoved"> + <arg name="object_name" type="s"> + <doc:doc> + <doc:summary>The id of the object.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an object has been moved.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="ObjectStyleModified"> + <arg name="object_name" type="s"> + <doc:doc> + <doc:summary>The id of the object.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when the style of an object has been changed.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="ObjectCreated"> + <arg name="object_name" type="s"> + <doc:doc> + <doc:summary>The id of the object.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an object has been created. Possibly useful for working in conjunction with a live user.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="ObjectAddedToSelection"> + <arg name="object_name" type="s"> + <doc:doc> + <doc:summary>The id of the object.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an object has been added to the selection. Possibly useful for working in conjunction with a live user.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <method name="path_new" > + <arg type="d" name="x" direction="in" > + <doc:doc> + <doc:summary>The x value to begin the path.</doc:summary> + </doc:doc> + </arg> + <arg type="d" name="y" direction="in" > + <doc:doc> + <doc:summary>The y value to begin the path.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Begins a new path, extra nodes can be added with path_append().</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="path_append" > + <arg type="s" name="path" direction="in" > + <doc:doc> + <doc:summary>The name of the path to append to.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="type" direction="in" > + <doc:doc> + <doc:summary>A single letter denoting what type of node is beeing appended.</doc:summary> + </doc:doc> + </arg> + <arg type="ad" name="arguments" direction="in" > + <doc:doc> + <doc:summary>An array of numbers that describe the position and attributes of the path node.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Adds to an existing path. Close the path by sending "z" and no arguments.</doc:para> + <doc:para>You can no longer append to a path if it is closed.</doc:para> + </doc:description> + </doc:doc> + </method> + + +<!-- USE document-subset.h FILES --> + <method name="get_parent" > + <arg type="s" name="type" direction="in" > + <doc:doc> + <doc:summary>Any node with an "id" attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="parentid" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The id of this nodes parent, NULL if toplevel.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Returns the parent of any node. This function along with <doc:ref type="method" to="proposed.get_children">get_children()</doc:ref> can be used to navigate the XML tree. In proposed because I think it might confuse users who don't know about the SVG tree structure. In the main API I have de-emphasized nodes and required no knowledge of internal representation.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="get_children" > + <arg type="s" name="type" direction="in" > + <doc:doc> + <doc:summary>Any node with an "id" attribute.</doc:summary> + </doc:doc> + </arg> + <arg type="as" name="parentid" direction="out" > + <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> + <doc:doc> + <doc:summary>The ids of this nodes children, NULL if bottom level.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Returns the children of any node. This function along with <doc:ref type="method" to="proposed.get_parent">get_parent()</doc:ref> can be used to navigate the XML tree. In proposed because I think it might confuse users who don't know about the SVG tree structure. In the main API I have de-emphasized nodes and required no knowledge of internal representation.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="selection_remove"> + <arg type="s" name="name" direction="in" > + <doc:doc> + <doc:summary>A object to remove from the selection.</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Removes a single object from the selection. In proposed because I already have a ton of selection functions and am not sure people would need this.</doc:para> + </doc:description> + </doc:doc> + </method> + + </interface> +</node> diff --git a/src/extension/dbus/pytester.py b/src/extension/dbus/pytester.py new file mode 100644 index 000000000..d4c41fb47 --- /dev/null +++ b/src/extension/dbus/pytester.py @@ -0,0 +1,290 @@ +##################################################################### +# Python test script for Inkscape DBus API. +# +# Contains many examples of functions and various stress tests etc. +# Multiple tests can be run at once but the output will be a bit chaotic. +# List of test functions can be found at the bottom of the script. +##################################################################### + +import dbus +import random + +##################################################################### +# Various test functions, called at bottom of script +##################################################################### + +def randomrect (document): + document.rectangle( random.randint(0,1000), + random.randint(0,1000), + random.randint(1,100), + random.randint(1,100)) + +def lottarects ( document ): + document.pause_updates() + listy = [] + for x in range(1,2000): + if x == 1000: + print "HALFWAY" + if x == 1: + print "BEGUN" + document.rectangle( 0, 0, 100, 100) + #randomrect(document) + print "DONE" + for x in listy: + print x + selection_set(x) + document.resume_updates() + +def lottaverbs (doc): + doc.pause_updates() + doc.document_set_css ("fill:#ff0000;fill-opacity:.5;stroke:#0000ff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none") + doc.rectangle( 0, 0, 100, 100) + doc.select_all() + doc.selection_copy() + for x in range(1,2000): + if x == 1000: + print "HALFWAY" + if x == 1: + print "BEGUN" + doc.selection_paste() + #doc.rectangle( 0, 0, 100, 100) + doc.resume_updates() + +def testDrawing (doc): + doc.document_set_css ("fill:#000000;fill-opacity:.5;stroke:#000000;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none") + doc.ellipse( 0, 0, 100, 100) + doc.select_all() + doc.selection_copy() + for x in range(1,2000): + if x == 1000: + print "HALFWAY" + if x == 1: + print "BEGUN" + doc.selection_paste() + newrect = doc.selection_get()[0] + doc.set_color(newrect, 255 - x%255, 0, 200, True) + doc.set_color(newrect, 0, 255 - x%75, x%75, False) + doc.mark_as_unmodified() + + +def testcopypaste (document ): + #document.pause_updates() + print document.rectangle (400, 500, 100, 100) + print document.rectangle (200, 200, 100, 100) + document.select_all() + document.selection_copy() + document.selection_paste() + #document.resume_updates() + +def testShapes (doc): + doc.rectangle (0, 0, 100, 100) + doc.ellipse (100, 100, 100, 100) + doc.star (250, 250, 50, 25, 5, False, .9, 1.4) + doc.polygon (350, 350, 50, 0, 5) + doc.line (400,400,500,500) + doc.spiral (550,550,50,3) + +def testMovement (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (0, 100, 100, 100) + rect3 = doc.rectangle (0, 200, 100, 100) + doc.select_all() + doc.move(rect2, 100,100) + +def testImport (doc): + # CHANGE TO LOCAL SVG FILE! + img1 = doc.image(0,0, "/home/soren/chavatar.jpg") + doc.selection_add(img1) + doc.selection_scale(500) + doc.transform(img1, "rotate(30)") + +def testSelections (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (0, 100, 100, 100) + rect3 = doc.rectangle (0, 200, 100, 100) + rect4 = doc.rectangle (0, 300, 100, 100) + + doc.selection_add (rect1) + center = doc.selection_get_center() + for d in center: + print d + doc.selection_to_path() + doc.get_path(rect1) + doc.selection_move(100.0, 100.0) + doc.selection_set(rect2) + doc.selection_move_to(0.0,0.0) + doc.selection_set(rect3) + doc.move(rect4, 500.0, 500.0) + doc.select_all() + doc.selection_to_path() + result = doc.selection_get() + print len(result) + for d in result: + print d + +def testLevels (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (20, 20, 100, 100) + rect3 = doc.rectangle (40, 40, 100, 100) + rect4 = doc.rectangle (60, 60, 100, 100) + + doc.selection_set(rect1) + doc.selection_change_level("raise") + + doc.selection_set(rect4) + doc.selection_change_level("to_bottom") + +def testCombinations (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (20, 20, 100, 100) + rect3 = doc.rectangle (40, 40, 100, 100) + rect4 = doc.rectangle (60, 60, 100, 100) + rect5 = doc.rectangle (80, 80, 100, 100) + rect6 = doc.rectangle (100, 100, 100, 100) + rect7 = doc.rectangle (120, 120, 100, 100) + rect8 = doc.rectangle (140, 140, 100, 100) + rect9 = doc.rectangle (160, 160, 100, 100) + rect10 = doc.rectangle (180, 180, 100, 100) + + doc.selection_set_list([rect1, rect2]) + print doc.selection_combine("union") + doc.selection_set_list([rect3, rect4]) + print doc.selection_combine("intersection") + doc.selection_set_list([rect5, rect6]) + print doc.selection_combine("difference") + doc.selection_set_list([rect7, rect8]) + print doc.selection_combine("exclusion") + doc.selection_set_list([rect9, rect10]) + for d in doc.selection_divide(): + print d + +def testTransforms (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (20, 20, 100, 100) + doc.set_attribute(rect1, "transform", "matrix(0.08881734,0.94288151,-0.99604793,0.68505564,245.36153,118.60315)") + doc.selection_set(rect1) + + doc.selection_move_to(200, 200) + +def testLayer (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + print doc.new_layer() + rect2 = doc.rectangle (20, 20, 100, 100) + +def testGetSelection (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (20, 20, 100, 100) + rect3 = doc.rectangle (40, 40, 100, 100) + doc.select_all() + result = doc.selection_get() + print result + print len(result) + for d in result: + print d + + +def testDocStyle (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + doc.document_set_css ("fill:#ff0000;fill-opacity:.5;stroke:#0000ff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none") + rect2 = doc.rectangle (20, 20, 100, 100) + doc.document_set_css ("fill:#ffff00;fill-opacity:1;stroke:#009002;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none") + rect3 = doc.rectangle (40, 40, 100, 100) + doc.document_set_css ("fill:#00ff00;fill-opacity:1") + rect4 = doc.rectangle (60, 60, 100, 100) + +def testStyle (doc): + doc.document_set_css ("fill:#ffff00;fill-opacity:1;stroke:#009002;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none") + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (20, 20, 100, 100) + rect3 = doc.rectangle (40, 40, 100, 100) + rect4 = doc.rectangle (60, 60, 100, 100) + + doc.modify_css (rect3, "fill-opacity", ".5") + doc.merge_css (rect4, "fill:#0000ff;fill-opacity:.25;") + print doc.get_css (rect4) + +def testLayers (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + layer1 = doc.layer_new() + layer2 = doc.layer_new() + rect2 = doc.rectangle (20, 20, 100, 100) + rect3 = doc.rectangle (40, 40, 100, 100) + doc.selection_add(rect3) + doc.selection_move_to_layer(layer1) + +def testLoadSave (doc): + doc.load("/home/soren/testfile.svg") + rect2 = doc.rectangle (0, 0, 200, 200) + doc.save_as("/home/soren/testsave.svg") + rect1 = doc.rectangle (20, 20, 200, 200) + doc.save() + rect3 = doc.rectangle (40, 40, 200, 200) + doc.save_as("/home/soren/testsave2.svg") + +def testArray (doc): + rect1 = doc.rectangle (0, 0, 100, 100) + rect2 = doc.rectangle (20, 20, 100, 100) + rect3 = doc.rectangle (40, 40, 100, 100) + doc.selection_set_list([rect1, rect2, rect3]) + +def testPath (doc): + cr1 = doc.ellipse(0,0,50,50) + print doc.get_path(cr1) + doc.object_to_path(cr1) + print doc.get_path(cr1) + #doc.get_node_coordinates(cr1) + +# Needs work. +def testText(doc): + print doc.text(200, 200, "svg:text") + + +##################################################################### +# Setup bus connection, create documents. +##################################################################### + +# Connect to bus +bus = dbus.SessionBus() + +# Get interface for default document +inkdoc1 = bus.get_object('org.inkscape', '/org/inkscape/desktop_0') +doc1 = dbus.Interface(inkdoc1, dbus_interface="org.inkscape.document") + +# Create new window and get the interface for that. (optional) +inkapp = bus.get_object('org.inkscape', + '/org/inkscape/application') +desk2 = inkapp.desktop_new(dbus_interface='org.inkscape.application') +inkdoc2 = bus.get_object('org.inkscape', desk2) +doc2 = dbus.Interface(inkdoc2, dbus_interface="org.inkscape.document") + + +##################################################################### +# Call desired test functions +##################################################################### + +#lottaverbs (doc1) +#lottarects (doc1) +#testDrawing (doc1) + +#doc1.pause_updates() + +testShapes(doc1) +#testMovement(doc1) +#testImport(doc1) # EDIT FUNCTION TO OPEN EXISTING FILE! +#testcopypaste (doc1) +#testTransforms (doc1) +#testDocStyle(doc1) +#testLayers(doc1) +#testLoadSave(doc1) +#testArray(doc1) +#testSelections(doc1) +#testCombinations(doc1) +#testText(doc1) +#testPath(doc1) + +#doc1.resume_updates + + +# Prevents asking if you want to save when closing document. +doc1.mark_as_unmodified() + diff --git a/src/extension/dbus/wrapper/inkdbus.pc.in b/src/extension/dbus/wrapper/inkdbus.pc.in new file mode 100644 index 000000000..2bdfb75eb --- /dev/null +++ b/src/extension/dbus/wrapper/inkdbus.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +bindir=@bindir@ +includedir=@includedir@ + +Cflags: -I${includedir}/libinkdbus-0.1 +Requires: gobject-2.0 dbus-glib-1 +Libs: -L${libdir} -linkdbus + +Name: inkdbus +Description: Inkscape DBus Interface Wrapper +Version: @VERSION@ + diff --git a/src/extension/dbus/wrapper/inkscape-dbus-wrapper.c b/src/extension/dbus/wrapper/inkscape-dbus-wrapper.c new file mode 100644 index 000000000..b59ee746b --- /dev/null +++ b/src/extension/dbus/wrapper/inkscape-dbus-wrapper.c @@ -0,0 +1,784 @@ +#include "inkscape-dbus-wrapper.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + + + +#include "document-client-glue.h" +#include <dbus/dbus-glib.h> +#include <dbus/dbus.h> + +// (static.*(\n[^}]*)*(async)+.*(\n[^}]*)*})|typedef void .*; +// http://www.josephkahn.com/music/index.xml + +/* PRIVATE get a connection to the session bus */ +DBusGConnection * +dbus_get_connection() { + GError *error = NULL; + DBusGConnection *connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error) { + fprintf(stderr, "Failed to get connection"); + return NULL; + } + else + return connection; +} + +/* PRIVATE create a proxy object for a bus.*/ +DBusGProxy * +dbus_get_proxy(DBusGConnection *connection) { + return dbus_g_proxy_new_for_name (connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); +} + +#if 0 +/* PRIVATE register an object on a bus */ +static gpointer +dbus_register_object (DBusGConnection *connection, + DBusGProxy * proxy, + GType object_type, + const DBusGObjectInfo *info, + const gchar *path) +{ + GObject *object = (GObject*)g_object_new (object_type, NULL); + dbus_g_object_type_install_info (object_type, info); + dbus_g_connection_register_g_object (connection, path, object); + return object; +} +#endif + +/**************************************************************************** + DOCUMENT INTERFACE CLASS STUFF +****************************************************************************/ + +struct _DocumentInterface { + GObject parent; + DBusGProxy * proxy; +}; + +G_DEFINE_TYPE(DocumentInterface, document_interface, G_TYPE_OBJECT) + +static void +document_interface_finalize (GObject *object) +{ + G_OBJECT_CLASS (document_interface_parent_class)->finalize (object); +} + + +static void +document_interface_class_init (DocumentInterfaceClass *klass) +{ + GObjectClass *object_class; + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = document_interface_finalize; +} + +static void +document_interface_init (DocumentInterface *object) +{ + object->proxy = NULL; +} + + +DocumentInterface * +document_interface_new (void) +{ + return (DocumentInterface*)g_object_new (TYPE_DOCUMENT_INTERFACE, NULL); +} + +DocumentInterface * +inkscape_desktop_init_dbus () +{ + DBusGConnection *connection; + GError *error; + DBusGProxy *proxy; + + g_type_init (); + + error = NULL; + connection = dbus_g_bus_get (DBUS_BUS_SESSION, + &error); + if (connection == NULL) + { + g_printerr ("Failed to open connection to bus: %s\n", + error->message); + g_error_free (error); + exit (1); + } + + proxy = dbus_g_proxy_new_for_name (connection, + "org.inkscape", + "/org/inkscape/desktop_0", + "org.inkscape.document"); + + DocumentInterface * inkdesk = (DocumentInterface *)g_object_new (TYPE_DOCUMENT_INTERFACE, NULL); + inkdesk->proxy = proxy; + return inkdesk; +} + + +//static +gboolean +inkscape_delete_all (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_delete_all (proxy, error); +} + +//static +gboolean +inkscape_call_verb (DocumentInterface *doc, const char * IN_verbid, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_call_verb(proxy, IN_verbid, error); +} + + +//static +gchar * +inkscape_rectangle (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_width, const gint IN_height, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_rectangle (proxy, IN_x, IN_y, IN_width, IN_height, &OUT_object_name, error); + return OUT_object_name; +} + +//static +char * +inkscape_ellipse (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_width, const gint IN_height, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_ellipse (proxy, IN_x, IN_y, IN_width, IN_height, &OUT_object_name, error); + return OUT_object_name; +} + +//static +char * +inkscape_polygon (DocumentInterface *doc, const gint IN_cx, const gint IN_cy, const gint IN_radius, const gint IN_rotation, const gint IN_sides, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_polygon (proxy, IN_cx, IN_cy, IN_radius, IN_rotation, IN_sides, &OUT_object_name, error); + return OUT_object_name; +} + +//static +char * +inkscape_star (DocumentInterface *doc, const gint IN_cx, const gint IN_cy, const gint IN_r1, const gint IN_r2, const gdouble IN_arg1, const gdouble IN_arg2, const gint IN_sides, const gdouble IN_rounded, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_star (proxy, IN_cx, IN_cy, IN_r1, IN_r2, IN_arg1, IN_arg2, IN_sides, IN_rounded, &OUT_object_name, error); + return OUT_object_name; +} + +//static +char * +inkscape_spiral (DocumentInterface *doc, const gint IN_cx, const gint IN_cy, const gint IN_r, const gint IN_revolutions, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_spiral (proxy, IN_cx, IN_cy, IN_r, IN_revolutions, &OUT_object_name, error); + return OUT_object_name; +} + +//static +char * +inkscape_line (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_x2, const gint IN_y2, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_line (proxy, IN_x, IN_y, IN_x2, IN_y2, &OUT_object_name, error); + return OUT_object_name; +} + +//static +gboolean +inkscape_text (DocumentInterface *doc, const gint IN_x, const gint IN_y, const char * IN_text, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_text (proxy, IN_x, IN_y, IN_text, error); +} + +//static +char * +inkscape_image (DocumentInterface *doc, const gint IN_x, const gint IN_y, const char * IN_text, GError **error) +{ + char * OUT_object_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_image (proxy, IN_x, IN_y, IN_text, &OUT_object_name, error); + return OUT_object_name; +} + +//static +char * +inkscape_node (DocumentInterface *doc, const char * IN_svgtype, GError **error) +{ + char *OUT_node_name; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_node (proxy, IN_svgtype, &OUT_node_name, error); + return OUT_node_name; +} + +//static +gdouble +inkscape_document_get_width (DocumentInterface *doc, GError **error) +{ + gdouble OUT_val; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_document_get_width (proxy, &OUT_val, error); + return OUT_val; +} + +//static +gdouble +inkscape_document_get_height (DocumentInterface *doc, GError **error) +{ + gdouble OUT_val; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_document_get_height (proxy, &OUT_val, error); + return OUT_val; +} + +//static +char * +inkscape_document_get_css (DocumentInterface *doc, GError **error) +{ + char * OUT_css; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_document_get_css (proxy, &OUT_css, error); + return OUT_css; +} + +//static +gboolean +inkscape_document_set_css (DocumentInterface *doc, const char * IN_stylestring, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_document_set_css (proxy, IN_stylestring, error); +} + +//static +gboolean +inkscape_document_merge_css (DocumentInterface *doc, const char * IN_stylestring, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_document_merge_css (proxy, IN_stylestring, error); +} + +//static +gboolean +inkscape_document_resize_to_fit_selection (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_document_resize_to_fit_selection (proxy, error); +} + +//static +gboolean +inkscape_set_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, const char * IN_newval, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_set_attribute (proxy, IN_shape, IN_attribute, IN_newval, error); +} + +//static +gboolean +inkscape_set_int_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, const gint IN_newval, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_set_int_attribute (proxy, IN_shape, IN_attribute, IN_newval, error); +} + +//static +gboolean +inkscape_set_double_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, const gdouble IN_newval, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_set_double_attribute (proxy, IN_shape, IN_attribute, IN_newval, error); +} + +//static +char * +inkscape_get_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, GError **error) +{ + char * OUT_val; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_get_attribute (proxy, IN_shape, IN_attribute, &OUT_val, error); + return OUT_val; +} + +//static +gboolean +inkscape_move (DocumentInterface *doc, const char * IN_shape, const gdouble IN_x, const gdouble IN_y, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_move (proxy, IN_shape, IN_x, IN_y, error); +} + +//static +gboolean +inkscape_move_to (DocumentInterface *doc, const char * IN_shape, const gdouble IN_x, const gdouble IN_y, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_move_to (proxy, IN_shape, IN_x, IN_y, error); +} + +//static +gboolean +inkscape_object_to_path (DocumentInterface *doc, const char * IN_objectname, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_object_to_path (proxy, IN_objectname, error); +} + +//static +char * +inkscape_get_path (DocumentInterface *doc, const char * IN_shape, GError **error) +{ + char * OUT_val; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_get_path (proxy, IN_shape, &OUT_val, error); + return OUT_val; +} + +//static +gboolean +inkscape_transform (DocumentInterface *doc, const char * IN_shape, const char * IN_transformstr, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_transform (proxy, IN_shape, IN_transformstr, error); +} + +//static +char * +inkscape_get_css (DocumentInterface *doc, const char * IN_shape, GError **error) +{ + char * OUT_css; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_get_css (proxy, IN_shape, &OUT_css, error); + return OUT_css; +} + +//static +gboolean +inkscape_modify_css (DocumentInterface *doc, const char * IN_shape, const char * IN_cssattrib, const char * IN_newval, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_modify_css (proxy, IN_shape, IN_cssattrib, IN_newval, error); +} + +//static +gboolean +inkscape_merge_css (DocumentInterface *doc, const char * IN_shape, const char * IN_stylestring, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_merge_css (proxy, IN_shape, IN_stylestring, error); +} + +//static +gboolean +inkscape_set_color (DocumentInterface *doc, const char * IN_shape, const gint IN_red, const gint IN_green, const gint IN_blue, const gboolean IN_fill, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_set_color (proxy, IN_shape, IN_red, IN_green, IN_blue, IN_fill, error); +} + +//static +gboolean +inkscape_move_to_layer (DocumentInterface *doc, const char * IN_objectname, const char * IN_layername, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_move_to_layer (proxy, IN_objectname, IN_layername, error); +} + +//static +GArray* +inkscape_get_node_coordinates (DocumentInterface *doc, const char * IN_shape, GError **error) +{ + GArray* OUT_points; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_get_node_coordinates (proxy, IN_shape, &OUT_points, error); + return OUT_points; +} + +//static +gboolean +inkscape_save (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_save (proxy, error); +} + +//static +gboolean +inkscape_save_as (DocumentInterface *doc, const char * IN_pathname, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_save_as (proxy, IN_pathname, error); +} + +//static +gboolean +inkscape_load (DocumentInterface *doc, const char * IN_pathname, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_load (proxy, IN_pathname, error); +} + +//static +gboolean +inkscape_mark_as_unmodified (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_mark_as_unmodified (proxy, error); +} + +//static +gboolean +inkscape_close (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_close (proxy, error); +} + +//static +gboolean +inkscape_inkscape_exit (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_exit (proxy, error); +} + +//static +gboolean +inkscape_undo (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_undo (proxy, error); +} + +//static +gboolean +inkscape_redo (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_redo (proxy, error); +} + +//static +gboolean +inkscape_pause_updates (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_pause_updates (proxy, error); +} + +//static +gboolean +inkscape_resume_updates (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_resume_updates (proxy, error); +} + +//static +gboolean +inkscape_update (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_update (proxy, error); +} + +//static +char ** +inkscape_selection_get (DocumentInterface *doc, GError **error) +{ + char ** OUT_listy; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_selection_get (proxy, &OUT_listy, error); + return OUT_listy; +} + +//static +gboolean +inkscape_selection_add (DocumentInterface *doc, const char * IN_name, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_add (proxy, IN_name, error); +} + +//static +gboolean +inkscape_selection_add_list (DocumentInterface *doc, const char ** IN_name, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_add_list (proxy, IN_name, error); +} + +//static +gboolean +inkscape_selection_set (DocumentInterface *doc, const char * IN_name, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_set (proxy, IN_name, error); +} + +//static +gboolean +inkscape_selection_set_list (DocumentInterface *doc, const char ** IN_name, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_set_list (proxy, IN_name, error); +} + +//static +gboolean +inkscape_selection_rotate (DocumentInterface *doc, const gint IN_angle, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_rotate (proxy, IN_angle, error); +} + +//static +gboolean +inkscape_selection_delete (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_delete (proxy, error); +} + +//static +gboolean +inkscape_selection_clear (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_clear (proxy, error); +} + +//static +gboolean +inkscape_select_all (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_select_all (proxy, error); +} + +//static +gboolean +inkscape_select_all_in_all_layers (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_select_all_in_all_layers (proxy, error); +} + +//static +gboolean +inkscape_selection_box (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_x2, const gint IN_y2, const gboolean IN_replace, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_box (proxy, IN_x, IN_y, IN_x2, IN_y2, IN_replace, error); +} + +//static +gboolean +inkscape_selection_invert (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_invert (proxy, error); +} + +//static +gboolean +inkscape_selection_group (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_group (proxy, error); +} + +//static +gboolean +inkscape_selection_ungroup (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_ungroup (proxy, error); +} + +//static +gboolean +inkscape_selection_cut (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_cut (proxy, error); +} + +//static +gboolean +inkscape_selection_copy (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_copy (proxy, error); +} + +//static +gboolean +inkscape_selection_paste (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_paste (proxy, error); +} + +//static +gboolean +inkscape_selection_scale (DocumentInterface *doc, const gdouble IN_grow, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_scale (proxy, IN_grow, error); +} + +//static +gboolean +inkscape_selection_move (DocumentInterface *doc, const gdouble IN_x, const gdouble IN_y, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_move (proxy, IN_x, IN_y, error); +} + +//static +gboolean +inkscape_selection_move_to (DocumentInterface *doc, const gdouble IN_x, const gdouble IN_y, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_move_to (proxy, IN_x, IN_y, error); +} + +//static +gboolean +inkscape_selection_move_to_layer (DocumentInterface *doc, const char * IN_layer, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_move_to_layer (proxy, IN_layer, error); +} + +//static +GArray * +inkscape_selection_get_center (DocumentInterface *doc, GError **error) +{ + GArray* OUT_centerpoint; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_selection_get_center (proxy, &OUT_centerpoint, error); + return OUT_centerpoint; +} + +//static +gboolean +inkscape_selection_to_path (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_selection_to_path (proxy, error); +} + +//static +char * +inkscape_selection_combine (DocumentInterface *doc, const char * IN_type, GError **error) +{ + char * OUT_newpath; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_selection_combine (proxy, IN_type, &OUT_newpath, error); + return OUT_newpath; +} + +//static +char ** +inkscape_selection_divide (DocumentInterface *doc, GError **error) +{ + char ** OUT_pieces; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_selection_divide (proxy, &OUT_pieces, error); + return OUT_pieces; +} + +//static +gboolean +inkscape_selection_change_level (DocumentInterface *doc, const char * IN_command, GError **error) +{ + gboolean OUT_objectsmoved; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_selection_change_level (proxy, IN_command, &OUT_objectsmoved, error); + return OUT_objectsmoved; +} + +//static +char * +inkscape_layer_new (DocumentInterface *doc, GError **error) +{ + char * OUT_layername; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_layer_new (proxy, &OUT_layername, error); + return OUT_layername; +} + +//static +gboolean +inkscape_layer_set (DocumentInterface *doc, const char * IN_layer, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_layer_set (proxy, IN_layer, error); +} + +//static +char ** +inkscape_layer_get_all (DocumentInterface *doc, GError **error) +{ + char ** OUT_layers; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_layer_get_all (proxy, &OUT_layers, error); + return OUT_layers; +} + +//static +gboolean +inkscape_layer_change_level (DocumentInterface *doc, const char * IN_command, GError **error) +{ + gboolean OUT_layermoved; + DBusGProxy *proxy = doc->proxy; + org_inkscape_document_layer_change_level (proxy, IN_command, &OUT_layermoved, error); + return OUT_layermoved; +} + +//static +gboolean +inkscape_layer_next (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_layer_next (proxy, error); +} + +//static +gboolean +inkscape_layer_previous (DocumentInterface *doc, GError **error) +{ + DBusGProxy *proxy = doc->proxy; + return org_inkscape_document_layer_previous (proxy, error); +} + +/* +int +main (int argc, char** argv) +{ + gchar * result; + GError *error = NULL; + DocumentInterface * doc = inkscape_desktop_init_dbus (); + result = rectangle (doc->proxy, 10, 10, 100, 100, &error); + printf("RESULT: %s\n", result); + + //dbus_g_proxy_call(doc->proxy, "rectangle", &error, G_TYPE_INT, 100, G_TYPE_INT, 100, G_TYPE_INT, 100, G_TYPE_INT, 100, G_TYPE_INVALID, G_TYPE_INVALID); + printf("yes\n"); +} +*/ + diff --git a/src/extension/dbus/wrapper/inkscape-dbus-wrapper.h b/src/extension/dbus/wrapper/inkscape-dbus-wrapper.h new file mode 100644 index 000000000..c314bf6f8 --- /dev/null +++ b/src/extension/dbus/wrapper/inkscape-dbus-wrapper.h @@ -0,0 +1,344 @@ +#ifndef INKSCAPE_EXTENSION_DOCUMENT_INTERFACE_H_ +#define INKSCAPE_EXTENSION_DOCUMENT_INTERFACE_H_ + +#include <glib.h> +#include <glib-object.h> + +//#include "document-client-glue-mod.h" + +//#include <dbus/dbus-glib-bindings.h> +//#include <dbus/dbus-glib-lowlevel.h> + +#define DBUS_DOCUMENT_INTERFACE_PATH "/org/inkscape/document" + +#define TYPE_DOCUMENT_INTERFACE (document_interface_get_type ()) +#define DOCUMENT_INTERFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_DOCUMENT_INTERFACE, DocumentInterface)) +#define DOCUMENT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_DOCUMENT_INTERFACE, DocumentInterfaceClass)) +#define IS_DOCUMENT_INTERFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_DOCUMENT_INTERFACE)) +#define IS_DOCUMENT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_DOCUMENT_INTERFACE)) +#define DOCUMENT_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_DOCUMENT_INTERFACE, DocumentInterfaceClass)) + +G_BEGIN_DECLS + +typedef struct _DocumentInterface DocumentInterface; +typedef struct _DocumentInterfaceClass DocumentInterfaceClass; + +struct _DocumentInterface; + +struct _DocumentInterfaceClass { + GObjectClass parent; +}; + + +DocumentInterface *document_interface_new (void); +GType document_interface_get_type (void); + + + +DocumentInterface * +inkscape_desktop_init_dbus (); + +//static +gboolean +inkscape_delete_all (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_call_verb (DocumentInterface *doc, const char * IN_verbid, GError **error); + + +//static +gchar * +inkscape_rectangle (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_width, const gint IN_height, GError **error); + +//static +char * +inkscape_ellipse (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_width, const gint IN_height, GError **error); + +//static +char * +inkscape_polygon (DocumentInterface *doc, const gint IN_cx, const gint IN_cy, const gint IN_radius, const gint IN_rotation, const gint IN_sides, GError **error); + +//static +char * +inkscape_star (DocumentInterface *doc, const gint IN_cx, const gint IN_cy, const gint IN_r1, const gint IN_r2, const gdouble IN_arg1, const gdouble IN_arg2, const gint IN_sides, const gdouble IN_rounded, GError **error); + +//static +char * +inkscape_spiral (DocumentInterface *doc, const gint IN_cx, const gint IN_cy, const gint IN_r, const gint IN_revolutions, GError **error); + +//static +char * +inkscape_line (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_x2, const gint IN_y2, GError **error); + +//static +gboolean +inkscape_text (DocumentInterface *doc, const gint IN_x, const gint IN_y, const char * IN_text, GError **error); + +//static +char * +inkscape_image (DocumentInterface *doc, const gint IN_x, const gint IN_y, const char * IN_text, GError **error); + +//static +char * +inkscape_node (DocumentInterface *doc, const char * IN_svgtype, GError **error); + +//static +gdouble +inkscape_document_get_width (DocumentInterface *doc, GError **error); + +//static +gdouble +inkscape_document_get_height (DocumentInterface *doc, GError **error); + +//static +char * +inkscape_document_get_css (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_document_set_css (DocumentInterface *doc, const char * IN_stylestring, GError **error); + +//static +gboolean +inkscape_document_merge_css (DocumentInterface *doc, const char * IN_stylestring, GError **error); + +//static +gboolean +inkscape_document_resize_to_fit_selection (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_set_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, const char * IN_newval, GError **error); + +//static +gboolean +inkscape_set_int_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, const gint IN_newval, GError **error); + +//static +gboolean +inkscape_set_double_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, const gdouble IN_newval, GError **error); + +//static +char * +inkscape_get_attribute (DocumentInterface *doc, const char * IN_shape, const char * IN_attribute, GError **error); + +//static +gboolean +inkscape_move (DocumentInterface *doc, const char * IN_shape, const gdouble IN_x, const gdouble IN_y, GError **error); + +//static +gboolean +inkscape_move_to (DocumentInterface *doc, const char * IN_shape, const gdouble IN_x, const gdouble IN_y, GError **error); + +//static +gboolean +inkscape_object_to_path (DocumentInterface *doc, const char * IN_objectname, GError **error); + +//static +char * +inkscape_get_path (DocumentInterface *doc, const char * IN_shape, GError **error); + +//static +gboolean +inkscape_transform (DocumentInterface *doc, const char * IN_shape, const char * IN_transformstr, GError **error); + +//static +char * +inkscape_get_css (DocumentInterface *doc, const char * IN_shape, GError **error); + +//static +gboolean +inkscape_modify_css (DocumentInterface *doc, const char * IN_shape, const char * IN_cssattrib, const char * IN_newval, GError **error); + +//static +gboolean +inkscape_inkscape_merge_css (DocumentInterface *doc, const char * IN_shape, const char * IN_stylestring, GError **error); + +//static +gboolean +inkscape_set_color (DocumentInterface *doc, const char * IN_shape, const gint IN_red, const gint IN_green, const gint IN_blue, const gboolean IN_fill, GError **error); + +//static +gboolean +inkscape_move_to_layer (DocumentInterface *doc, const char * IN_objectname, const char * IN_layername, GError **error); + +//static +GArray* +inkscape_get_node_coordinates (DocumentInterface *doc, const char * IN_shape, GError **error); + +//static +gboolean +inkscape_save (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_save_as (DocumentInterface *doc, const char * IN_pathname, GError **error); + +//static +gboolean +inkscape_load (DocumentInterface *doc, const char * IN_pathname, GError **error); + +//static +gboolean +inkscape_mark_as_unmodified (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_close (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_inkscape_exit (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_undo (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_redo (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_pause_updates (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_resume_updates (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_update (DocumentInterface *doc, GError **error); + +//static +char ** +inkscape_selection_get (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_add (DocumentInterface *doc, const char * IN_name, GError **error); + +//static +gboolean +inkscape_selection_add_list (DocumentInterface *doc, const char ** IN_name, GError **error); + +//static +gboolean +inkscape_selection_set (DocumentInterface *doc, const char * IN_name, GError **error); + +//static +gboolean +inkscape_selection_set_list (DocumentInterface *doc, const char ** IN_name, GError **error); + +//static +gboolean +inkscape_selection_rotate (DocumentInterface *doc, const gint IN_angle, GError **error); + +//static +gboolean +inkscape_selection_delete (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_clear (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_select_all (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_select_all_in_all_layers (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_box (DocumentInterface *doc, const gint IN_x, const gint IN_y, const gint IN_x2, const gint IN_y2, const gboolean IN_replace, GError **error); + +//static +gboolean +inkscape_selection_invert (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_group (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_ungroup (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_cut (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_copy (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_paste (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_scale (DocumentInterface *doc, const gdouble IN_grow, GError **error); + +//static +gboolean +inkscape_selection_move (DocumentInterface *doc, const gdouble IN_x, const gdouble IN_y, GError **error); + +//static +gboolean +inkscape_selection_move_to (DocumentInterface *doc, const gdouble IN_x, const gdouble IN_y, GError **error); + +//static +gboolean +inkscape_selection_move_to_layer (DocumentInterface *doc, const char * IN_layer, GError **error); + +//static +GArray * +inkscape_selection_get_center (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_to_path (DocumentInterface *doc, GError **error); + +//static +char * +inkscape_selection_combine (DocumentInterface *doc, const char * IN_type, GError **error); + +//static +char ** +inkscape_selection_divide (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_selection_change_level (DocumentInterface *doc, const char * IN_command, GError **error); + +//static +char * +inkscape_layer_new (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_layer_set (DocumentInterface *doc, const char * IN_layer, GError **error); + +//static +char ** +inkscape_layer_get_all (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_layer_change_level (DocumentInterface *doc, const char * IN_command, GError **error); + +//static +gboolean +inkscape_layer_next (DocumentInterface *doc, GError **error); + +//static +gboolean +inkscape_layer_previous (DocumentInterface *doc, GError **error); + +G_END_DECLS + +#endif // INKSCAPE_EXTENSION_DOCUMENT_INTERFACE_H_ diff --git a/src/extension/init.cpp b/src/extension/init.cpp index 777e3d718..f58c8cbe6 100644 --- a/src/extension/init.cpp +++ b/src/extension/init.cpp @@ -55,6 +55,9 @@ #endif #include "preferences.h" #include "io/sys.h" +#ifdef WITH_DBUS +#include "dbus/dbus-init.h" +#endif #ifdef WITH_IMAGE_MAGICK #include "internal/bitmap/adaptiveThreshold.h" @@ -186,6 +189,10 @@ init() Internal::GimpGrad::init(); Internal::Grid::init(); +#ifdef WITH_DBUS + Dbus::init(); +#endif + /* Raster Effects */ #ifdef WITH_IMAGE_MAGICK Internal::Bitmap::AdaptiveThreshold::init(); diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp index 61760e9d9..16adebac3 100644 --- a/src/extension/internal/cairo-ps-out.cpp +++ b/src/extension/internal/cairo-ps-out.cpp @@ -195,12 +195,7 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con // Create LaTeX file (if requested) if (new_textToLaTeX) { - gchar * tex_filename; - //strip filename of ".ps", do not add ".tex" here. - gsize n = g_str_has_suffix(filename, ".ps") ? strlen(filename)-3 : strlen(filename); - tex_filename = g_strndup(filename, n); - ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_areaDrawing, new_areaPage, false); - g_free(tex_filename); + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, false); if (!ret) throw Inkscape::Extension::Output::save_failed(); @@ -283,12 +278,7 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co // Create LaTeX file (if requested) if (new_textToLaTeX) { - gchar * tex_filename; - //strip filename of ".eps", do not add ".tex" here. - gsize n = g_str_has_suffix(filename, ".eps") ? strlen(filename)-4 : strlen(filename); - tex_filename = g_strndup(filename, n); - ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_areaDrawing, new_areaPage, false); - g_free(tex_filename); + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_areaDrawing, new_areaPage, false); if (!ret) throw Inkscape::Extension::Output::save_failed(); diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index 8b40f60b4..80152c298 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -1120,11 +1120,11 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain SPLinearGradient *lg=SP_LINEARGRADIENT(paintserver); - sp_gradient_ensure_vector(SP_GRADIENT(lg)); // when exporting from commandline, vector is not built + SP_GRADIENT(lg)->ensureVector(); // when exporting from commandline, vector is not built Geom::Point p1 (lg->x1.computed, lg->y1.computed); Geom::Point p2 (lg->x2.computed, lg->y2.computed); - if (pbox && SP_GRADIENT(lg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (pbox && SP_GRADIENT(lg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { // convert to userspace Geom::Matrix bbox2user(pbox->x1 - pbox->x0, 0, 0, pbox->y1 - pbox->y0, pbox->x0, pbox->y0); p1 *= bbox2user; @@ -1144,12 +1144,12 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain SPRadialGradient *rg=SP_RADIALGRADIENT(paintserver); - sp_gradient_ensure_vector(SP_GRADIENT(rg)); // when exporting from commandline, vector is not built + SP_GRADIENT(rg)->ensureVector(); // when exporting from commandline, vector is not built Geom::Point c (rg->cx.computed, rg->cy.computed); Geom::Point f (rg->fx.computed, rg->fy.computed); double r = rg->r.computed; - if (pbox && SP_GRADIENT(rg)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) + if (pbox && SP_GRADIENT(rg)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) apply_bbox2user = true; // create radial gradient pattern @@ -1172,7 +1172,7 @@ CairoRenderContext::_createPatternForPaintServer(SPPaintServer const *const pain SPGradient *g = SP_GRADIENT(paintserver); // set extend type - SPGradientSpread spread = sp_gradient_get_spread(g); + SPGradientSpread spread = g->fetchSpread(); switch (spread) { case SP_GRADIENT_SPREAD_REPEAT: { cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp index 808590e04..e8eff20b7 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -213,12 +213,7 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, // Create LaTeX file (if requested) if (new_textToLaTeX) { - gchar * tex_filename; - //strip filename of ".pdf", do not add ".tex" here. - gsize n = g_str_has_suffix(filename, ".pdf") ? strlen(filename)-4 : strlen(filename); - tex_filename = g_strndup(filename, n); - ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_exportDrawing, new_exportCanvas, true); - g_free(tex_filename); + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_exportDrawing, new_exportCanvas, true); if (!ret) throw Inkscape::Extension::Output::save_failed(); diff --git a/src/extension/internal/emf-win32-inout.cpp b/src/extension/internal/emf-win32-inout.cpp index 7ed1a9d66..4cace7db7 100644 --- a/src/extension/internal/emf-win32-inout.cpp +++ b/src/extension/internal/emf-win32-inout.cpp @@ -711,7 +711,7 @@ assert_empty_path(PEMF_CALLBACK_DATA d, const char * /*fun*/) static int CALLBACK -myEnhMetaFileProc(HDC /*hDC*/, HANDLETABLE * /*lpHTable*/, ENHMETARECORD *lpEMFR, int /*nObj*/, LPARAM lpData) +myEnhMetaFileProc(HDC /*hDC*/, HANDLETABLE * /*lpHTable*/, ENHMETARECORD const *lpEMFR, int /*nObj*/, LPARAM lpData) { PEMF_CALLBACK_DATA d; SVGOStringStream tmp_outsvg; @@ -2331,7 +2331,8 @@ EmfWin32::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.pDesc[lstrlen(d.pDesc)] = '#'; } - EnumEnhMetaFile(NULL, hemf, myEnhMetaFileProc, (LPVOID) &d, NULL); + // This ugly reinterpret_cast is to prevent old versions of gcc from whining about a mismatch in the const-ness of the arguments + EnumEnhMetaFile(NULL, hemf, reinterpret_cast<ENHMFENUMPROC>(myEnhMetaFileProc), (LPVOID) &d, NULL); DeleteEnhMetaFile(hemf); } else { diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp index a1295406c..df7f3481c 100644 --- a/src/extension/internal/gdkpixbuf-input.cpp +++ b/src/extension/internal/gdkpixbuf-input.cpp @@ -2,6 +2,7 @@ # include <config.h> #endif #include <glib/gprintf.h> +#include <glibmm/i18n.h> #include "document-private.h" #include <dir-util.h> #include "extension/input.h" @@ -143,10 +144,10 @@ GdkpixbufInput::init(void) if (strcmp(extensions[i], "svg.gz") == 0) { continue; } - + gchar *caption = g_strdup_printf(_("%s GDK pixbuf Input"), name); gchar *xmlString = g_strdup_printf( "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n" - "<name>" N_("%s GDK pixbuf Input") "</name>\n" + "<name>%s</name>\n" "<id>org.inkscape.input.gdkpixbuf.%s</id>\n" "<param name='link' type='optiongroup' appearance='full' _gui-text='" N_("Link or embed image:") "' >\n" "<_option value='embed'>" N_("embed") "</_option>\n" @@ -160,7 +161,7 @@ GdkpixbufInput::init(void) "<filetypetooltip>%s</filetypetooltip>\n" "</input>\n" "</inkscape-extension>", - name, + caption, extensions[i], extensions[i], mimetypes[j], @@ -171,6 +172,7 @@ GdkpixbufInput::init(void) Inkscape::Extension::build_from_mem(xmlString, new GdkpixbufInput()); g_free(xmlString); + g_free(caption); }} g_free(name); diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp index a4d348940..c635f7b2d 100644 --- a/src/extension/internal/javafx-out.cpp +++ b/src/extension/internal/javafx-out.cpp @@ -386,7 +386,7 @@ bool JavaFXOutput::doStyle(SPStyle *style) /** * Fill */ - SPIPaint fill = style->fill; + SPIPaint const &fill = style->fill; if (fill.isColor()) { // see color.h for how to parse SPColor @@ -423,7 +423,7 @@ bool JavaFXOutput::doStyle(SPStyle *style) */ if (style->stroke_opacity.value > 0) { - SPIPaint stroke = style->stroke; + SPIPaint const &stroke = style->stroke; out(" stroke: %s\n", rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str()); double strokewidth = style->stroke_width.value; diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp index bebc21f2f..00448b89e 100644 --- a/src/extension/internal/latex-text-renderer.cpp +++ b/src/extension/internal/latex-text-renderer.cpp @@ -51,7 +51,7 @@ namespace Internal { /** * This method is called by the PDF, EPS and PS output extensions. - * @param filename This should be the filename without extension to which the tex code should be written. Output goes to <filename>.tex. + * @param filename This should be the filename without '_tex' extension to which the tex code should be written. Output goes to <filename>_tex, note the underscore instead of period. */ bool latex_render_document_text_to_file( SPDocument *doc, gchar const *filename, @@ -132,7 +132,7 @@ LaTeXTextRenderer::setTargetFile(gchar const *filename) { _filename = g_path_get_basename(filename); - gchar *filename_ext = g_strdup_printf("%s.tex", filename); + gchar *filename_ext = g_strdup_printf("%s_tex", filename); Inkscape::IO::dump_fopen_call(filename_ext, "K"); FILE *osf = Inkscape::IO::fopen_utf8name(filename_ext, "w+"); if (!osf) { @@ -176,12 +176,12 @@ LaTeXTextRenderer::setTargetFile(gchar const *filename) { static char const preamble[] = "%% To include the image in your LaTeX document, write\n" -"%% \\input{<filename>.tex}\n" +"%% \\input{<filename>.pdf_tex}\n" "%% instead of\n" "%% \\includegraphics{<filename>.pdf}\n" "%% To scale the image, write\n" -"%% \\def{\\svgwidth}{<desired width>}\n" -"%% \\input{<filename>.tex}\n" +"%% \\def\\svgwidth{<desired width>}\n" +"%% \\input{<filename>.pdf_tex}\n" "%% instead of\n" "%% \\includegraphics[width=<desired width>]{<filename>.pdf}\n" "%%\n" @@ -190,7 +190,7 @@ static char const preamble[] = "%% installed) using\n" "%% \\usepackage{import}\n" "%% in the preamble, and then including the image with\n" -"%% \\import{<path to file>}{<filename>.tex}\n" +"%% \\import{<path to file>}{<filename>.pdf_tex}\n" "%% Alternatively, one can specify\n" "%% \\graphicspath{{<path to file>/}}\n" "%% \n" diff --git a/src/extension/system.cpp b/src/extension/system.cpp index 6ffa7f57f..5412a5cc0 100644 --- a/src/extension/system.cpp +++ b/src/extension/system.cpp @@ -589,7 +589,7 @@ get_file_save_path (SPDocument *doc, FileSaveMethod method) { switch (method) { case FILE_SAVE_METHOD_SAVE_AS: { - bool use_current_dir = prefs->getBool("/dialogs/save_as/use_current_dir"); + bool use_current_dir = prefs->getBool("/dialogs/save_as/use_current_dir", true); if (doc->uri && use_current_dir) { path = Glib::path_get_dirname(doc->uri); } else { diff --git a/src/file.cpp b/src/file.cpp index e0ecd5084..50fcd3642 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -68,6 +68,10 @@ # include <libgnomevfs/gnome-vfs.h> #endif +#ifdef WITH_DBUS +#include "extension/dbus/dbus-init.h" +#endif + //#ifdef WITH_INKBOARD //#include "jabber_whiteboard/session-manager.h" //#endif @@ -135,6 +139,11 @@ sp_file_new(const Glib::ustring &templ) sp_namedview_window_from_document(dt); sp_namedview_update_layers_from_document(dt); } + +#ifdef WITH_DBUS + Inkscape::Extension::Dbus::dbus_init_desktop_interface(dt); +#endif + return dt; } diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index bf71253ff..24e4b534c 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -37,6 +37,7 @@ #include "preferences.h" #include "libnr/nr-point-fns.h" +#define noSP_GR_VERBOSE // Terminology: // @@ -49,9 +50,11 @@ static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *gr); -SPGradient * -sp_gradient_ensure_vector_normalized(SPGradient *gr) +SPGradient *sp_gradient_ensure_vector_normalized(SPGradient *gr) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_ensure_vector_normalized(%p)", gr); +#endif g_return_val_if_fail(gr != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); @@ -64,9 +67,9 @@ sp_gradient_ensure_vector_normalized(SPGradient *gr) } /* First make sure we have vector directly defined (i.e. gr has its own stops) */ - if (!gr->has_stops) { + if ( !gr->hasStops() ) { /* We do not have stops ourselves, so flatten stops as well */ - sp_gradient_ensure_vector(gr); + gr->ensureVector(); g_assert(gr->vector.built); // this adds stops from gr->vector as children to gr sp_gradient_repr_write_vector (gr); @@ -88,13 +91,16 @@ sp_gradient_ensure_vector_normalized(SPGradient *gr) * Creates new private gradient for the given vector */ -static SPGradient * -sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type) +static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPGradientType type) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_get_private_normalized(%p, %p, %d)", document, vector, type); +#endif + g_return_val_if_fail(document != NULL, NULL); g_return_val_if_fail(vector != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(vector), NULL); - g_return_val_if_fail(SP_GRADIENT_HAS_STOPS(vector), NULL); + g_return_val_if_fail(vector->hasStops(), NULL); SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document); @@ -128,8 +134,7 @@ sp_gradient_get_private_normalized(SPDocument *document, SPGradient *vector, SPG /** Count how many times gr is used by the styles of o and its descendants */ -guint -count_gradient_hrefs(SPObject *o, SPGradient *gr) +guint count_gradient_hrefs(SPObject *o, SPGradient *gr) { if (!o) return 1; @@ -164,18 +169,21 @@ count_gradient_hrefs(SPObject *o, SPGradient *gr) /** * If gr has other users, create a new private; also check if gr links to vector, relink if not */ -SPGradient * -sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, - SPGradientType type, SPObject *o) +SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, + SPGradientType type, SPObject *o) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_fork_private_if_necessary(%p, %p, %d, %p)", gr, vector, type, o); +#endif g_return_val_if_fail(gr != NULL, NULL); g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); // Orphaned gradient, no vector with stops at the end of the line; this used to be an assert // but i think we should not abort on this - maybe just write a validity warning into some sort // of log - if (!vector || !SP_GRADIENT_HAS_STOPS(vector)) + if ( !vector || !vector->hasStops() ) { return (gr); + } // user is the object that uses this gradient; normally it's item but for tspans, we // check its ancestor text so that tspans don't get different gradients from their @@ -199,7 +207,7 @@ sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, SPDocument *doc = SP_OBJECT_DOCUMENT(gr); SPObject *defs = SP_DOCUMENT_DEFS(doc); - if ((gr->has_stops) || + if ((gr->hasStops()) || (gr->state != SP_GRADIENT_STATE_UNKNOWN) || (SP_OBJECT_PARENT(gr) != SP_OBJECT(defs)) || (SP_OBJECT_HREFCOUNT(gr) > 1)) { @@ -233,9 +241,11 @@ sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, } } -SPGradient * -sp_gradient_fork_vector_if_necessary (SPGradient *gr) +SPGradient *sp_gradient_fork_vector_if_necessary(SPGradient *gr) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_fork_vector_if_necessary(%p)", gr); +#endif // Some people actually prefer their gradient vectors to be shared... Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (!prefs->getBool("/options/forkgradientvectors/value", true)) @@ -258,9 +268,11 @@ sp_gradient_fork_vector_if_necessary (SPGradient *gr) /** * Obtain the vector from the gradient. A forked vector will be created and linked to this gradient if another gradient uses it. */ -SPGradient * -sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector) +SPGradient *sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_get_forked_vector_if_necessary(%p, %d)", gradient, force_vector); +#endif SPGradient *vector = gradient->getVector(force_vector); vector = sp_gradient_fork_vector_if_necessary (vector); if ( gradient != vector && gradient->ref->getObject() != vector ) { @@ -275,9 +287,11 @@ sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vect * instead. No forking or reapplying is done because this is only called for newly created privates. * @return The new gradient. */ -SPGradient * -sp_gradient_reset_to_userspace (SPGradient *gr, SPItem *item) +SPGradient *sp_gradient_reset_to_userspace(SPGradient *gr, SPItem *item) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_reset_to_userspace(%p, %p)", gr, item); +#endif Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr); // calculate the bbox of the item @@ -327,16 +341,22 @@ sp_gradient_reset_to_userspace (SPGradient *gr, SPItem *item) * Convert an item's gradient to userspace if necessary, also fork it if necessary. * @return The new gradient. */ -SPGradient * -sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property) +SPGradient *sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *property) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_convert_to_userspace(%p, %p, \"%s\")", gr, item, property); +#endif g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); + if ( gr && gr->isSolid() ) { + return gr; + } + // First, fork it if it is shared gr = sp_gradient_fork_private_if_necessary(gr, gr->getVector(), SP_IS_RADIALGRADIENT(gr) ? SP_GRADIENT_TYPE_RADIAL : SP_GRADIENT_TYPE_LINEAR, SP_OBJECT(item)); - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { Inkscape::XML::Node *repr = SP_OBJECT_REPR(gr); @@ -439,9 +459,11 @@ sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *prop return gr; } -void -sp_gradient_transform_multiply(SPGradient *gradient, Geom::Matrix postmul, bool set) +void sp_gradient_transform_multiply(SPGradient *gradient, Geom::Matrix postmul, bool set) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_transform_multiply(%p, , %d)", gradient, set); +#endif if (set) { gradient->gradientTransform = postmul; } else { @@ -454,24 +476,23 @@ sp_gradient_transform_multiply(SPGradient *gradient, Geom::Matrix postmul, bool g_free(c); } -SPGradient * -sp_item_gradient (SPItem *item, bool fill_or_stroke) +SPGradient *sp_item_gradient(SPItem *item, bool fill_or_stroke) { - SPStyle *style = SP_OBJECT_STYLE (item); - SPGradient *gradient = NULL; + SPStyle *style = item->style; + SPGradient *gradient = 0; if (fill_or_stroke) { if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_GRADIENT (server)) { - gradient = SP_GRADIENT (server); + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { + gradient = SP_GRADIENT(server); } } } else { if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_GRADIENT (server)) { - gradient = SP_GRADIENT (server); + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { + gradient = SP_GRADIENT(server); } } } @@ -479,7 +500,7 @@ sp_item_gradient (SPItem *item, bool fill_or_stroke) return gradient; } -SPStop* sp_last_stop(SPGradient *gradient) +SPStop *sp_last_stop(SPGradient *gradient) { for (SPStop *stop = gradient->getFirstStop(); stop != NULL; stop = stop->getNextStop()) { if (stop->getNextStop() == NULL) @@ -488,8 +509,7 @@ SPStop* sp_last_stop(SPGradient *gradient) return NULL; } -SPStop* -sp_get_stop_i(SPGradient *gradient, guint stop_i) +SPStop *sp_get_stop_i(SPGradient *gradient, guint stop_i) { SPStop *stop = gradient->getFirstStop(); @@ -509,20 +529,22 @@ sp_get_stop_i(SPGradient *gradient, guint stop_i) return stop; } -guint32 -average_color (guint32 c1, guint32 c2, gdouble p) +guint32 average_color(guint32 c1, guint32 c2, gdouble p) { guint32 r = (guint32) (SP_RGBA32_R_U (c1) * (1 - p) + SP_RGBA32_R_U (c2) * p); guint32 g = (guint32) (SP_RGBA32_G_U (c1) * (1 - p) + SP_RGBA32_G_U (c2) * p); guint32 b = (guint32) (SP_RGBA32_B_U (c1) * (1 - p) + SP_RGBA32_B_U (c2) * p); guint32 a = (guint32) (SP_RGBA32_A_U (c1) * (1 - p) + SP_RGBA32_A_U (c2) * p); - return SP_RGBA32_U_COMPOSE (r, g, b, a); + return SP_RGBA32_U_COMPOSE(r, g, b, a); } -SPStop * -sp_vector_add_stop (SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gfloat offset) +SPStop *sp_vector_add_stop(SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gfloat offset) { +#ifdef SP_GR_VERBOSE + g_message("sp_vector_add_stop(%p, %p, %p, %f)", vector, prev_stop, next_stop, offset); +#endif + Inkscape::XML::Node *new_stop_repr = NULL; new_stop_repr = SP_OBJECT_REPR(prev_stop)->duplicate(SP_OBJECT_REPR(vector)->document()); SP_OBJECT_REPR(vector)->addChild(new_stop_repr, SP_OBJECT_REPR(prev_stop)); @@ -544,8 +566,7 @@ sp_vector_add_stop (SPGradient *vector, SPStop* prev_stop, SPStop* next_stop, gf return newstop; } -void -sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) +void sp_item_gradient_edit_stop(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); @@ -585,8 +606,7 @@ sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool } } -guint32 -sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) +guint32 sp_item_gradient_stop_query_style(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); @@ -638,9 +658,11 @@ sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i return 0; } -void -sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke, SPCSSAttr *stop) +void sp_item_gradient_stop_set_style(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke, SPCSSAttr *stop) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_stop_set_style(%p, %d, %d, %d, %p)", item, point_type, point_i, fill_or_stroke, stop); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); if (!gradient || !SP_IS_GRADIENT(gradient)) @@ -695,9 +717,11 @@ sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, } } -void -sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke) +void sp_item_gradient_reverse_vector(SPItem *item, bool fill_or_stroke) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_reverse_vector(%p, %d)", item, fill_or_stroke); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); if (!gradient || !SP_IS_GRADIENT(gradient)) return; @@ -753,9 +777,11 @@ sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke) Set the position of point point_type of the gradient applied to item (either fill_or_stroke) to p_w (in desktop coordinates). Write_repr if you want the change to become permanent. */ -void -sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom::Point p_w, bool fill_or_stroke, bool write_repr, bool scale) +void sp_item_gradient_set_coords(SPItem *item, guint point_type, guint point_i, Geom::Point p_w, bool fill_or_stroke, bool write_repr, bool scale) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_set_coords(%p, %d, %d, ...)", item, point_type, point_i ); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); if (!gradient || !SP_IS_GRADIENT(gradient)) @@ -814,7 +840,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom // using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end. double offset = get_offset_between_points (p, Geom::Point(lg->x1.computed, lg->y1.computed), Geom::Point(lg->x2.computed, lg->y2.computed)); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (lg, false); - sp_gradient_ensure_vector(lg); + lg->ensureVector(); lg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; @@ -908,7 +934,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom Geom::Point end = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed); double offset = get_offset_between_points (p, start, end); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (rg, false); - sp_gradient_ensure_vector(rg); + rg->ensureVector(); rg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; @@ -924,7 +950,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom Geom::Point end = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed); double offset = get_offset_between_points (p, start, end); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(rg, false); - sp_gradient_ensure_vector(rg); + rg->ensureVector(); rg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; @@ -950,8 +976,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom } } -SPGradient * -sp_item_gradient_get_vector (SPItem *item, bool fill_or_stroke) +SPGradient *sp_item_gradient_get_vector(SPItem *item, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); @@ -961,14 +986,15 @@ sp_item_gradient_get_vector (SPItem *item, bool fill_or_stroke) return NULL; } -SPGradientSpread -sp_item_gradient_get_spread (SPItem *item, bool fill_or_stroke) +SPGradientSpread sp_item_gradient_get_spread(SPItem *item, bool fill_or_stroke) { + SPGradientSpread spread = SP_GRADIENT_SPREAD_PAD; SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); - if (gradient) - return sp_gradient_get_spread (gradient); - return SP_GRADIENT_SPREAD_PAD; + if (gradient) { + spread = gradient->fetchSpread(); + } + return spread; } @@ -977,9 +1003,11 @@ Returns the position of point point_type of the gradient applied to item (either in desktop coordinates. */ -Geom::Point -sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) +Geom::Point sp_item_gradient_get_coords(SPItem *item, guint point_type, guint point_i, bool fill_or_stroke) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_gradient_get_coords(%p, %d, %d, %d)", item, point_type, point_i, fill_or_stroke); +#endif SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); Geom::Point p (0, 0); @@ -1033,7 +1061,7 @@ sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool } } - if (SP_GRADIENT(gradient)->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (SP_GRADIENT(gradient)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item)); Geom::OptRect bbox = item->getBounds(Geom::identity()); // we need "true" bbox without item_i2d_affine if (bbox) { @@ -1053,9 +1081,11 @@ sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool * gr has to be a normalized vector. */ -SPGradient * -sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_fill) +SPGradient *sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_fill) { +#ifdef SP_GR_VERBOSE + g_message("sp_item_set_gradient(%p, %p, %d, %d)", item, gr, type, is_fill); +#endif g_return_val_if_fail(item != NULL, NULL); g_return_val_if_fail(SP_IS_ITEM(item), NULL); g_return_val_if_fail(gr != NULL, NULL); @@ -1124,6 +1154,9 @@ sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_ static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *link) { +#ifdef SP_GR_VERBOSE + g_message("sp_gradient_repr_set_link(%p, %p)", repr, link); +#endif g_return_if_fail(repr != NULL); if (link) { g_return_if_fail(SP_IS_GRADIENT(link)); @@ -1138,59 +1171,54 @@ static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *lin } } -/* - * Get default normalized gradient vector of document, create if there is none - */ -SPGradient * -sp_document_default_gradient_vector(SPDocument *document, guint32 color) +static void addStop( Inkscape::XML::Node *parent, Glib::ustring const &color, gint opacity, gchar const *offset ) { - SPDefs *defs = (SPDefs *) SP_DOCUMENT_DEFS(document); - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document); - - Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient"); - - repr->setAttribute("inkscape:collect", "always"); - // set here, but removed when it's edited in the gradient editor - // to further reduce clutter, we could - // (1) here, search gradients by color and return what is found without duplication - // (2) in fill & stroke, show only one copy of each gradient in list - - Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop"); - - gchar b[64]; - sp_svg_write_color(b, sizeof(b), color); - +#ifdef SP_GR_VERBOSE + g_message("addStop(%p, %s, %d, %s)", parent, color.c_str(), opacity, offset); +#endif + Inkscape::XML::Node *stop = parent->document()->createElement("svg:stop"); { - gchar *t = g_strdup_printf("stop-color:%s;stop-opacity:1;", b); - stop->setAttribute("style", t); - g_free(t); + gchar *tmp = g_strdup_printf( "stop-color:%s;stop-opacity:%d;", color.c_str(), opacity ); + stop->setAttribute( "style", tmp ); + g_free(tmp); } - stop->setAttribute("offset", "0"); + stop->setAttribute( "offset", offset ); - repr->appendChild(stop); + parent->appendChild(stop); Inkscape::GC::release(stop); +} - stop = xml_doc->createElement("svg:stop"); +/* + * Get default normalized gradient vector of document, create if there is none + */ +SPGradient *sp_document_default_gradient_vector( SPDocument *document, SPColor const &color, bool singleStop ) +{ + SPDefs *defs = static_cast<SPDefs *>(SP_DOCUMENT_DEFS(document)); + Inkscape::XML::Document *xml_doc = document->rdoc; - { - gchar *t = g_strdup_printf("stop-color:%s;stop-opacity:0;", b); - stop->setAttribute("style", t); - g_free(t); - } + Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient"); - stop->setAttribute("offset", "1"); + if ( !singleStop ) { + repr->setAttribute("inkscape:collect", "always"); + // set here, but removed when it's edited in the gradient editor + // to further reduce clutter, we could + // (1) here, search gradients by color and return what is found without duplication + // (2) in fill & stroke, show only one copy of each gradient in list + } - repr->appendChild(stop); - Inkscape::GC::release(stop); + Glib::ustring colorStr = color.toString(); + addStop( repr, colorStr, 1, "0" ); + if ( !singleStop ) { + addStop( repr, colorStr, 0, "1" ); + } SP_OBJECT_REPR(defs)->addChild(repr, NULL); Inkscape::GC::release(repr); /* fixme: This does not look like nice */ - SPGradient *gr; - gr = (SPGradient *) document->getObjectByRepr(repr); + SPGradient *gr = static_cast<SPGradient *>(document->getObjectByRepr(repr)); g_assert(gr != NULL); g_assert(SP_IS_GRADIENT(gr)); /* fixme: Maybe add extra sanity check here */ @@ -1203,13 +1231,12 @@ sp_document_default_gradient_vector(SPDocument *document, guint32 color) Return the preferred vector for \a o, made from (in order of preference) its current vector, current fill or stroke color, or from desktop style if \a o is NULL or doesn't have style. */ -SPGradient * -sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop, - SPObject *const o, bool const is_fill) +SPGradient *sp_gradient_vector_for_object( SPDocument *const doc, SPDesktop *const desktop, + SPObject *const o, bool const is_fill, bool singleStop ) { - guint32 rgba = 0; + SPColor color; if (o == NULL || SP_OBJECT_STYLE(o) == NULL) { - rgba = sp_desktop_get_color(desktop, is_fill); + color = sp_desktop_get_color(desktop, is_fill); } else { // take the color of the object SPStyle const &style = *SP_OBJECT_STYLE(o); @@ -1217,21 +1244,21 @@ sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop, ? style.fill : style.stroke ); if (paint.isPaintserver()) { - SPObject *server = is_fill? SP_OBJECT_STYLE_FILL_SERVER(o) : SP_OBJECT_STYLE_STROKE_SERVER(o); - if (SP_IS_GRADIENT (server)) { + SPObject *server = is_fill? o->style->getFillPaintServer() : o->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { return SP_GRADIENT(server)->getVector(true); } else { - rgba = sp_desktop_get_color(desktop, is_fill); + color = sp_desktop_get_color(desktop, is_fill); } } else if (paint.isColor()) { - rgba = paint.value.color.toRGBA32( 0xff ); + color = paint.value.color; } else { // if o doesn't use flat color, then take current color of the desktop. - rgba = sp_desktop_get_color(desktop, is_fill); + color = sp_desktop_get_color(desktop, is_fill); } } - return sp_document_default_gradient_vector(doc, rgba); + return sp_document_default_gradient_vector( doc, color, singleStop ); } diff --git a/src/gradient-chemistry.h b/src/gradient-chemistry.h index 3f72fa394..0c8d0afe7 100644 --- a/src/gradient-chemistry.h +++ b/src/gradient-chemistry.h @@ -42,9 +42,8 @@ SPGradient *sp_item_set_gradient (SPItem *item, SPGradient *gr, SPGradientType t /* * Get default normalized gradient vector of document, create if there is none */ - -SPGradient *sp_document_default_gradient_vector (SPDocument *document, guint32 color = 0); -SPGradient *sp_gradient_vector_for_object (SPDocument *doc, SPDesktop *desktop, SPObject *o, bool is_fill); +SPGradient *sp_document_default_gradient_vector( SPDocument *document, SPColor const &color, bool singleStop ); +SPGradient *sp_gradient_vector_for_object( SPDocument *doc, SPDesktop *desktop, SPObject *o, bool is_fill, bool singleStop = false ); void sp_object_ensure_fill_gradient_normalized (SPObject *object); void sp_object_ensure_stroke_gradient_normalized (SPObject *object); diff --git a/src/gradient-context.cpp b/src/gradient-context.cpp index f8117ed5b..de4de7d5d 100644 --- a/src/gradient-context.cpp +++ b/src/gradient-context.cpp @@ -1,5 +1,3 @@ -#define __SP_GRADIENT_CONTEXT_C__ - /* * Gradient drawing and editing tool * @@ -381,7 +379,7 @@ sp_gradient_context_add_stops_between_selected_stops (SPGradientContext *rc) if (SP_IS_GRADIENT (parent)) { doc = SP_OBJECT_DOCUMENT (parent); sp_vector_add_stop (SP_GRADIENT (parent), this_stop, next_stop, offset); - sp_gradient_ensure_vector (SP_GRADIENT (parent)); + SP_GRADIENT(parent)->ensureVector(); } } diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index 11de93d68..e7536a86a 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -1,5 +1,3 @@ -#define __GRADIENT_DRAG_C__ - /* * On-canvas gradient dragging * @@ -178,41 +176,91 @@ gr_drag_style_query (SPStyle *style, int property, gpointer data) } } -bool -gr_drag_style_set (const SPCSSAttr *css, gpointer data) +Glib::ustring GrDrag::makeStopSafeColor( gchar const *str, bool &isNull ) { - GrDrag *drag = (GrDrag *) data; + Glib::ustring colorStr; + if ( str ) { + isNull = false; + colorStr = str; + Glib::ustring::size_type pos = colorStr.find("url(#"); + if ( pos != Glib::ustring::npos ) { + Glib::ustring targetName = colorStr.substr(pos + 5, colorStr.length() - 6); + const GSList *gradients = sp_document_get_resource_list(desktop->doc(), "gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( targetName == grad->getId() ) { + SPGradient *vect = grad->getVector(); + SPStop *firstStop = (vect) ? vect->getFirstStop() : grad->getFirstStop(); + if (firstStop) { + Glib::ustring stopColorStr; + if (firstStop->currentColor) { + stopColorStr = sp_object_get_style_property(firstStop, "color", NULL); + } else { + stopColorStr = firstStop->specified_color.toString(); + } + if ( !stopColorStr.empty() ) { + colorStr = stopColorStr; + } + } + break; + } + } + } + } else { + isNull = true; + } - if (!drag->selected) + return colorStr; +} + +bool GrDrag::styleSet( const SPCSSAttr *css ) +{ + if (!selected) { return false; + } - SPCSSAttr *stop = sp_repr_css_attr_new (); + SPCSSAttr *stop = sp_repr_css_attr_new(); // See if the css contains interesting properties, and if so, translate them into the format // acceptable for gradient stops // any of color properties, in order of increasing priority: - if (css->attribute("flood-color")) + if (css->attribute("flood-color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("flood-color")); + } - if (css->attribute("lighting-color")) + if (css->attribute("lighting-color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("lighting-color")); + } - if (css->attribute("color")) + if (css->attribute("color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("color")); + } - if (css->attribute("stroke") && strcmp(css->attribute("stroke"), "none")) + if (css->attribute("stroke") && strcmp(css->attribute("stroke"), "none")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("stroke")); + } - if (css->attribute("fill") && strcmp(css->attribute("fill"), "none")) + if (css->attribute("fill") && strcmp(css->attribute("fill"), "none")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("fill")); + } - if (css->attribute("stop-color")) + if (css->attribute("stop-color")) { sp_repr_css_set_property (stop, "stop-color", css->attribute("stop-color")); + } + + // Make sure the style is allowed for gradient stops. + if ( !sp_repr_css_property_is_unset( stop, "stop-color") ) { + bool stopIsNull = false; + Glib::ustring tmp = makeStopSafeColor( sp_repr_css_property( stop, "stop-color", "" ), stopIsNull ); + if ( !stopIsNull && !tmp.empty() ) { + sp_repr_css_set_property( stop, "stop-color", tmp.c_str() ); + } + } if (css->attribute("stop-opacity")) { // direct setting of stop-opacity has priority - sp_repr_css_set_property (stop, "stop-opacity", css->attribute("stop-opacity")); + sp_repr_css_set_property(stop, "stop-opacity", css->attribute("stop-opacity")); } else { // multiply all opacity properties: gdouble accumulated = 1.0; accumulated *= sp_svg_read_percentage(css->attribute("flood-opacity"), 1.0); @@ -222,11 +270,12 @@ gr_drag_style_set (const SPCSSAttr *css, gpointer data) Inkscape::CSSOStringStream os; os << accumulated; - sp_repr_css_set_property (stop, "stop-opacity", os.str().c_str()); + sp_repr_css_set_property(stop, "stop-opacity", os.str().c_str()); if ((css->attribute("fill") && !css->attribute("stroke") && !strcmp(css->attribute("fill"), "none")) || - (css->attribute("stroke") && !css->attribute("fill") && !strcmp(css->attribute("stroke"), "none"))) - sp_repr_css_set_property (stop, "stop-opacity", "0"); // if a single fill/stroke property is set to none, don't change color, set opacity to 0 + (css->attribute("stroke") && !css->attribute("fill") && !strcmp(css->attribute("stroke"), "none"))) { + sp_repr_css_set_property(stop, "stop-opacity", "0"); // if a single fill/stroke property is set to none, don't change color, set opacity to 0 + } } if (!stop->attributeList()) { // nothing for us here, pass it on @@ -234,13 +283,13 @@ gr_drag_style_set (const SPCSSAttr *css, gpointer data) return false; } - for (GList const* sel = drag->selected; sel != NULL; sel = sel->next) { // for all selected draggers - GrDragger* dragger = (GrDragger*) sel->data; + for (GList const* sel = selected; sel != NULL; sel = sel->next) { // for all selected draggers + GrDragger* dragger = reinterpret_cast<GrDragger*>(sel->data); for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger - GrDraggable *draggable = (GrDraggable *) i->data; + GrDraggable *draggable = reinterpret_cast<GrDraggable *>(i->data); - drag->local_change = true; - sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop); + local_change = true; + sp_item_gradient_stop_set_style(draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop); } } @@ -348,7 +397,7 @@ GrDrag::addStopNearPoint (SPItem *item, Geom::Point mouse_p, double tolerance) SPStop *newstop = sp_vector_add_stop (vector, prev_stop, next_stop, offset); - sp_gradient_ensure_vector (gradient); + gradient->ensureVector(); updateDraggers(); return newstop; @@ -361,14 +410,18 @@ GrDrag::addStopNearPoint (SPItem *item, Geom::Point mouse_p, double tolerance) bool GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p) { + // Note: not sure if a null pointer can come in for the style, but handle that just in case + bool stopIsNull = false; + Glib::ustring toUse = makeStopSafeColor( c, stopIsNull ); + // first, see if we can drop onto one of the existing draggers for (GList *i = draggers; i != NULL; i = i->next) { // for all draggables of dragger GrDragger *d = (GrDragger *) i->data; if (Geom::L2(p - d->point)*desktop->current_zoom() < 5) { SPCSSAttr *stop = sp_repr_css_attr_new (); - sp_repr_css_set_property (stop, "stop-color", c); - sp_repr_css_set_property (stop, "stop-opacity", "1"); + sp_repr_css_set_property( stop, "stop-color", stopIsNull ? 0 : toUse.c_str() ); + sp_repr_css_set_property( stop, "stop-opacity", "1" ); for (GSList *j = d->draggables; j != NULL; j = j->next) { // for all draggables of dragger GrDraggable *draggable = (GrDraggable *) j->data; local_change = true; @@ -391,8 +444,8 @@ GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p) SPStop *stop = addStopNearPoint (line->item, p, 5/desktop->current_zoom()); if (stop) { SPCSSAttr *css = sp_repr_css_attr_new (); - sp_repr_css_set_property (css, "stop-color", c); - sp_repr_css_set_property (css, "stop-opacity", "1"); + sp_repr_css_set_property( css, "stop-color", stopIsNull ? 0 : toUse.c_str() ); + sp_repr_css_set_property( css, "stop-opacity", "1" ); sp_repr_css_change (SP_OBJECT_REPR (stop), css, "style"); return true; } @@ -404,51 +457,47 @@ GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p) } -GrDrag::GrDrag(SPDesktop *desktop) { - - this->desktop = desktop; - - this->selection = sp_desktop_selection(desktop); - - this->draggers = NULL; - this->lines = NULL; - this->selected = NULL; - - this->hor_levels.clear(); - this->vert_levels.clear(); - - this->local_change = false; - - this->sel_changed_connection = this->selection->connectChanged( - sigc::bind ( +GrDrag::GrDrag(SPDesktop *desktop) : + selected(0), + keep_selection(false), + local_change(false), + desktop(desktop), + hor_levels(), + vert_levels(), + draggers(0), + lines(0), + selection(sp_desktop_selection(desktop)), + sel_changed_connection(), + sel_modified_connection(), + style_set_connection(), + style_query_connection() +{ + sel_changed_connection = selection->connectChanged( + sigc::bind( sigc::ptr_fun(&gr_drag_sel_changed), (gpointer)this ) ); - this->sel_modified_connection = this->selection->connectModified( + sel_modified_connection = selection->connectModified( sigc::bind( sigc::ptr_fun(&gr_drag_sel_modified), (gpointer)this ) ); - this->style_set_connection = this->desktop->connectSetStyle( - sigc::bind( - sigc::ptr_fun(&gr_drag_style_set), - (gpointer)this ) - ); + style_set_connection = desktop->connectSetStyle( sigc::mem_fun(*this, &GrDrag::styleSet) ); - this->style_query_connection = this->desktop->connectQueryStyle( + style_query_connection = desktop->connectQueryStyle( sigc::bind( sigc::ptr_fun(&gr_drag_style_query), (gpointer)this ) ); - this->updateDraggers (); - this->updateLines (); - this->updateLevels (); + updateDraggers(); + updateLines(); + updateLevels(); if (desktop->gr_item) { - this->setSelected (getDraggerFor (desktop->gr_item, desktop->gr_point_type, desktop->gr_point_i, desktop->gr_fill_or_stroke)); + setSelected(getDraggerFor(desktop->gr_item, desktop->gr_point_type, desktop->gr_point_i, desktop->gr_fill_or_stroke)); } } @@ -503,17 +552,18 @@ GrDraggable::~GrDraggable () } -SPObject * -GrDraggable::getServer () +SPObject *GrDraggable::getServer() { - if (!item) + if (!item) { return NULL; + } SPObject *server = NULL; - if (fill_or_stroke) - server = SP_OBJECT_STYLE_FILL_SERVER (item); - else - server = SP_OBJECT_STYLE_STROKE_SERVER (item); + if (fill_or_stroke) { + server = item->style->getFillPaintServer(); + }else { + server = item->style->getStrokePaintServer(); + } return server; } @@ -659,7 +709,7 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0); } if (snap_vector) { - Inkscape::Snapper::ConstraintLine cl(dr_snap, p + *snap_vector - dr_snap); + Inkscape::Snapper::SnapConstraint cl(dr_snap, p + *snap_vector - dr_snap); Inkscape::SnappedPoint s = m.constrainedSnap(Inkscape::SnapCandidatePoint(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE), cl); if (s.getSnapped()) { s.setTransformation(s.getPoint() - p); @@ -809,9 +859,10 @@ gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, gu } else { p = snap_vector_midpoint (p, low_lim, high_lim, 0); if (!(state & GDK_SHIFT_MASK)) { + Inkscape::Snapper::SnapConstraint cl(low_lim, high_lim - low_lim); SPDesktop *desktop = dragger->parent->desktop; SnapManager &m = desktop->namedview->snap_manager; - Inkscape::Snapper::ConstraintLine cl(low_lim, high_lim - low_lim); + m.setup(desktop); m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE, cl); } } @@ -1623,41 +1674,43 @@ GrDrag::grabKnot (SPItem *item, gint point_type, gint point_i, bool fill_or_stro Regenerates the draggers list from the current selection; is called when selection is changed or modified, also when a radial dragger needs to update positions of other draggers in the gradient */ -void -GrDrag::updateDraggers () +void GrDrag::updateDraggers () { while (selected) { selected = g_list_remove(selected, selected->data); } // delete old draggers for (GList const* i = this->draggers; i != NULL; i = i->next) { - delete ((GrDragger *) i->data); + delete static_cast<GrDragger *>(i->data); } - g_list_free (this->draggers); + g_list_free(this->draggers); this->draggers = NULL; - g_return_if_fail (this->selection != NULL); + g_return_if_fail(this->selection != NULL); for (GSList const* i = this->selection->itemList(); i != NULL; i = i->next) { - SPItem *item = SP_ITEM(i->data); - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { - addDraggersLinear (SP_LINEARGRADIENT (server), item, true); - } else if (SP_IS_RADIALGRADIENT (server)) { - addDraggersRadial (SP_RADIALGRADIENT (server), item, true); + SPPaintServer *server = style->getFillPaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { + addDraggersLinear( SP_LINEARGRADIENT(server), item, true ); + } else if ( SP_IS_RADIALGRADIENT(server) ) { + addDraggersRadial( SP_RADIALGRADIENT(server), item, true ); } } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { - addDraggersLinear (SP_LINEARGRADIENT (server), item, false); - } else if (SP_IS_RADIALGRADIENT (server)) { - addDraggersRadial (SP_RADIALGRADIENT (server), item, false); + SPPaintServer *server = style->getStrokePaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { + addDraggersLinear( SP_LINEARGRADIENT(server), item, false ); + } else if ( SP_IS_RADIALGRADIENT(server) ) { + addDraggersRadial( SP_RADIALGRADIENT(server), item, false ); } } } @@ -1702,10 +1755,12 @@ GrDrag::updateLines () SPStyle *style = SP_OBJECT_STYLE (item); if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, true), sp_item_gradient_get_coords (item, POINT_LG_END, 0, true), GR_LINE_COLOR_FILL); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { Geom::Point center = sp_item_gradient_get_coords (item, POINT_RG_CENTER, 0, true); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, true), GR_LINE_COLOR_FILL); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, true), GR_LINE_COLOR_FILL); @@ -1713,10 +1768,12 @@ GrDrag::updateLines () } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( server && server->isSolid() ) { + // Suppress "gradientness" of solid paint + } else if ( SP_IS_LINEARGRADIENT(server) ) { this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, false), sp_item_gradient_get_coords (item, POINT_LG_END, 0, false), GR_LINE_COLOR_STROKE); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { Geom::Point center = sp_item_gradient_get_coords (item, POINT_RG_CENTER, 0, false); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, false), GR_LINE_COLOR_STROKE); this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, false), GR_LINE_COLOR_STROKE); diff --git a/src/gradient-drag.h b/src/gradient-drag.h index 460d1396e..8cbe9f305 100644 --- a/src/gradient-drag.h +++ b/src/gradient-drag.h @@ -2,7 +2,7 @@ #define __GRADIENT_DRAG_H__ /* - * On-canvas gradient dragging + * On-canvas gradient dragging * * Authors: * bulia byak <bulia@users.sf.net> @@ -78,7 +78,7 @@ struct GrDragger { void updateKnotShape(); void updateTip(); - + void select(); void deselect(); bool isSelected(); @@ -131,9 +131,9 @@ public: // FIXME: make more of this private! void deleteSelected (bool just_one = false); guint32 getColor(); - - bool keep_selection; - + + bool keep_selection; + GrDragger *getDraggerFor (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke); void grabKnot (GrDragger *dragger, gint x, gint y, guint32 etime); @@ -165,7 +165,7 @@ public: // FIXME: make more of this private! void selected_reverse_vector (); -private: +private: void deselect_all(); void addLine (SPItem *item, Geom::Point p1, Geom::Point p2, guint32 rgba); @@ -175,6 +175,10 @@ private: void addDraggersRadial (SPRadialGradient *rg, SPItem *item, bool fill_or_stroke); void addDraggersLinear (SPLinearGradient *lg, SPItem *item, bool fill_or_stroke); + bool styleSet( const SPCSSAttr *css ); + + Glib::ustring makeStopSafeColor( gchar const *str, bool &isNull ); + Inkscape::Selection *selection; sigc::connection sel_changed_connection; sigc::connection sel_modified_connection; diff --git a/src/inkscape.h b/src/inkscape.h index ca2894227..d82092754 100644 --- a/src/inkscape.h +++ b/src/inkscape.h @@ -42,6 +42,8 @@ Inkscape::XML::Node *inkscape_get_menus (Inkscape::Application * inkscape); Inkscape::Application *inkscape_get_instance(); +SPDesktop * inkscape_find_desktop_by_dkey (unsigned int dkey); + #define SP_ACTIVE_EVENTCONTEXT inkscape_active_event_context () SPEventContext * inkscape_active_event_context (void); @@ -57,6 +59,7 @@ gchar *homedir_path(const char *filename); gchar *profile_path(const char *filename); /* Inkscape desktop stuff */ +void inkscape_activate_desktop (SPDesktop * desktop); void inkscape_switch_desktops_next (); void inkscape_switch_desktops_prev (); void inkscape_get_all_desktops (std::list< SPDesktop* >& listbuf); diff --git a/src/interface.cpp b/src/interface.cpp index 085eca6bd..230fd8a60 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -904,7 +904,7 @@ void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view) gchar const* data[] = { _("Default"), _("Default interface setup"), _("Custom"), _("Set the custom task"), - _("Wide"), _("Setup for widescreen work."), + _("Wide"), _("Setup for widescreen work"), 0, 0 }; @@ -1367,7 +1367,7 @@ sp_ui_drag_data_received(GtkWidget *widget, for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); if ( color.descr == grad->getId() ) { - if ( grad->has_stops ) { + if ( grad->hasStops() ) { matches = grad; break; } diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index 71d2c8235..0ae7b8dc6 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -100,7 +100,7 @@ KnotHolderEntity::snap_knot_position(Geom::Point const &p) } Geom::Point -KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint) +KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint) { Geom::Matrix const i2d (sp_item_i2d_affine(item)); Geom::Point s = p * i2d; @@ -122,7 +122,7 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape: } else { // constrainedSnap() will first project the point p onto the constraint line and then try to snap along that line. // This way the constraint is already enforced, no need to worry about that later on - Inkscape::Snapper::ConstraintLine transformed_constraint = Inkscape::Snapper::ConstraintLine(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d); + Inkscape::Snapper::SnapConstraint transformed_constraint = Inkscape::Snapper::SnapConstraint(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d); m.constrainedSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE, transformed_constraint); } diff --git a/src/knot-holder-entity.h b/src/knot-holder-entity.h index c8fd29ddf..aba93798a 100644 --- a/src/knot-holder-entity.h +++ b/src/knot-holder-entity.h @@ -58,7 +58,7 @@ public: //private: Geom::Point snap_knot_position(Geom::Point const &p); - Geom::Point snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint); + Geom::Point snap_knot_position_constrained(Geom::Point const &p, Inkscape::Snapper::SnapConstraint const &constraint); SPKnot *knot; SPItem *item; diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 192596ecf..77e21ef56 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -445,22 +445,32 @@ class Layout::Calculator // we may also have y move orders to deal with here (dx, dy and rotate are done per span) - // Comment added: 1 June 2010: - // The first line in a normal <text> object is placed by the read-in "y" value. The rest are - // determined by Inkscape. This is to allow insertation of new lines in the middle - // of a <text> object. New "y" values are then stored in each <tspan> that represents - // a new line. The line spacing should depend only on font-size and line-height (and not - // on any y-kerning). Line spacing is already handled by the calling routine. Note that - // this may render improperly any SVG with <tspan>s created/edited by other programs. + // Comment updated: 23 July 2010: + // We must handle two cases: + // + // 1. Inkscape SVG where the first line is placed by the read-in "y" value and the + // rest are determined by 'font-size' and 'line-height' (and not by any + // y-kerning). <tspan>s in this case are marked by sodipodi:role="line". This + // allows new lines to be inserted in the middle of a <text> object. On output, + // new "y" values are calculated for each <tspan> that represents a new line. Line + // spacing is already handled by the calling routine. + // + // 2. Plain SVG where each <text> or <tspan> is placed by its own "x" and "y" values. + // Note that in this case Inkscape treats each <text> object with any included + // <tspan>s as a single line of text. This can be confusing in the code below. + if (!it_chunk->broken_spans.empty() // Not empty paragraph && it_chunk->broken_spans.front().start.char_byte == 0 ) { // Beginning of unbroken span - // If empty or new line + // If empty or new line (sodipode:role="line") if( _flow._characters.empty() || _flow._characters.back().chunk(&_flow).in_line != _flow._lines.size() - 1) { + // This is the Inkscape SVG case. + // // If <tspan> "y" attribute is set, use it (initial "y" attributes in - // <tspans> other than the first have already been stripped). + // <tspans> other than the first have already been stripped for <tspans> + // marked with role="line", see sp-text.cpp: SPText::_buildLayoutInput). if( it_chunk->broken_spans.front().start.iter_span->y._set ) { // Use set "y" attribute @@ -475,9 +485,17 @@ class Layout::Calculator } // Reset relative y_offset ("dy" attribute is relative but should be reset at - // the beginning of each <tspan>.) + // the beginning of each line since each line will have a new "y" written out.) _y_offset = 0.0; + } else { + + // This is the plain SVG case + // + // "x" and "y" are used to place text, simulating lines as necessary + if( it_chunk->broken_spans.front().start.iter_span->y._set ) { + _y_offset = it_chunk->broken_spans.front().start.iter_span->y.computed - new_line.baseline_y; + } } } _flow._chunks.push_back(new_chunk); @@ -1243,8 +1261,9 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, UnbrokenSpanPosition span_pos; for( ; ; ) { std::vector<ScanlineMaker::ScanRun> scan_runs; - scan_runs = _scanline_maker->makeScanline(*line_height); + scan_runs = _scanline_maker->makeScanline(*line_height); // Only one line with "InfiniteScanlineMaker while (scan_runs.empty()) { + // Only used by ShapeScanlineMaker if (!_goToNextWrapShape()) return false; // no more shapes to wrap in to scan_runs = _scanline_maker->makeScanline(*line_height); } diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index fc40643c8..19e6c0fe6 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -63,7 +63,7 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &/*bbox_to_snap*/, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const */*it*/) const { @@ -75,20 +75,37 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, const LineList lines = _getSnapLines(p.getPoint()); for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) { - if (Geom::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint - // constraint line - Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint(); - Geom::Line line1(point_on_line, point_on_line + c.getDirection()); - - // grid/guide line - Geom::Point const p1 = i->second; // point at guide/grid line - Geom::Point const p2 = p1 + Geom::rot90(i->first); // 2nd point at guide/grid line - Geom::Line line2(p1, p2); + Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint(); + Geom::Line gridguide_line(i->second, i->second + Geom::rot90(i->first)); + if (c.isCircular()) { + // Find the intersections between the line and the circular constraint + // First, project the origin of the circle onto the line + Geom::Point const origin = c.getPoint(); + Geom::Point const p_proj = Geom::projection(origin, gridguide_line); + Geom::Coord dist = Geom::L2(p_proj - origin); // distance from circle origin to constraint line + Geom::Coord radius = c.getRadius(); + if (dist == radius) { + // Only one point of intersection; + _addSnappedPoint(sc, p_proj, Geom::L2(p.getPoint() - p_proj), p.getSourceType(), p.getSourceNum(), true); + } else if (dist < radius) { + // Two points of intersection, symmetrical with respect to the projected point + // Calculate half the length of the linesegment between the two points of intersection + Geom::Coord l = sqrt(radius*radius - dist*dist); + Geom::Coord d = Geom::L2(gridguide_line.versor()); // length of versor, needed to normalize the versor + if (d > 0) { + Geom::Point v = l*gridguide_line.versor()/d; + _addSnappedPoint(sc, p_proj + v, Geom::L2(p.getPoint() - (p_proj + v)), p.getSourceType(), p.getSourceNum(), true); + _addSnappedPoint(sc, p_proj - v, Geom::L2(p.getPoint() - (p_proj - v)), p.getSourceType(), p.getSourceNum(), true); + } + } + } else { + // Find the intersections between the line and the linear constraint + Geom::Line constraint_line(point_on_line, point_on_line + c.getDirection()); Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default try { - inters = Geom::intersection(line1, line2); + inters = Geom::intersection(constraint_line, gridguide_line); } catch (Geom::InfiniteSolutions e) { @@ -97,7 +114,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, } if (inters) { - Geom::Point t = line1.pointAt((*inters).ta); + Geom::Point t = constraint_line.pointAt((*inters).ta); const Geom::Coord dist = Geom::L2(t - p.getPoint()); if (dist < getSnapperTolerance()) { // When doing a constrained snap, we're already at an intersection. @@ -105,15 +122,6 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, // to look for additional intersections; just return the snapped point // and forget about the line _addSnappedPoint(sc, t, dist, p.getSourceType(), p.getSourceNum(), true); - // For any line that's within range, we will also look at it's "point on line" p1. For guides - // this point coincides with its origin; for grids this is of no use, but we cannot - // discern between grids and guides here - Geom::Coord const dist_p1 = Geom::L2(p1 - p.getPoint()); - if (dist_p1 < getSnapperTolerance()) { - _addSnappedLinesOrigin(sc, p1, dist_p1, p.getSourceType(), p.getSourceNum(), true); - // Only relevant for guides; grids don't have an origin per line - // Therefore _addSnappedLinesOrigin() will only be implemented for guides - } } } } diff --git a/src/line-snapper.h b/src/line-snapper.h index 1aa3526cc..4f3d17998 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -34,7 +34,7 @@ public: void constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const *it) const; protected: diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index adf172247..70e8cbaf8 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -21,6 +21,7 @@ lpeobject-reference.cpp lpe-patternalongpath.cpp lpe-perp_bisector.cpp lpe-perspective_path.cpp +lpe-powerstroke.cpp lpe-skeleton.cpp lpe-sketch.cpp lpe-spiro.cpp diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 1cc2e1b69..aacabc2db 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -67,6 +67,8 @@ ink_common_sources += \ live_effects/lpe-parallel.h \ live_effects/lpe-copy_rotate.cpp \ live_effects/lpe-copy_rotate.h \ + live_effects/lpe-powerstroke.cpp \ + live_effects/lpe-powerstroke.h \ live_effects/lpe-offset.cpp \ live_effects/lpe-offset.h \ live_effects/lpe-ruler.cpp \ diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h index 6f1004ae5..535cc2564 100644 --- a/src/live_effects/effect-enum.h +++ b/src/live_effects/effect-enum.h @@ -48,6 +48,7 @@ enum EffectType { DYNASTROKE, RECURSIVE_SKELETON, EXTRUDE, + POWERSTROKE, INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan) }; diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 6266fade0..9dbd27b50 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -75,6 +75,7 @@ #include "live_effects/lpe-line_segment.h" #include "live_effects/lpe-recursiveskeleton.h" #include "live_effects/lpe-extrude.h" +#include "live_effects/lpe-powerstroke.h" namespace Inkscape { @@ -100,6 +101,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { {PATH_LENGTH, N_("Path length"), "path_length"}, {PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"}, {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"}, + {POWERSTROKE, N_("Power stroke"), "powerstroke"}, {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"}, {RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"}, {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"}, @@ -120,6 +122,7 @@ const Util::EnumData<EffectType> LPETypeData[] = { {ROUGH_HATCHES, N_("Hatches (rough)"), "rough_hatches"}, {SKETCH, N_("Sketch"), "sketch"}, {RULER, N_("Ruler"), "ruler"}, +/* 0.49 */ }; const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData)); @@ -237,6 +240,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case EXTRUDE: neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) ); break; + case POWERSTROKE: + neweffect = static_cast<Effect*> ( new LPEPowerStroke(lpeobj) ); + break; default: g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr); neweffect = NULL; diff --git a/src/live_effects/lpe-circle_3pts.cpp b/src/live_effects/lpe-circle_3pts.cpp index d600ef046..951a3b7c8 100644 --- a/src/live_effects/lpe-circle_3pts.cpp +++ b/src/live_effects/lpe-circle_3pts.cpp @@ -17,6 +17,7 @@ // You might need to include other 2geom files. You can add them here: #include <2geom/path.h> +#include <2geom/circle.h> namespace Inkscape { namespace LivePathEffect { @@ -30,24 +31,6 @@ LPECircle3Pts::~LPECircle3Pts() { } -static void _circle(Geom::Point center, double radius, std::vector<Geom::Path> &path_out) { - using namespace Geom; - - Geom::Path pb; - - D2<SBasis> B; - Linear bo = Linear(0, 2 * M_PI); - - B[0] = cos(bo,4); - B[1] = sin(bo,4); - - B = B * radius + center; - - pb.append(SBasisCurve(B)); - - path_out.push_back(pb); -} - static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, std::vector<Geom::Path> &path_out) { using namespace Geom; @@ -64,7 +47,8 @@ static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point con Point M = D + v * lambda; double radius = L2(M - A); - _circle(M, radius, path_out); + Geom::Circle c(M, radius); + c.getPath(path_out); } std::vector<Geom::Path> diff --git a/src/live_effects/lpe-circle_with_radius.cpp b/src/live_effects/lpe-circle_with_radius.cpp index 574a9c004..71611e18b 100644 --- a/src/live_effects/lpe-circle_with_radius.cpp +++ b/src/live_effects/lpe-circle_with_radius.cpp @@ -18,6 +18,7 @@ #include <2geom/sbasis.h> #include <2geom/bezier-to-sbasis.h> #include <2geom/d2.h> +#include <2geom/circle.h> using namespace Geom; @@ -38,22 +39,6 @@ LPECircleWithRadius::~LPECircleWithRadius() } -void _circle(Geom::Point center, double radius, std::vector<Geom::Path> &path_out) { - Geom::Path pb; - - D2<SBasis> B; - Linear bo = Linear(0, 2 * M_PI); - - B[0] = cos(bo,4); - B[1] = sin(bo,4); - - B = B * radius + center; - - pb.append(SBasisCurve(B)); - - path_out.push_back(pb); -} - std::vector<Geom::Path> LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in) { @@ -64,7 +49,8 @@ LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in) double radius = Geom::L2(pt - center); - _circle(center, radius, path_out); + Geom::Circle c(center, radius); + c.getPath(path_out); return path_out; } diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp new file mode 100644 index 000000000..6109ea498 --- /dev/null +++ b/src/live_effects/lpe-powerstroke.cpp @@ -0,0 +1,262 @@ +#define INKSCAPE_LPE_POWERSTROKE_CPP +/** \file + * @brief PowerStroke LPE implementation. Creates curves with modifiable stroke width. + */ +/* Authors: + * Johan Engelen <j.b.c.engelen@utwente.nl> + * + * Copyright (C) 2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-powerstroke.h" + +#include "sp-shape.h" +#include "display/curve.h" + +#include <2geom/path.h> +#include <2geom/piecewise.h> +#include <2geom/sbasis-geometric.h> +#include <2geom/transforms.h> +#include <2geom/bezier-utils.h> + + +/// @TODO Move this to 2geom +namespace Geom { +namespace Interpolate { + +class Interpolator { +public: + Interpolator() {}; + virtual ~Interpolator() {}; + +// virtual Piecewise<D2<SBasis> > interpolateToPwD2Sb(std::vector<Point> points) = 0; + virtual Path interpolateToPath(std::vector<Point> points) = 0; + +private: + Interpolator(const Interpolator&); + Interpolator& operator=(const Interpolator&); +}; + +class Linear : public Interpolator { +public: + Linear() {}; + virtual ~Linear() {}; + + virtual Path interpolateToPath(std::vector<Point> points) { + Path path; + path.start( points.at(0) ); + for (unsigned int i = 1 ; i < points.size(); ++i) { + path.appendNew<Geom::LineSegment>(points.at(i)); + } + return path; + }; + +private: + Linear(const Linear&); + Linear& operator=(const Linear&); +}; + +// this class is terrible +class CubicBezierFit : public Interpolator { +public: + CubicBezierFit() {}; + virtual ~CubicBezierFit() {}; + + virtual Path interpolateToPath(std::vector<Point> points) { + unsigned int n_points = points.size(); + // worst case gives us 2 segment per point + int max_segs = 8*n_points; + Geom::Point * b = g_new(Geom::Point, max_segs); + Geom::Point * points_array = g_new(Geom::Point, 4*n_points); + for (unsigned i = 0; i < n_points; ++i) { + points_array[i] = points.at(i); + } + + double tolerance_sq = 0; // this value is just a random guess + + int const n_segs = Geom::bezier_fit_cubic_r(b, points_array, n_points, + tolerance_sq, max_segs); + + Geom::Path fit; + if ( n_segs > 0) + { + fit.start(b[0]); + for (int c = 0; c < n_segs; c++) { + fit.appendNew<Geom::CubicBezier>(b[4*c+1], b[4*c+2], b[4*c+3]); + } + } + g_free(b); + g_free(points_array); + return fit; + }; + +private: + CubicBezierFit(const CubicBezierFit&); + CubicBezierFit& operator=(const CubicBezierFit&); +}; + +/// @todo invent name for this class +class CubicBezierJohan : public Interpolator { +public: + CubicBezierJohan() {}; + virtual ~CubicBezierJohan() {}; + + virtual Path interpolateToPath(std::vector<Point> points) { + Path fit; + fit.start(points.at(0)); + for (unsigned int i = 1; i < points.size(); ++i) { + Point p0 = points.at(i-1); + Point p1 = points.at(i); + Point dx = Point(p1[X] - p0[X], 0); + fit.appendNew<CubicBezier>(p0+0.2*dx, p1-0.2*dx, p1); + } + return fit; + }; + +private: + CubicBezierJohan(const CubicBezierJohan&); + CubicBezierJohan& operator=(const CubicBezierJohan&); +}; + +} //namespace Interpolate +} //namespace Geom + +namespace Inkscape { +namespace LivePathEffect { + +LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this), + sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve."), "sort_points", &wr, this, true) +{ + show_orig_path = true; + + /// @todo offset_points are initialized with empty path, is that bug-save? + + registerParameter( dynamic_cast<Parameter *>(&offset_points) ); + registerParameter( dynamic_cast<Parameter *>(&sort_points) ); +} + +LPEPowerStroke::~LPEPowerStroke() +{ + +} + + +void +LPEPowerStroke::doOnApply(SPLPEItem *lpeitem) +{ + std::vector<Geom::Point> points; + Geom::Path::size_type size = SP_SHAPE(lpeitem)->curve->get_pathvector().front().size_open(); + points.push_back( Geom::Point(0,0) ); + points.push_back( Geom::Point(0.5*size,0) ); + points.push_back( Geom::Point(size,0) ); + offset_points.param_set_and_write_new_value(points); +} + +static bool compare_offsets (Geom::Point first, Geom::Point second) +{ + return first[Geom::X] < second[Geom::X]; +} + + +Geom::Piecewise<Geom::D2<Geom::SBasis> > +LPEPowerStroke::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) +{ + using namespace Geom; + + offset_points.set_pwd2(pwd2_in); + + Piecewise<D2<SBasis> > der = unitVector(derivative(pwd2_in)); + Piecewise<D2<SBasis> > n = rot90(der); + offset_points.set_pwd2_normal(n); + + // see if we should treat the path as being closed. + bool closed_path = false; + if ( are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) { + closed_path = true; + } + + Piecewise<D2<SBasis> > output; + if (!closed_path) { + // perhaps use std::list instead of std::vector? + std::vector<Geom::Point> ts(offset_points.data().size() + 2); + // first and last point coincide with input path (for now at least) + ts.front() = Point(pwd2_in.domain().min(),0); + ts.back() = Point(pwd2_in.domain().max(),0); + for (unsigned int i = 0; i < offset_points.data().size(); ++i) { + ts.at(i+1) = offset_points.data().at(i); + } + + if (sort_points) { + sort(ts.begin(), ts.end(), compare_offsets); + } + + // create stroke path where points (x,y) := (t, offset) + Geom::Interpolate::CubicBezierJohan interpolator; + Path strokepath = interpolator.interpolateToPath(ts); + Path mirroredpath = strokepath.reverse() * Geom::Scale(1,-1); + + strokepath.append(mirroredpath, Geom::Path::STITCH_DISCONTINUOUS); + strokepath.close(); + + D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb()); + Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]); + Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]); + + output = compose(pwd2_in,x) + y*compose(n,x); + } else { + // path is closed + + // perhaps use std::list instead of std::vector? + std::vector<Geom::Point> ts = offset_points.data(); + if (sort_points) { + sort(ts.begin(), ts.end(), compare_offsets); + } + // add extra points for interpolation between first and last point + Point first_point = ts.front(); + Point last_point = ts.back(); + ts.insert(ts.begin(), last_point - Point(pwd2_in.domain().extent() ,0)); + ts.push_back( first_point + Point(pwd2_in.domain().extent() ,0) ); + // create stroke path where points (x,y) := (t, offset) + Geom::Interpolate::CubicBezierJohan interpolator; + Path strokepath = interpolator.interpolateToPath(ts); + + // output 2 separate paths + D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(strokepath.toPwSb()); + Piecewise<SBasis> x = Piecewise<SBasis>(patternd2[0]); + Piecewise<SBasis> y = Piecewise<SBasis>(patternd2[1]); + // find time values for which x lies outside path domain + // and only take portion of x and y that lies within those time values + std::vector< double > rtsmin = roots (x - pwd2_in.domain().min()); + std::vector< double > rtsmax = roots (x - pwd2_in.domain().max()); + if ( !rtsmin.empty() && !rtsmax.empty() ) { + x = portion(x, rtsmin.at(0), rtsmax.at(0)); + y = portion(y, rtsmin.at(0), rtsmax.at(0)); + } + output = compose(pwd2_in,x) + y*compose(n,x); + x = reverse(x); + y = reverse(y); + output.concat(compose(pwd2_in,x) - y*compose(n,x)); + } + + return output; +} + +/* ######################## */ + +} //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:encoding=utf-8:textwidth=99 : diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h new file mode 100644 index 000000000..6c208fda4 --- /dev/null +++ b/src/live_effects/lpe-powerstroke.h @@ -0,0 +1,53 @@ +/** @file + * @brief PowerStroke LPE effect, see lpe-powerstroke.cpp. + */ +/* Authors: + * Johan Engelen <j.b.c.engelen@utwente.nl> + * + * Copyright (C) 2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_LPE_POWERSTROKE_H +#define INKSCAPE_LPE_POWERSTROKE_H + +#include "live_effects/effect.h" +#include "live_effects/parameter/bool.h" +#include "live_effects/parameter/powerstrokepointarray.h" + +namespace Inkscape { +namespace LivePathEffect { + +class LPEPowerStroke : public Effect { +public: + LPEPowerStroke(LivePathEffectObject *lpeobject); + virtual ~LPEPowerStroke(); + + virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); + + virtual void doOnApply(SPLPEItem *lpeitem); + +private: + PowerStrokePointArrayParam offset_points; + BoolParam sort_points; + + LPEPowerStroke(const LPEPowerStroke&); + LPEPowerStroke& operator=(const LPEPowerStroke&); +}; + +} //namespace LivePathEffect +} //namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/live_effects/parameter/CMakeLists.txt b/src/live_effects/parameter/CMakeLists.txt index 2fa0d5bc6..8657b2bec 100644 --- a/src/live_effects/parameter/CMakeLists.txt +++ b/src/live_effects/parameter/CMakeLists.txt @@ -5,6 +5,7 @@ parameter.cpp path.cpp path-reference.cpp point.cpp +powerstrokepointarray.cpp random.cpp text.cpp unit.cpp diff --git a/src/live_effects/parameter/Makefile_insert b/src/live_effects/parameter/Makefile_insert index 74b166e8b..99cd88d62 100644 --- a/src/live_effects/parameter/Makefile_insert +++ b/src/live_effects/parameter/Makefile_insert @@ -16,6 +16,8 @@ ink_common_sources += \ live_effects/parameter/path-reference.h \ live_effects/parameter/path.cpp \ live_effects/parameter/path.h \ + live_effects/parameter/powerstrokepointarray.cpp \ + live_effects/parameter/powerstrokepointarray.h \ live_effects/parameter/text.cpp \ live_effects/parameter/text.h \ live_effects/parameter/unit.cpp \ diff --git a/src/live_effects/parameter/array.cpp b/src/live_effects/parameter/array.cpp index c576bedd5..d1c30edf7 100644 --- a/src/live_effects/parameter/array.cpp +++ b/src/live_effects/parameter/array.cpp @@ -12,6 +12,7 @@ #include "svg/stringstream.h" #include <2geom/coord.h> +#include <2geom/point.h> namespace Inkscape { @@ -35,6 +36,21 @@ ArrayParam<float>::readsvg(const gchar * str) return newx; } +template <> +Geom::Point +ArrayParam<Geom::Point>::readsvg(const gchar * str) +{ + gchar ** strarray = g_strsplit(str, ",", 2); + double newx, newy; + unsigned int success = sp_svg_number_read_d(strarray[0], &newx); + success += sp_svg_number_read_d(strarray[1], &newy); + g_strfreev (strarray); + if (success == 2) { + return Geom::Point(newx, newy); + } + return Geom::Point(Geom::infinity(),Geom::infinity()); +} + } /* namespace LivePathEffect */ } /* namespace Inkscape */ diff --git a/src/live_effects/parameter/array.h b/src/live_effects/parameter/array.h index e5f230111..89c344594 100644 --- a/src/live_effects/parameter/array.h +++ b/src/live_effects/parameter/array.h @@ -85,10 +85,7 @@ public: g_free(str); } -private: - ArrayParam(const ArrayParam&); - ArrayParam& operator=(const ArrayParam&); - +protected: std::vector<StorageType> _vector; size_t _default_size; @@ -103,6 +100,10 @@ private: } StorageType readsvg(const gchar * str); + +private: + ArrayParam(const ArrayParam&); + ArrayParam& operator=(const ArrayParam&); }; diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp new file mode 100644 index 000000000..c20980193 --- /dev/null +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -0,0 +1,173 @@ +#define INKSCAPE_LIVEPATHEFFECT_POWERSTROKE_POINT_ARRAY_CPP + +/* + * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/parameter/powerstrokepointarray.h" + +#include "live_effects/effect.h" +#include "svg/svg.h" +#include "svg/stringstream.h" +#include "knotholder.h" +#include "sp-lpe-item.h" + +#include <2geom/piecewise.h> +#include <2geom/sbasis-geometric.h> + +// needed for on-canvas editting: +#include "desktop.h" + +namespace Inkscape { + +namespace LivePathEffect { + +PowerStrokePointArrayParam::PowerStrokePointArrayParam( const Glib::ustring& label, const Glib::ustring& tip, + const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, + Effect* effect, const gchar *htip) + : ArrayParam<Geom::Point>(label, tip, key, wr, effect, 0) +{ + knot_shape = SP_KNOT_SHAPE_DIAMOND; + knot_mode = SP_KNOT_MODE_XOR; + knot_color = 0xff00ff00; + handle_tip = g_strdup(htip); +} + +PowerStrokePointArrayParam::~PowerStrokePointArrayParam() +{ + if (handle_tip) + g_free(handle_tip); +} + +Gtk::Widget * +PowerStrokePointArrayParam::param_newWidget(Gtk::Tooltips * /*tooltips*/) +{ + return NULL; +/* + Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg = Gtk::manage( + new Inkscape::UI::Widget::RegisteredTransformedPoint( param_label, + param_tooltip, + param_key, + *param_wr, + param_effect->getRepr(), + param_effect->getSPDoc() ) ); + // TODO: fix to get correct desktop (don't use SP_ACTIVE_DESKTOP) + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Geom::Matrix transf = desktop->doc2dt(); + pointwdg->setTransform(transf); + pointwdg->setValue( *this ); + pointwdg->clearProgrammatically(); + pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter")); + + Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() ); + static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true); + static_cast<Gtk::HBox*>(hbox)->show_all_children(); + + return dynamic_cast<Gtk::Widget *> (hbox); +*/ +} + + +void +PowerStrokePointArrayParam::param_transform_multiply(Geom::Matrix const& postmul, bool /*set*/) +{ +// param_set_and_write_new_value( (*this) * postmul ); +} + + +void +PowerStrokePointArrayParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color) +{ + knot_shape = shape; + knot_mode = mode; + knot_color = color; +} + +class PowerStrokePointArrayParamKnotHolderEntity : public LPEKnotHolderEntity { +public: + PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index); + virtual ~PowerStrokePointArrayParamKnotHolderEntity() {} + + virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); + virtual Geom::Point knot_get(); + virtual void knot_click(guint state); + +private: + PowerStrokePointArrayParam *_pparam; + unsigned int _index; +}; + +PowerStrokePointArrayParamKnotHolderEntity::PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index) + : _pparam(p), + _index(index) +{ +} + +void +PowerStrokePointArrayParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) +{ +/// @todo how about item transforms??? + using namespace Geom; + Piecewise<D2<SBasis> > const & pwd2 = _pparam->get_pwd2(); + Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal(); + + Geom::Point const s = snap_knot_position(p); + double t = nearest_point(s, pwd2); + double offset = dot(s - pwd2.valueAt(t), n.valueAt(t)); + _pparam->_vector.at(_index) = Geom::Point(t, offset); + sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false); +} + +Geom::Point +PowerStrokePointArrayParamKnotHolderEntity::knot_get() +{ + using namespace Geom; + Piecewise<D2<SBasis> > const & pwd2 = _pparam->get_pwd2(); + Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal(); + + Point offset_point = _pparam->_vector.at(_index); + + Point canvas_point = pwd2.valueAt(offset_point[X]) + offset_point[Y] * n.valueAt(offset_point[X]); + return canvas_point; +} + +void +PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state) +{ + g_print ("This is the %d handle associated to parameter '%s'\n", _index, _pparam->param_key.c_str()); + + if (state & GDK_CONTROL_MASK) { + std::vector<Geom::Point> & vec = _pparam->_vector; + vec.insert(vec.begin() + _index, 1, vec.at(_index)); + _pparam->param_set_and_write_new_value(vec); + g_print ("Added handle %d associated to parameter '%s'\n", _index, _pparam->param_key.c_str()); + /// @todo this BUGS ! the knot stuff should be reloaded when adding a new node! + } +} + +void +PowerStrokePointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +{ + for (unsigned int i = 0; i < _vector.size(); ++i) { + PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i); + e->create(desktop, item, knotholder, handle_tip, knot_shape, knot_mode, knot_color); + knotholder->add(e); + } +} + +} /* 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/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h new file mode 100644 index 000000000..06d406dfe --- /dev/null +++ b/src/live_effects/parameter/powerstrokepointarray.h @@ -0,0 +1,71 @@ +#ifndef INKSCAPE_LIVEPATHEFFECT_POWERSTROKE_POINT_ARRAY_H +#define INKSCAPE_LIVEPATHEFFECT_POWERSTROKE_POINT_ARRAY_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/gtypes.h> +#include <2geom/point.h> + +#include <gtkmm/tooltips.h> + +#include "live_effects/parameter/array.h" + +#include "knot-holder-entity.h" + +namespace Inkscape { + +namespace LivePathEffect { + +class PowerStrokePointArrayParamKnotHolderEntity; + +class PowerStrokePointArrayParam : public ArrayParam<Geom::Point> { +public: + PowerStrokePointArrayParam( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Inkscape::UI::Widget::Registry* wr, + Effect* effect, + const gchar *handle_tip = NULL); // tip for automatically associated on-canvas handle + virtual ~PowerStrokePointArrayParam(); + + virtual Gtk::Widget * param_newWidget(Gtk::Tooltips * tooltips); + + virtual void param_transform_multiply(Geom::Matrix const& /*postmul*/, bool /*set*/); + + void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color); + + virtual bool providesKnotHolderEntities() { return true; } + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + + void set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) { last_pwd2 = pwd2_in; } + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2() { return last_pwd2; } + void set_pwd2_normal(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) { last_pwd2_normal = pwd2_in; } + Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2_normal() { return last_pwd2_normal; } + + friend class PowerStrokePointArrayParamKnotHolderEntity; + +private: + PowerStrokePointArrayParam(const PowerStrokePointArrayParam&); + PowerStrokePointArrayParam& operator=(const PowerStrokePointArrayParam&); + + SPKnotShapeType knot_shape; + SPKnotModeType knot_mode; + guint32 knot_color; + gchar *handle_tip; + + Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2; + Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal; +}; + + +} //namespace LivePathEffect + +} //namespace Inkscape + +#endif diff --git a/src/main.cpp b/src/main.cpp index 9f7bc9ad3..78b66d847 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1345,7 +1345,7 @@ sp_do_export_png(SPDocument *doc) if (sp_export_area) { /* Try to parse area (given in SVG pixels) */ gdouble x0,y0,x1,y1; - if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) { + if (sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) != 4) { g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area); return; } diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index 4e4c45b8c..9c0ca1767 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -108,8 +108,9 @@ static char const menus_skeleton[] = " <verb verb-id=\"ViewModeNormal\" radio=\"yes\" default=\"yes\"/>\n" " <verb verb-id=\"ViewModeNoFilters\" radio=\"yes\"/>\n" " <verb verb-id=\"ViewModeOutline\" radio=\"yes\"/>\n" -" <verb verb-id=\"ViewModePrintColorsPreview\" radio=\"yes\"/>\n" -" <verb verb-id=\"DialogPrintColorsPreview\" />\n" +// Better location in menu needs to be found +//" <verb verb-id=\"ViewModePrintColorsPreview\" radio=\"yes\"/>\n" +//" <verb verb-id=\"DialogPrintColorsPreview\" />\n" " </submenu>\n" " <separator/>\n" " <verb verb-id=\"ToggleGrid\" />\n" diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 6bbcda93c..6db280670 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -134,7 +134,7 @@ RectKnotHolderEntityRX::knot_set(Geom::Point const &p, Geom::Point const &/*orig //In general we cannot just snap this radius to an arbitrary point, as we have only a single //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap //the radius then we should have a constrained snap. snap_knot_position() is unconstrained - Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0))); + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(-1, 0))); if (state & GDK_CONTROL_MASK) { gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; @@ -184,7 +184,7 @@ RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*orig //In general we cannot just snap this radius to an arbitrary point, as we have only a single //degree of freedom. For snapping to an arbitrary point we need two DOF. If we're going to snap //the radius then we should have a constrained snap. snap_knot_position() is unconstrained - Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1))); + Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1))); if (state & GDK_CONTROL_MASK) { // When holding control then rx will be kept equal to ry, // resulting in a perfect circle (and not an ellipse) @@ -273,13 +273,13 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or // snap to horizontal or diagonal if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->height.computed = MAX(h_orig + minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->height.computed = MAX(h_orig, 0); @@ -290,13 +290,13 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or // snap to vertical or diagonal if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->width.computed = MAX(w_orig + miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->width.computed = MAX(w_orig, 0); @@ -363,14 +363,14 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin // snap to horizontal or diagonal if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y); rect->height.computed = MAX(h_orig - minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-1, 0))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->y.computed = MIN(origin[Geom::Y], opposite_y); @@ -382,14 +382,14 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin // snap to vertical or diagonal if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(-ratio, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x); rect->width.computed = MAX(w_orig - miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); + s = snap_knot_position_constrained(p, Inkscape::Snapper::SnapConstraint(p_handle, Geom::Point(0, -1))); minx = s[Geom::X] - origin[Geom::X]; miny = s[Geom::Y] - origin[Geom::Y]; rect->x.computed = MIN(origin[Geom::X], opposite_x); diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index bced0ac44..d84ee9c4f 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -6,16 +6,18 @@ * Carl Hetherington <inkscape@carlh.net> * Diederik van Lierop <mail@diedenrezi.nl> * - * Copyright (C) 2005 - 2008 Authors + * Copyright (C) 2005 - 2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #include "svg/svg.h" #include <2geom/path-intersection.h> +#include <2geom/pathvector.h> #include <2geom/point.h> #include <2geom/rect.h> #include <2geom/line.h> +#include <2geom/circle.h> #include "document.h" #include "sp-namedview.h" #include "sp-image.h" @@ -73,24 +75,24 @@ bool Inkscape::ObjectSnapper::getSnapperAlwaysSnap() const * \param parent Pointer to the document's root, or to a clipped path or mask object * \param it List of items to ignore * \param bbox_to_snap Bounding box hulling the whole bunch of points, all from the same selection and having the same transformation - * \param DimensionToSnap Snap in X, Y, or both directions. */ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, std::vector<SPItem const *> const *it, bool const &first_point, Geom::Rect const &bbox_to_snap, - DimensionToSnap const snap_dim, bool const clip_or_mask, Geom::Matrix const additional_affine) const // transformation of the item being clipped / masked { - bool const c1 = (snap_dim == TRANSL_SNAP_XY) && ThisSnapperMightSnap(); - bool const c2 = (snap_dim != TRANSL_SNAP_XY) && GuidesMightSnap(); - - if (!(c1 || c2)) { + if (!ThisSnapperMightSnap()) { return; } + if (_snapmanager->getDesktop() == NULL) { + g_warning("desktop == NULL, so we cannot snap; please inform the developpers of this bug"); + // Apparently the etup() method from the SnapManager class hasn't been called before trying to snap. + } + if (first_point) { _candidates->clear(); } @@ -99,7 +101,6 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, bbox_to_snap_incl.expandBy(getSnapperTolerance()); // see? for (SPObject* o = sp_object_first_child(parent); o != NULL; o = SP_OBJECT_NEXT(o)) { - g_assert(_snapmanager->getDesktop() != NULL); if (SP_IS_ITEM(o) && !(_snapmanager->getDesktop()->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) { // Snapping to items in a locked layer is allowed // Don't snap to hidden objects, unless they're a clipped path or a mask @@ -122,17 +123,17 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, // we should also consider that path or mask for snapping to obj = SP_OBJECT(item->clip_ref->getObject()); if (obj) { - _findCandidates(obj, it, false, bbox_to_snap, snap_dim, true, sp_item_i2doc_affine(item)); + _findCandidates(obj, it, false, bbox_to_snap, true, sp_item_i2doc_affine(item)); } obj = SP_OBJECT(item->mask_ref->getObject()); if (obj) { - _findCandidates(obj, it, false, bbox_to_snap, snap_dim, true, sp_item_i2doc_affine(item)); + _findCandidates(obj, it, false, bbox_to_snap, true, sp_item_i2doc_affine(item)); } } } if (SP_IS_GROUP(o)) { - _findCandidates(o, it, false, bbox_to_snap, snap_dim, clip_or_mask, additional_affine); + _findCandidates(o, it, false, bbox_to_snap, clip_or_mask, additional_affine); } else { Geom::OptRect bbox_of_item = Geom::Rect(); if (clip_or_mask) { @@ -147,7 +148,8 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, } if (bbox_of_item) { // See if the item is within range - if (bbox_to_snap_incl.intersects(*bbox_of_item)) { + if (bbox_to_snap_incl.intersects(*bbox_of_item) + || (_snapmanager->snapprefs.getIncludeItemCenter() && bbox_to_snap_incl.contains(item->getCenter()))) { // rotation center might be outside of the bounding box // This item is within snapping range, so record it as a candidate _candidates->push_back(SnapCandidateItem(item, clip_or_mask, additional_affine)); // For debugging: print the id of the candidate to the console @@ -227,11 +229,17 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapSourceType const &t, _snapmanager->snapprefs.setSnapIntersectionCS(false); } + bool old_pref2 = _snapmanager->snapprefs.getIncludeItemCenter(); + if ((*i).item == _snapmanager->getRotationCenterSource()) { + // don't snap to this item's rotation center + _snapmanager->snapprefs.setIncludeItemCenter(false); + } + sp_item_snappoints(root_item, *_points_to_snap_to, &_snapmanager->snapprefs); - if (_snapmanager->snapprefs.getSnapToItemPath()) { - _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); - } + // restore the original snap preferences + _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); + _snapmanager->snapprefs.setIncludeItemCenter(old_pref2); } //Collect the bounding box's corners so we can snap to them @@ -275,18 +283,16 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, } } -void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &sc, +void Inkscape::ObjectSnapper::_snapTranslatingGuide(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal) const { // Iterate through all nodes, find out which one is the closest to this guide, and snap to it! _collectNodes(SNAPSOURCE_GUIDE, true); - // Although we won't snap to paths here (which would give us under constrained snaps) we can still snap to intersections of paths. if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { _collectPaths(Inkscape::SnapCandidatePoint(p, SNAPSOURCE_GUIDE), true); _snapPaths(sc, Inkscape::SnapCandidatePoint(p, SNAPSOURCE_GUIDE), NULL, NULL); - // The paths themselves should be discarded in findBestSnap(), as we should only snap to their intersections } SnappedPoint s; @@ -294,6 +300,7 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s Geom::Coord tol = getSnapperTolerance(); for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { + // Project each node (*k) on the guide line (running through point p) Geom::Point p_proj = Geom::projection((*k).getPoint(), Geom::Line(p, p + Geom::rot90(guide_normal))); Geom::Coord dist = Geom::L2((*k).getPoint() - p_proj); // distance from node to the guide @@ -333,7 +340,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const & } // Consider the page border for snapping - if (_snapmanager->snapprefs.getSnapToPageBorder()) { + if (_snapmanager->snapprefs.getSnapToPageBorder() && _snapmanager->snapprefs.getSnapModeBBoxOrNodes()) { Geom::PathVector *border_path = _getBorderPathv(); if (border_path != NULL) { _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(border_path, SNAPTARGET_PAGE_BORDER, Geom::OptRect())); @@ -358,7 +365,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const & //Build a list of all paths considered for snapping to //Add the item's path to snap to - if (_snapmanager->snapprefs.getSnapToItemPath()) { + if (_snapmanager->snapprefs.getSnapToItemPath() && _snapmanager->snapprefs.getSnapModeNode()) { if (p_is_other || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node)) { // Snapping to the path of characters is very cool, but for a large // chunk of text this will take ages! So limit snapping to text paths @@ -382,9 +389,14 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const & if (!very_lenghty_prose && !very_complex_path) { SPCurve *curve = curve_for_item(root_item); if (curve) { - // We will get our own copy of the path, which must be freed at some point - Geom::PathVector *borderpathv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); - _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(borderpathv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. + // We will get our own copy of the pathvector, which must be freed at some point + + // Geom::PathVector *pv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); + + Geom::PathVector *pv = new Geom::PathVector(curve->get_pathvector()); + (*pv) *= sp_item_i2d_affine(root_item) * (*i).additional_affine * _snapmanager->getDesktop()->doc2dt(); // (_edit_transform * _i2d_transform); + + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(pv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. curve->unref(); } } @@ -392,7 +404,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const & } //Add the item's bounding box to snap to - if (_snapmanager->snapprefs.getSnapToBBoxPath()) { + if (_snapmanager->snapprefs.getSnapToBBoxPath() && _snapmanager->snapprefs.getSnapModeBBox()) { if (p_is_other || !(_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) { // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox // of the item AND the bbox of the clipping path at the same time @@ -517,7 +529,7 @@ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::ve void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, - ConstraintLine const &c) const + SnapConstraint const &c) const { _collectPaths(p, p.getSourceNum() == 0); @@ -532,36 +544,48 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, direction_vector = Geom::unit_vector(direction_vector); } - // The intersection point of the constraint line with any path, - // must lie within two points on the constraintline: p_min_on_cl and p_max_on_cl - // The distance between those points is twice the snapping tolerance + // The intersection point of the constraint line with any path, must lie within two points on the + // SnapConstraint: p_min_on_cl and p_max_on_cl. The distance between those points is twice the snapping tolerance Geom::Point const p_proj_on_cl = p.getPoint(); // projection has already been taken care of in constrainedSnap in the snapmanager; Geom::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector); Geom::Point const p_max_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector); + Geom::Coord tolerance = getSnapperTolerance(); + + // PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have + // to convert the snapper coordinates from the desktop coordinates to document coordinates - Geom::Path cl; - std::vector<Geom::Path> clv; - cl.start(p_min_on_cl); - cl.appendNew<Geom::LineSegment>(p_max_on_cl); - clv.push_back(cl); + std::vector<Geom::Path> constraint_path; + if (c.isCircular()) { + Geom::Circle constraint_circle(_snapmanager->getDesktop()->dt2doc(c.getPoint()), c.getRadius()); + constraint_circle.getPath(constraint_path); + } else { + Geom::Path constraint_line; + constraint_line.start(p_min_on_cl); + constraint_line.appendNew<Geom::LineSegment>(p_max_on_cl); + constraint_path.push_back(constraint_line); + } for (std::vector<Inkscape::SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { if (k->path_vector) { - Geom::CrossingSet cs = Geom::crossings(clv, *(k->path_vector)); - if (cs.size() > 0) { - // We need only the first element of cs, because cl is only a single straight linesegment - // This first element contains a vector filled with crossings of cl with k->first - for (std::vector<Geom::Crossing>::const_iterator m = cs[0].begin(); m != cs[0].end(); m++) { - if ((*m).ta >= 0 && (*m).ta <= 1 ) { - // Reconstruct the point of intersection - Geom::Point p_inters = p_min_on_cl + ((*m).ta) * (p_max_on_cl - p_min_on_cl); - // When it's within snapping range, then return it - // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1) - Geom::Coord dist = Geom::L2(_snapmanager->getDesktop()->dt2doc(p_proj_on_cl) - p_inters); - SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox); + Geom::CrossingSet cs = Geom::crossings(constraint_path, *(k->path_vector)); + unsigned int index = 0; + for (Geom::CrossingSet::const_iterator i = cs.begin(); i != cs.end(); i++) { + if (index >= constraint_path.size()) { + break; + } + for (Geom::Crossings::const_iterator m = (*i).begin(); m != (*i).end(); m++) { + //std::cout << "ta = " << (*m).ta << " | tb = " << (*m).tb << std::endl; + // Reconstruct the point of intersection + Geom::Point p_inters = constraint_path[index].pointAt((*m).ta); + // .. and convert it to desktop coordinates + p_inters = _snapmanager->getDesktop()->doc2dt(p_inters); + Geom::Coord dist = Geom::L2(p_proj_on_cl - p_inters); + SnappedPoint s = SnappedPoint(p_inters, p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox);; + if (dist <= tolerance) { // If the intersection is within snapping range, then we might snap to it sc.points.push_back(s); } } + index++; } } } @@ -581,18 +605,31 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, /* Get a list of all the SPItems that we will try to snap to */ if (p.getSourceNum() == 0) { Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint()); - _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); + _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, false, Geom::identity()); } - if (_snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() - || _snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapToPageBorder() - || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() - || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() - || _snapmanager->snapprefs.getIncludeItemCenter()) { + + bool snap_nodes = (_snapmanager->snapprefs.getSnapModeNode() && ( + _snapmanager->snapprefs.getSnapToItemNode() || + _snapmanager->snapprefs.getSnapSmoothNodes() || + _snapmanager->snapprefs.getSnapLineMidpoints() || + _snapmanager->snapprefs.getSnapObjectMidpoints() + )) || (_snapmanager->snapprefs.getSnapModeBBox() && ( + _snapmanager->snapprefs.getSnapToBBoxNode() || + _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || + _snapmanager->snapprefs.getSnapBBoxMidpoints() + )) || (_snapmanager->snapprefs.getSnapModeBBoxOrNodes() && ( + _snapmanager->snapprefs.getIncludeItemCenter() || + _snapmanager->snapprefs.getSnapToPageBorder() + )); + + if (snap_nodes) { _snapNodes(sc, p, unselected_nodes); } - if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { + if ((_snapmanager->snapprefs.getSnapModeNode() && _snapmanager->snapprefs.getSnapToItemPath()) || + (_snapmanager->snapprefs.getSnapModeBBox() && _snapmanager->snapprefs.getSnapToBBoxPath()) || + (_snapmanager->snapprefs.getSnapModeBBoxOrNodes() && _snapmanager->snapprefs.getSnapToPageBorder())) { unsigned n = (unselected_nodes == NULL) ? 0 : unselected_nodes->size(); if (n > 0) { /* While editing a path in the node tool, findCandidates must ignore that path because @@ -617,7 +654,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const *it) const { if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) { @@ -627,7 +664,7 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, /* Get a list of all the SPItems that we will try to snap to */ if (p.getSourceNum() == 0) { Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint()); - _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); + _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, false, Geom::identity()); } // A constrained snap, is a snap in only one degree of freedom (specified by the constraint line). @@ -654,17 +691,8 @@ void Inkscape::ObjectSnapper::guideFreeSnap(SnappedConstraints &sc, std::vector<SPItem*> cand; std::vector<SPItem const *> const it; //just an empty list - DimensionToSnap snap_dim; - if (guide_normal == to_2geom(component_vectors[Geom::Y])) { - snap_dim = GUIDE_TRANSL_SNAP_Y; - } else if (guide_normal == to_2geom(component_vectors[Geom::X])) { - snap_dim = GUIDE_TRANSL_SNAP_X; - } else { - snap_dim = ANGLED_GUIDE_TRANSL_SNAP; - } - - _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), snap_dim, false, Geom::identity()); - _snapTranslatingGuideToNodes(sc, p, guide_normal); + _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), false, Geom::identity()); + _snapTranslatingGuide(sc, p, guide_normal); } @@ -672,23 +700,14 @@ void Inkscape::ObjectSnapper::guideFreeSnap(SnappedConstraints &sc, void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal, - ConstraintLine const &/*c*/) const + SnapConstraint const &/*c*/) const { /* Get a list of all the SPItems that we will try to snap to */ std::vector<SPItem*> cand; std::vector<SPItem const *> const it; //just an empty list - DimensionToSnap snap_dim; - if (guide_normal == to_2geom(component_vectors[Geom::Y])) { - snap_dim = GUIDE_TRANSL_SNAP_Y; - } else if (guide_normal == to_2geom(component_vectors[Geom::X])) { - snap_dim = GUIDE_TRANSL_SNAP_X; - } else { - snap_dim = ANGLED_GUIDE_TRANSL_SNAP; - } - - _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), snap_dim, false, Geom::identity()); - _snapTranslatingGuideToNodes(sc, p, guide_normal); + _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), false, Geom::identity()); + _snapTranslatingGuide(sc, p, guide_normal); } @@ -697,29 +716,23 @@ void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc, */ bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const { - bool snap_to_something = _snapmanager->snapprefs.getSnapToItemPath() - || _snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() - || _snapmanager->snapprefs.getSnapToBBoxPath() - || _snapmanager->snapprefs.getSnapToBBoxNode() - || _snapmanager->snapprefs.getSnapToPageBorder() - || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() - || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() - || _snapmanager->snapprefs.getIncludeItemCenter(); - - return (_snap_enabled && _snapmanager->snapprefs.getSnapModeBBoxOrNodes() && snap_to_something); -} - -bool Inkscape::ObjectSnapper::GuidesMightSnap() const // almost the same as ThisSnapperMightSnap above, but only looking at points (and not paths) -{ - bool snap_to_something = _snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() - || _snapmanager->snapprefs.getSnapToPageBorder() - || (_snapmanager->snapprefs.getSnapModeBBox() && _snapmanager->snapprefs.getSnapToBBoxNode()) - || (_snapmanager->snapprefs.getSnapModeBBox() && (_snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints())) - || (_snapmanager->snapprefs.getSnapModeNode() && (_snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints())) - || (_snapmanager->snapprefs.getSnapModeNode() && _snapmanager->snapprefs.getIncludeItemCenter()) - || (_snapmanager->snapprefs.getSnapModeNode() && (_snapmanager->snapprefs.getSnapToItemPath() && _snapmanager->snapprefs.getSnapIntersectionCS())); - - return (_snap_enabled && _snapmanager->snapprefs.getSnapModeGuide() && snap_to_something); + bool snap_to_something = (_snapmanager->snapprefs.getSnapModeNode() && ( + _snapmanager->snapprefs.getSnapToItemPath() || + _snapmanager->snapprefs.getSnapToItemNode() || + _snapmanager->snapprefs.getSnapSmoothNodes() || + _snapmanager->snapprefs.getSnapLineMidpoints() || + _snapmanager->snapprefs.getSnapObjectMidpoints() + )) || (_snapmanager->snapprefs.getSnapModeBBox() && ( + _snapmanager->snapprefs.getSnapToBBoxPath() || + _snapmanager->snapprefs.getSnapToBBoxNode() || + _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || + _snapmanager->snapprefs.getSnapBBoxMidpoints() + )) || (_snapmanager->snapprefs.getSnapModeBBoxOrNodes() && ( + _snapmanager->snapprefs.getSnapToPageBorder() || + _snapmanager->snapprefs.getIncludeItemCenter() + )); + + return (_snap_enabled && snap_to_something); } void Inkscape::ObjectSnapper::_clear_paths() const diff --git a/src/object-snapper.h b/src/object-snapper.h index caf643f73..99c8a077e 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -33,12 +33,6 @@ public: ObjectSnapper(SnapManager *sm, Geom::Coord const d); ~ObjectSnapper(); - enum DimensionToSnap { - GUIDE_TRANSL_SNAP_X, // For snapping a vertical guide (normal in the X-direction) to objects, - GUIDE_TRANSL_SNAP_Y, // For snapping a horizontal guide (normal in the Y-direction) to objects - ANGLED_GUIDE_TRANSL_SNAP, // For snapping an angled guide, while translating it accross the desktop - TRANSL_SNAP_XY}; // All other cases; for snapping to objects, other than guides - void guideFreeSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal) const; @@ -46,10 +40,9 @@ public: void guideConstrainedSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal, - ConstraintLine const &c) const; + SnapConstraint const &c) const; bool ThisSnapperMightSnap() const; - bool GuidesMightSnap() const; Geom::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom) bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance @@ -63,7 +56,7 @@ public: void constrainedSnap(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, - ConstraintLine const &c, + SnapConstraint const &c, std::vector<SPItem const *> const *it) const; private: @@ -76,7 +69,6 @@ private: std::vector<SPItem const *> const *it, bool const &first_point, Geom::Rect const &bbox_to_snap, - DimensionToSnap snap_dim, bool const _clip_or_mask, Geom::Matrix const additional_affine) const; @@ -84,7 +76,7 @@ private: Inkscape::SnapCandidatePoint const &p, std::vector<SnapCandidatePoint> *unselected_nodes) const; // in desktop coordinates - void _snapTranslatingGuideToNodes(SnappedConstraints &sc, + void _snapTranslatingGuide(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal) const; @@ -98,7 +90,7 @@ private: void _snapPathsConstrained(SnappedConstraints &sc, Inkscape::SnapCandidatePoint const &p, // in desktop coordinates - ConstraintLine const &c) const; + SnapConstraint const &c) const; bool isUnselectedNode(Geom::Point const &point, std::vector<Inkscape::SnapCandidatePoint> const *unselected_nodes) const; diff --git a/src/pen-context.cpp b/src/pen-context.cpp index 5b9f6808a..4a21d7bcb 100644 --- a/src/pen-context.cpp +++ b/src/pen-context.cpp @@ -1470,7 +1470,7 @@ void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt } } else { // Create a horizontal or vertical constraint line - Inkscape::Snapper::ConstraintLine cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0)); + Inkscape::Snapper::SnapConstraint cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0)); // Snap along the constraint line; if we didn't snap then still the constraint will be applied SnapManager &m = pc->desktop->namedview->snap_manager; diff --git a/src/pixmaps/cursor-node-d.xpm b/src/pixmaps/cursor-node-d.xpm index cdc792a3a..264da58c3 100644 --- a/src/pixmaps/cursor-node-d.xpm +++ b/src/pixmaps/cursor-node-d.xpm @@ -2,7 +2,7 @@ static char const *cursor_node_d_xpm[] = { "32 32 3 1", " g None", -". g #FFFFFF", +". g #ffffff", "+ g #000000", " . ", ".+. ", @@ -18,21 +18,21 @@ static char const *cursor_node_d_xpm[] = { " .++++++. ", " .+++++. ", " .+++.. ", -" .+. ++ ++ ", -" . +..+..+ ", -" ++..+..++ ", -" +.+..+..+.+ ", -" +.+..+..+.+ ", -" +.+..+..+.+ ", -" +.........+ ", -" +.........+ ", -" +........+ ", -" +........+ ", -" +......+ ", -" +......+ ", -" +.....+ ", -" +.....+ ", -" +++++++ ", +" .+. ++ ", +" . +++..+++ ", +" +..+..+..+ ", +" ++..+..+..++ ", +" +.+..+..+..+.+ ", +" +.+..+..+..+.+ ", +" +............+ ", +" +............+ ", +" +...........+ ", +" +..........+ ", +" +.........+ ", +" +........+ ", +" +.......+ ", +" +.......+ ", +" +++++++++ ", " ", " ", " "}; diff --git a/src/pixmaps/cursor-node-m.xpm b/src/pixmaps/cursor-node-m.xpm index 272eeb884..08574bfb0 100644 --- a/src/pixmaps/cursor-node-m.xpm +++ b/src/pixmaps/cursor-node-m.xpm @@ -15,24 +15,24 @@ static char const *cursor_node_m_xpm[] = { " .++++. ", " .+++++. ", " .+++++. ", -" .++++++. ++++ ", +" .++++++. +++ ", " .+++++. +.+..+ ", -" .+++.. +..+..++ ", -" .+. +..+..+.+ ", -" . + +..+..+..+ ", -" +.++..+..+..+ ", -" +..+..+..+..+ ", -" +..+..+..+..+ ", -" +..+..+..+..+ ", -" +...........+ ", -" +..........+ ", -" +.........+ ", -" +........+ ", -" +.......+ ", -" +......+ ", -" +.....+ ", -" +.....+ ", -" +++++++ ", +" .+++.. +..+..++++ ", +" .+. +..+..+..+ ", +" . +..+..+..++ ", +" +++..+..+..+.+ ", +" +..+..+..+..+.+ ", +" +..+..+..+..+.+ ", +" +..+..+..+..+.+ ", +" +.............+ ", +" +............+ ", +" +...........+ ", +" +..........+ ", +" +.........+ ", +" +........+ ", +" +.......+ ", +" +.......+ ", +" +++++++++ ", " ", " ", " "}; diff --git a/src/pixmaps/cursor-select-d.xpm b/src/pixmaps/cursor-select-d.xpm index 53b8c0cdf..13acc0aad 100644 --- a/src/pixmaps/cursor-select-d.xpm +++ b/src/pixmaps/cursor-select-d.xpm @@ -2,7 +2,7 @@ static char const *cursor_select_d_xpm[] = { "32 32 3 1", " g None", -". g #FFFFFF", +". g #ffffff", "+ g #000000", "+ ", "++ ", @@ -17,22 +17,22 @@ static char const *cursor_select_d_xpm[] = { "+.........+ ", "+..........+ ", "+......++++ ", -"+...+..+ ", +"+...+++. ", "+..+ +..+ ", -"+.+ +..+ ", -"++ +..+ ++ ++ ", -" +..+ +..+..+ ", -" +..+ ++..+..++ ", -" +..+ +.+..+..+.+ ", -" ++ +..+..+..+..+ ", -" +..+..+..+..+ ", -" +...........+ ", -" +...........+ ", -" +.........+ ", -" +........+ ", -" +......+ ", -" +......+ ", -" +.....+ ", -" +.....+ ", -" +++++++ ", +"+.+ +..+ ++ ", +".. +..+ +++..+ ", +" +..+ +..+..+++ ", +" +..+ +++..+..+..+ ", +" +..+ +..+..+..+..++ ", +" +++ +..+..+..+..+.+ ", +" +..+..+..+..+.+ ", +" +.............+ ", +" +.............+ ", +" +............+ ", +" +..........+ ", +" +.........+ ", +" +.........+ ", +" +.........+ ", +" +.........+ ", +" +++++++++++ ", " "}; diff --git a/src/pixmaps/cursor-select-m.xpm b/src/pixmaps/cursor-select-m.xpm index beea4739a..48be49f77 100644 --- a/src/pixmaps/cursor-select-m.xpm +++ b/src/pixmaps/cursor-select-m.xpm @@ -2,7 +2,7 @@ static char const *cursor_select_m_xpm[] = { "32 32 3 1", " g None", -". g #FFFFFF", +". g #ffffff", "+ g #000000", "+ ", "++ ", @@ -17,22 +17,22 @@ static char const *cursor_select_m_xpm[] = { "+.........+ ", "+..........+ ", "+......++++ ", -"+...+..+ ++ ++ ", -"+..+ +..+ +..+..+ ", -"+.+ +..+ +..+..+ ", -"++ +..+ +..+..++ ", -" +..+ +..+..+.+ ", -" +..+ ++ +..+..+..+ ", -" +..+ +.++..+..+..+ ", -" ++ +..+..+..+..+ ", -" +..+..+..+..+ ", -" +..+..+..+..+ ", -" +...........+ ", -" +..........+ ", -" +.........+ ", -" +........+ ", -" +.......+ ", -" +......+ ", -" +.....+ ", -" +.....+ ", -" +++++++ "}; +"+...+..+ ++++ ", +"+..++..+ ++++..+ ", +"+.+ +..+ +..+..+++ ", +"++ +..+ +..+..+..+ ", +" +..+ +..+..+..++ ", +" +..+ +++..+..+..+.+ ", +" +..+ +..+..+..+..+.+ ", +" +++ +..+..+..+..+.+ ", +" +..+..+..+..+.+ ", +" +..+..+..+..+.+ ", +" +.............+ ", +" +.............+ ", +" +...........+ ", +" +..........+ ", +" +........+ ", +" +........+ ", +" +........+ ", +" +........+ ", +" ++++++++++ "}; diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 0c8834951..260136949 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -193,7 +193,7 @@ static char const preferences_skeleton[] = " <group id=\"exportarea\"/>\n" " <group id=\"defaultxdpi\"/>\n" " </group>\n" -" <group id=\"save_as\" default=\"\" append_extension=\"1\" enable_preview=\"1\" path=\"\"/>\n" +" <group id=\"save_as\" default=\"\" append_extension=\"1\" enable_preview=\"1\" path=\"\" use_current_dir=\"1\"/>\n" " <group id=\"save_copy\" default=\"\" append_extension=\"1\" enable_preview=\"1\" path=\"\"/>\n" " <group id=\"open\" enable_preview=\"1\" path=\"\"/>\n" " <group id=\"import\" enable_preview=\"1\" path=\"\"/>\n" @@ -343,6 +343,7 @@ static char const preferences_skeleton[] = " </group>\n" " <group id=\"workarounds\"\n" " colorsontop=\"0\"\n" +" floatallowed=\"0\"\n" " partialdynamic=\"0\"/>\n" " </group>\n" "\n" @@ -389,7 +390,12 @@ static char const preferences_skeleton[] = " </group>\n" "\n" " <group\n" -" id=\"iconpreview\">\n" +" id=\"iconpreview\"\n" +" autoRefresh=\"1\"\n" +" pack=\"1\"\n" +" selectionHold=\"1\"\n" +" showFrames=\"1\"\n" +" selectionOnly=\"0\">\n" " <group\n" " id=\"sizes\">\n" " <group\n" diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index de2860c8d..7538145d4 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -281,9 +281,9 @@ void sp_selection_delete_impl(GSList const *items, bool propagate = true, bool p sp_object_ref((SPObject *)i->data, NULL); } for (GSList const *i = items; i != NULL; i = i->next) { - SPItem *item = (SPItem *) i->data; - SP_OBJECT(item)->deleteObject(propagate, propagate_descendants); - sp_object_unref((SPObject *)item, NULL); + SPItem *item = reinterpret_cast<SPItem *>(i->data); + item->deleteObject(propagate, propagate_descendants); + sp_object_unref(item, NULL); } } @@ -438,7 +438,7 @@ void sp_edit_clear_all(SPDesktop *dt) GSList *items = sp_item_group_item_list(SP_GROUP(dt->currentLayer())); while (items) { - SP_OBJECT(items->data)->deleteObject(); + reinterpret_cast<SPObject*>(items->data)->deleteObject(); items = g_slist_remove(items, items->data); } @@ -449,7 +449,7 @@ void sp_edit_clear_all(SPDesktop *dt) GSList * get_all_items(GSList *list, SPObject *from, SPDesktop *desktop, bool onlyvisible, bool onlysensitive, GSList const *exclude) { - for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (SPObject *child = sp_object_first_child(from) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_ITEM(child) && !desktop->isLayer(SP_ITEM(child)) && (!onlysensitive || !SP_ITEM(child)->isLocked()) && @@ -555,7 +555,7 @@ void sp_edit_invert_in_all_layers(SPDesktop *desktop) } void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc) { - + p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); // Remember the position and parent of the topmost object. @@ -634,9 +634,9 @@ void sp_selection_group(SPDesktop *desktop) } GSList const *l = (GSList *) selection->reprList(); - + GSList *p = g_slist_copy((GSList *) l); - + selection->clear(); Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); @@ -817,7 +817,7 @@ sp_selection_raise(SPDesktop *desktop) // Iterate over all objects in the selection (starting from top). if (selected) { while (rev) { - SPObject *child = SP_OBJECT(rev->data); + SPObject *child = reinterpret_cast<SPObject*>(rev->data); // for each selected object, find the next sibling for (SPObject *newref = child->next; newref; newref = newref->next) { // if the sibling is an item AND overlaps our selection, @@ -914,7 +914,7 @@ sp_selection_lower(SPDesktop *desktop) // Iterate over all objects in the selection (starting from top). if (selected) { while (rev) { - SPObject *child = SP_OBJECT(rev->data); + SPObject *child = reinterpret_cast<SPObject*>(rev->data); // for each selected object, find the prev sibling for (SPObject *newref = prev_sibling(child); newref; newref = prev_sibling(newref)) { // if the sibling is an item AND overlaps our selection, @@ -1018,16 +1018,16 @@ SPCSSAttr * take_style_from_item(SPItem *item) { // write the complete cascaded style, context-free - SPCSSAttr *css = sp_css_attr_from_object(SP_OBJECT(item), SP_STYLE_FLAG_ALWAYS); + SPCSSAttr *css = sp_css_attr_from_object(item, SP_STYLE_FLAG_ALWAYS); if (css == NULL) return NULL; - if ((SP_IS_GROUP(item) && SP_OBJECT(item)->children) || - (SP_IS_TEXT(item) && SP_OBJECT(item)->children && SP_OBJECT(item)->children->next == NULL)) { + if ((SP_IS_GROUP(item) && item->children) || + (SP_IS_TEXT(item) && item->children && item->children->next == NULL)) { // if this is a text with exactly one tspan child, merge the style of that tspan as well // If this is a group, merge the style of its topmost (last) child with style for (SPObject *last_element = item->lastChild(); last_element != NULL; last_element = SP_OBJECT_PREV(last_element)) { - if (SP_OBJECT_STYLE(last_element) != NULL) { + if ( last_element->style ) { SPCSSAttr *temp = sp_css_attr_from_object(last_element, SP_STYLE_FLAG_IFSET); if (temp) { sp_repr_css_merge(css, temp); @@ -1335,7 +1335,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons // we're moving both a clone and its original or any ancestor in clone chain? bool transform_clone_with_original = selection_contains_original(item, selection); // ...both a text-on-path and its path? - bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))) )); + bool transform_textpath_with_path = (SP_IS_TEXT_TEXTPATH(item) && selection->includes( sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(item))) )); // ...both a flowtext and its frame? bool transform_flowtext_with_frame = (SP_IS_FLOWTEXT(item) && selection->includes( SP_FLOWTEXT(item)->get_frame(NULL))); // (only the first frame is checked so far) // ...both an offset and its source? @@ -1370,7 +1370,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons * Same for linked offset if we are also moving its source: do not move it. */ if (transform_textpath_with_path || transform_offset_with_source) { // Restore item->transform field from the repr, in case it was changed by seltrans. - sp_object_read_attr(SP_OBJECT(item), "transform"); + sp_object_read_attr(item, "transform"); } else if (transform_flowtext_with_frame) { // apply the inverse of the region's transform to the <use> so that the flow remains // the same (even though the output itself gets transformed) @@ -1388,7 +1388,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons // transform and its move compensation are both cancelled out. // restore item->transform field from the repr, in case it was changed by seltrans - sp_object_read_attr(SP_OBJECT(item), "transform"); + sp_object_read_attr(item, "transform"); // calculate the matrix we need to apply to the clone to cancel its induced transform from its original Geom::Matrix parent2dt = sp_item_i2d_affine(SP_ITEM(SP_OBJECT_PARENT(item))); @@ -1430,7 +1430,7 @@ void sp_selection_apply_affine(Inkscape::Selection *selection, Geom::Matrix cons // center by the same matrix (only necessary for non-translations) if (set_i2d && item->isCenterSet() && !(affine.isTranslation() || affine.isIdentity())) { item->setCenter(old_center * affine); - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); } } } @@ -2092,7 +2092,7 @@ sp_selection_relink(SPDesktop *desktop) continue; SP_OBJECT_REPR(item)->setAttribute("xlink:href", newref); - SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); relinked = true; } @@ -2130,10 +2130,10 @@ sp_selection_unlink(SPDesktop *desktop) SPItem *item = (SPItem *) items->data; if (SP_IS_TEXT(item)) { - SPObject *tspan = sp_tref_convert_to_tspan(SP_OBJECT(item)); + SPObject *tspan = sp_tref_convert_to_tspan(item); if (tspan) { - SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } // Set unlink to true, and fall into the next if which @@ -2151,7 +2151,7 @@ sp_selection_unlink(SPDesktop *desktop) if (SP_IS_USE(item)) { unlink = sp_use_unlink(SP_USE(item)); } else /*if (SP_IS_TREF(use))*/ { - unlink = SP_ITEM(sp_tref_convert_to_tspan(SP_OBJECT(item))); + unlink = SP_ITEM(sp_tref_convert_to_tspan(item)); } unlinked = true; @@ -2196,7 +2196,7 @@ sp_select_clone_original(SPDesktop *desktop) } else if (SP_IS_OFFSET(item) && SP_OFFSET(item)->sourceHref) { original = sp_offset_get_source(SP_OFFSET(item)); } else if (SP_IS_TEXT_TEXTPATH(item)) { - original = sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(SP_OBJECT(item)))); + original = sp_textpath_get_path_item(SP_TEXTPATH(sp_object_first_child(item))); } else if (SP_IS_FLOWTEXT(item)) { original = SP_FLOWTEXT(item)->get_frame(NULL); // first frame only } else { // it's an object that we don't know what to do with @@ -2298,7 +2298,7 @@ void sp_selection_to_marker(SPDesktop *desktop, bool apply) if (apply) { // delete objects so that their clones don't get alerted; this object will be restored shortly for (GSList *i = items; i != NULL; i = i->next) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); item->deleteObject(false); } } @@ -2337,7 +2337,7 @@ static void sp_selection_to_guides_recursive(SPItem *item, bool deleteitem, bool sp_item_convert_item_to_guides(item); if (deleteitem) { - SP_OBJECT(item)->deleteObject(true); + item->deleteObject(true); } } } @@ -2422,7 +2422,7 @@ sp_selection_tile(SPDesktop *desktop, bool apply) if (apply) { // delete objects so that their clones don't get alerted; this object will be restored shortly for (GSList *i = items; i != NULL; i = i->next) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); item->deleteObject(false); } } @@ -2499,12 +2499,12 @@ sp_selection_untile(SPDesktop *desktop) SPItem *item = (SPItem *) items->data; - SPStyle *style = SP_OBJECT_STYLE(item); + SPStyle *style = item->style; if (!style || !style->fill.isPaintserver()) continue; - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); + SPPaintServer *server = item->style->getFillPaintServer(); if (!SP_IS_PATTERN(server)) continue; @@ -2516,7 +2516,7 @@ sp_selection_untile(SPDesktop *desktop) Geom::Matrix pat_transform = pattern_patternTransform(SP_PATTERN(server)); pat_transform *= item->transform; - for (SPObject *child = sp_object_first_child(SP_OBJECT(pattern)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (SPObject *child = sp_object_first_child(pattern) ; child != NULL; child = child->next ) { Inkscape::XML::Node *copy = SP_OBJECT_REPR(child)->duplicate(xml_doc); SPItem *i = SP_ITEM(desktop->currentLayer()->appendChildRepr(copy)); @@ -2856,7 +2856,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la GSList *items = g_slist_copy((GSList *) selection->itemList()); items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position); - + // See lp bug #542004 selection->clear(); @@ -2865,7 +2865,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la GSList *apply_to_items = NULL; GSList *items_to_delete = NULL; GSList *items_to_select = NULL; - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool topmost = prefs->getBool("/options/maskobject/topmost", true); bool remove_original = prefs->getBool("/options/maskobject/remove", true); @@ -2874,13 +2874,13 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la if (apply_to_layer) { // all selected items are used for mask, which is applied to a layer apply_to_items = g_slist_prepend(apply_to_items, desktop->currentLayer()); - + for (GSList *i = items; i != NULL; i = i->next) { Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc); mask_items = g_slist_prepend(mask_items, dup); - SPObject *item = SP_OBJECT(i->data); - if (remove_original) { + SPObject *item = reinterpret_cast<SPObject*>(i->data); + if (remove_original) { items_to_delete = g_slist_prepend(items_to_delete, item); } else { @@ -2894,7 +2894,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la mask_items = g_slist_prepend(mask_items, dup); if (remove_original) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); items_to_delete = g_slist_prepend(items_to_delete, item); } @@ -2913,7 +2913,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la mask_items = g_slist_prepend(mask_items, dup); if (remove_original) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); items_to_delete = g_slist_prepend(items_to_delete, item); } } @@ -2938,7 +2938,7 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la reprs_to_group = g_slist_reverse(reprs_to_group); sp_selection_group_impl(reprs_to_group, group, xml_doc, doc); - + reprs_to_group = NULL; // apply clip/mask only to newly created group @@ -3008,14 +3008,14 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la g_slist_free(apply_to_items); for (GSList *i = items_to_delete; NULL != i; i = i->next) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = reinterpret_cast<SPObject*>(i->data); item->deleteObject(false); items_to_select = g_slist_remove(items_to_select, item); } g_slist_free(items_to_delete); - + items_to_select = g_slist_reverse(items_to_select); - + selection->addList(items_to_select); g_slist_free(items_to_select); @@ -3049,11 +3049,11 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { GSList *items = g_slist_copy((GSList *) selection->itemList()); selection->clear(); - + GSList *items_to_ungroup = NULL; GSList *items_to_select = g_slist_copy(items); - items_to_select = g_slist_reverse(items_to_select); - + items_to_select = g_slist_reverse(items_to_select); + // SPObject* refers to a group containing the clipped path or mask itself, // whereas SPItem* refers to the item being clipped or masked @@ -3139,7 +3139,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } g_slist_free(items_to_ungroup); - + // rebuild selection items_to_select = g_slist_reverse(items_to_select); selection->addList(items_to_select); diff --git a/src/selection.cpp b/src/selection.cpp index 96c66e0c5..3f333e4e2 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -406,6 +406,8 @@ Geom::OptRect Selection::boundsInDocument(SPItem::BBoxType type) const { } /** Extract the position of the center from the first selected object */ +// If we have a selection of multiple items, then the center of the first item +// will be returned; this is also the case in SelTrans::centerRequest() boost::optional<Geom::Point> Selection::center() const { GSList *items = (GSList *) const_cast<Selection *>(this)->itemList(); Geom::Point center; diff --git a/src/selection.h b/src/selection.h index a79892eb5..5e3b4bba2 100644 --- a/src/selection.h +++ b/src/selection.h @@ -250,14 +250,16 @@ public: /** * @brief Returns the bounding rectangle of the selection * - * \todo how is this different from bounds()? + * Gives the coordinates in internal format, does not match onscreen guides. + * (0,0 is the upper left corner, not the lower left corner) */ NRRect *boundsInDocument(NRRect *dest, SPItem::BBoxType type = SPItem::APPROXIMATE_BBOX) const; /** * @brief Returns the bounding rectangle of the selection * - * \todo how is this different from bounds()? + * Gives the coordinates in internal format, does not match onscreen guides. + * (0,0 is the upper left corner, not the lower left corner) */ Geom::OptRect boundsInDocument(SPItem::BBoxType type = SPItem::APPROXIMATE_BBOX) const; diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 64c41ea23..4b1ab1e76 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -883,7 +883,11 @@ gboolean Inkscape::SelTrans::handleRequest(SPKnot *knot, Geom::Point *position, if (handle.request(this, handle, *position, state)) { sp_knot_set_position(knot, *position, state); SP_CTRL(_grip)->moveto(*position); - SP_CTRL(_norm)->moveto(_origin); + if (&handle == &handle_center) { + SP_CTRL(_norm)->moveto(*position); + } else { + SP_CTRL(_norm)->moveto(_origin); + } } return TRUE; @@ -1209,7 +1213,7 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P SnapManager &m = _desktop->namedview->snap_manager; m.setup(_desktop, false, _items_const); - Inkscape::Snapper::ConstraintLine const constraint(component_vectors[dim_b]); + Inkscape::Snapper::SnapConstraint const constraint(component_vectors[dim_b]); // When skewing, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapSkew" for details Geom::Point const s(skew[dim_a], scale[dim_a]); Inkscape::SnappedPoint sn = m.constrainedSnapSkew(_snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b)); @@ -1276,7 +1280,10 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state) if (fabs(h2) < 1e-15) return FALSE; Geom::Point q2 = d2 / h2; // normalized new vector to handle - double radians; + Geom::Rotate r1(q1); + Geom::Rotate r2(q2); + + double radians = atan2(Geom::dot(Geom::rot90(d1), d2), Geom::dot(d1, d2));; if (state & GDK_CONTROL_MASK) { // Snap to defined angle increments double cos_t = Geom::dot(q1, q2); @@ -1285,15 +1292,25 @@ gboolean Inkscape::SelTrans::rotateRequest(Geom::Point &pt, guint state) if (snaps) { radians = ( M_PI / snaps ) * floor( radians * snaps / M_PI + .5 ); } - q1 = Geom::Point(1, 0); - q2 = Geom::Point(cos(radians), sin(radians)); + r1 = Geom::Rotate(0); //q1 = Geom::Point(1, 0); + r2 = Geom::Rotate(radians); //q2 = Geom::Point(cos(radians), sin(radians)); } else { - radians = atan2(Geom::dot(Geom::rot90(d1), d2), - Geom::dot(d1, d2)); + SnapManager &m = _desktop->namedview->snap_manager; + m.setup(_desktop, false, _items_const); + // When rotating, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapRotate" for details + Inkscape::SnappedPoint sn = m.constrainedSnapRotate(_snap_points, _point, radians, _origin); + + if (sn.getSnapped()) { + _desktop->snapindicator->set_new_snaptarget(sn); + // We snapped something, so change the rotation to reflect it + radians = sn.getTransformation()[0]; + r1 = Geom::Rotate(0); + r2 = Geom::Rotate(radians); + } else { + _desktop->snapindicator->remove_snaptarget(); + } } - Geom::Rotate const r1(q1); - Geom::Rotate const r2(q2); // Calculate the relative affine _relative_affine = r2 * r1.inverse(); @@ -1316,7 +1333,18 @@ gboolean Inkscape::SelTrans::centerRequest(Geom::Point &pt, guint state) { SnapManager &m = _desktop->namedview->snap_manager; m.setup(_desktop); - m.freeSnapReturnByRef(pt, Inkscape::SNAPSOURCE_OTHER_HANDLE); + + // Center is being dragged for the first item in the selection only + // Find out which item is first ... + GSList *items = (GSList *) const_cast<Selection *>(_selection)->itemList(); + SPItem *first = NULL; + if (items) { + first = reinterpret_cast<SPItem*>(g_slist_last(items)->data); // from the first item in selection + } + // ... and store that item because later on we need to make sure that + // this transformation center won't snap to itself + m.setRotationCenterSource(first); + m.freeSnapReturnByRef(pt, Inkscape::SNAPSOURCE_ROTATION_CENTER); if (state & GDK_CONTROL_MASK) { if ( fabs(_point[Geom::X] - pt[Geom::X]) > fabs(_point[Geom::Y] - pt[Geom::Y]) ) { @@ -1326,25 +1354,6 @@ gboolean Inkscape::SelTrans::centerRequest(Geom::Point &pt, guint state) } } - if ( !(state & GDK_SHIFT_MASK) && _bbox ) { - // screen pixels to snap center to bbox -#define SNAP_DIST 5 - // FIXME: take from prefs - double snap_dist = SNAP_DIST / _desktop->current_zoom(); - - for (int i = 0; i < 2; i++) { - if (fabs(pt[i] - _bbox->min()[i]) < snap_dist) { - pt[i] = _bbox->min()[i]; - } - if (fabs(pt[i] - _bbox->midpoint()[i]) < snap_dist) { - pt[i] = _bbox->midpoint()[i]; - } - if (fabs(pt[i] - _bbox->max()[i]) < snap_dist) { - pt[i] = _bbox->max()[i]; - } - } - } - // status text GString *xs = SP_PX_TO_METRIC_STRING(pt[Geom::X], _desktop->namedview->getDefaultMetric()); GString *ys = SP_PX_TO_METRIC_STRING(pt[Geom::Y], _desktop->namedview->getDefaultMetric()); @@ -1460,14 +1469,14 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) // the constraint-line once. The constraint lines are parallel, but might not be colinear. // Therefore we will have to set the point through which the constraint-line runs // individually for each point to be snapped; this will be handled however by _snapTransformed() - s.push_back(m.constrainedSnapTranslation(_bbox_points_for_translating, + s.push_back(m.constrainedSnapTranslate(_bbox_points_for_translating, _point, - Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + Inkscape::Snapper::SnapConstraint(component_vectors[dim]), dxy)); - s.push_back(m.constrainedSnapTranslation(_snap_points, + s.push_back(m.constrainedSnapTranslate(_snap_points, _point, - Inkscape::Snapper::ConstraintLine(component_vectors[dim]), + Inkscape::Snapper::SnapConstraint(component_vectors[dim]), dxy)); } else { // !control @@ -1477,8 +1486,8 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) g_get_current_time(&starttime); */ /* Snap to things with no constraint */ - s.push_back(m.freeSnapTranslation(_bbox_points_for_translating, _point, dxy)); - s.push_back(m.freeSnapTranslation(_snap_points, _point, dxy)); + s.push_back(m.freeSnapTranslate(_bbox_points_for_translating, _point, dxy)); + s.push_back(m.freeSnapTranslate(_snap_points, _point, dxy)); /*g_get_current_time(&endtime); double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0; @@ -1504,7 +1513,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) if (control) { // If we didn't snap, then we should still constrain horizontally or vertically // (When we did snap, then this constraint has already been enforced by - // calling constrainedSnapTranslation() above) + // calling constrainedSnapTranslate() above) if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) { dxy[Geom::Y] = 0; } else { diff --git a/src/snap-enums.h b/src/snap-enums.h index 4e6854054..aa5db9328 100644 --- a/src/snap-enums.h +++ b/src/snap-enums.h @@ -66,10 +66,10 @@ enum SnapSourceType { SNAPSOURCE_CONVEX_HULL_CORNER, SNAPSOURCE_ELLIPSE_QUADRANT_POINT, SNAPSOURCE_NODE_HANDLE, // eg. nodes in the path editor, handles of stars or rectangles, etc. (tied to a stroke) + SNAPSOURCE_OBJECT_MIDPOINT, // midpoint of rectangles, polygon, etc. //------------------------------------------------------------------- // Other points (e.g. guides, gradient knots) will snap to both bounding boxes and nodes SNAPSOURCE_OTHER_CATEGORY = 1024, // will be used as a flag and must therefore be a power of two - SNAPSOURCE_OBJECT_MIDPOINT, SNAPSOURCE_ROTATION_CENTER, SNAPSOURCE_CENTER, // of ellipse SNAPSOURCE_GUIDE, diff --git a/src/snap.cpp b/src/snap.cpp index 718700103..40dd4ca31 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -174,7 +174,6 @@ void SnapManager::freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Geom::OptRect const &bbox_to_snap) const { - //TODO: SnapCandidatePoint and point_type are somewhat redundant; can't we get rid of the point_type parameter? Inkscape::SnappedPoint const s = freeSnap(Inkscape::SnapCandidatePoint(p, source_type), bbox_to_snap); s.getPoint(p); } @@ -335,7 +334,7 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point c void SnapManager::constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap) const { Inkscape::SnappedPoint const s = constrainedSnap(Inkscape::SnapCandidatePoint(p, source_type, 0), constraint, bbox_to_snap); @@ -359,13 +358,13 @@ void SnapManager::constrainedSnapReturnByRef(Geom::Point &p, */ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint const &p, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap) const { // First project the mouse pointer onto the constraint Geom::Point pp = constraint.projection(p.getPoint()); - Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::L2(pp - p.getPoint()), 0, false, true, false); + Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, NR_HUGE, 0, false, true, false); if (!someSnapperMightSnap()) { // Always return point on constraint @@ -390,6 +389,68 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint return no_snap; } +/* See the documentation for constrainedSnap() directly above for more details. + * The difference is that multipleConstrainedSnaps() will take a list of constraints instead of a single one, + * and will try to snap the SnapCandidatePoint to all of the provided constraints and see which one fits best + * \param p Source point to be snapped + * \param constraints List of directions or lines along which snapping must occur + * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation + */ + + +Inkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandidatePoint const &p, + std::vector<Inkscape::Snapper::SnapConstraint> const &constraints, + Geom::OptRect const &bbox_to_snap) const +{ + + Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(p.getPoint(), p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, NR_HUGE, 0, false, true, false); + if (constraints.size() == 0) { + return no_snap; + } + + SnappedConstraints sc; + SnapperList const snappers = getSnappers(); + std::vector<Geom::Point> projections; + bool snapping_is_futile = !someSnapperMightSnap(); + + // Iterate over the constraints + for (std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); c != constraints.end(); c++) { + // Project the mouse pointer onto the constraint; In case we don't snap then we will + // return the projection onto the constraint, such that the constraint is always enforced + Geom::Point pp = (*c).projection(p.getPoint()); + projections.push_back(pp); + // Try to snap to the constraint + if (!snapping_is_futile) { + for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { + (*i)->constrainedSnap(sc, p, bbox_to_snap, *c, &_items_to_ignore); + } + } + } + + Inkscape::SnappedPoint result = findBestSnap(p, sc, true); + + if (result.getSnapped()) { + // only change the snap indicator if we really snapped to something + if (_snapindicator) { + _desktop->snapindicator->set_new_snaptarget(result); + } + return result; + } + + // So we didn't snap, but we still need to return a point on one of the constraints + // Find out which of the constraints yielded the closest projection of point p + no_snap.setPoint(projections.front()); + for (std::vector<Geom::Point>::iterator pp = projections.begin(); pp != projections.end(); pp++) { + if (pp != projections.begin()) { + if (Geom::L2(*pp - p.getPoint()) < Geom::L2(no_snap.getPoint() - p.getPoint())) { + no_snap.setPoint(*pp); + } + } + } + + return no_snap; +} + /** * \brief Try to snap a point of a guide to another guide or to a node * @@ -408,7 +469,7 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, return; } - if (!(object.GuidesMightSnap() || snapprefs.getSnapToGuides())) { + if (!(object.ThisSnapperMightSnap() || snapprefs.getSnapToGuides())) { return; } @@ -419,7 +480,7 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, // Snap to nodes SnappedConstraints sc; - if (object.GuidesMightSnap()) { + if (object.ThisSnapperMightSnap()) { object.guideFreeSnap(sc, p, guide_normal); } @@ -430,8 +491,7 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, (*i)->freeSnap(sc, candidate, Geom::OptRect(), NULL, NULL); } - // Snap to intersections of curves, but not to the curves themselves! (see _snapTranslatingGuideToNodes in object-snapper.cpp) - Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false, true); + Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false, false); s.getPoint(p); } @@ -464,7 +524,7 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) // Snap to nodes or paths SnappedConstraints sc; - Inkscape::Snapper::ConstraintLine cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); + Inkscape::Snapper::SnapConstraint cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); if (object.ThisSnapperMightSnap()) { object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL); } @@ -509,7 +569,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer, bool constrained, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Transformation transformation_type, Geom::Point const &transformation, Geom::Point const &origin, @@ -559,8 +619,17 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point g_assert(best_snapped_point.getAtIntersection() == false); - std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin(); + // Warnings for the devs + if (constrained && transformation_type == SCALE && !uniform) { + g_warning("Non-uniform constrained scaling is not supported!"); + } + if (!constrained && transformation_type == ROTATE) { + // We do not yet allow for simultaneous rotation and scaling + g_warning("Unconstrained rotation is not supported!"); + } + + std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin(); // std::cout << std::endl; bool first_free_snap = true; @@ -568,27 +637,38 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( /* Snap it */ Inkscape::SnappedPoint snapped_point; - Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - Geom::Point const b = ((*i).getPoint() - origin); // vector to original point + Inkscape::Snapper::SnapConstraint dedicated_constraint = constraint; + Geom::Point const b = ((*i).getPoint() - origin); // vector to original point (not the transformed point! required for rotations!) if (constrained) { - if ((transformation_type == SCALE || transformation_type == STRETCH) && uniform) { + if (((transformation_type == SCALE || transformation_type == STRETCH) && uniform)) { // When uniformly scaling, each point will have its own unique constraint line, // running from the scaling origin to the original untransformed point. We will // calculate that line here - dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, b); + dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b); + } else if (transformation_type == ROTATE) { + Geom::Coord r = Geom::L2(b); // the radius of the circular constraint + if (r < 1e-9) { // points too close to the rotation center will not move. Don't try to snap these + // as they will always yield a perfect snap result if they're already snapped beforehand (e.g. + // when the transformation center has been snapped to a grid intersection in the selector tool) + continue; // skip this SnapCandidate and continue with the next one + // PS1: Apparently we don't have to do this for skewing, but why? + // PS2: We cannot easily filter these points upstream, e.g. in the grab() method (seltrans.cpp) + // because the rotation center will change when pressing shift, and grab() won't be recalled. + // Filtering could be done in handleRequest() (again in seltrans.cpp), by iterating through + // the snap candidates. But hey, we're iterating here anyway. + } + dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, b, r); } else if (transformation_type == STRETCH) { // when non-uniform stretching { - dedicated_constraint = Inkscape::Snapper::ConstraintLine((*i).getPoint(), component_vectors[dim]); - } else if (transformation_type == TRANSLATION) { + dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), component_vectors[dim]); + } else if (transformation_type == TRANSLATE) { // When doing a constrained translation, all points will move in the same direction, i.e. // either horizontally or vertically. The lines along which they move are therefore all - // parallel, but might not be colinear. Therefore we will have to set the point through - // which the constraint-line runs here, for each point individually. - dedicated_constraint.setPoint((*i).getPoint()); + // parallel, but might not be colinear. Therefore we will have to specify the point through + // which the constraint-line runs here, for each point individually. (we could also have done this + // earlier on, e.g. in seltrans.cpp but we're being lazy there and don't want to add an iteration loop) + dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), constraint.getDirection()); } // else: leave the original constraint, e.g. for skewing - if (transformation_type == SCALE && !uniform) { - g_warning("Non-uniform constrained scaling is not supported!"); - } snapped_point = constrainedSnap(*j, dedicated_constraint, bbox); } else { bool const c1 = fabs(b[Geom::X]) < 1e-6; @@ -597,7 +677,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // When scaling, a point aligned either horizontally or vertically with the origin can only // move in that specific direction; therefore it should only snap in that direction, otherwise // we will get snapped points with an invalid transformation - dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]); + dedicated_constraint = Inkscape::Snapper::SnapConstraint(origin, component_vectors[c1]); snapped_point = constrainedSnap(*j, dedicated_constraint, bbox); } else { // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping @@ -623,11 +703,11 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( /* We snapped. Find the transformation that describes where the snapped point has ** ended up, and also the metric for this transformation. */ - Geom::Point const a = (snapped_point.getPoint() - origin); // vector to snapped point + Geom::Point const a = snapped_point.getPoint() - origin; // vector to snapped point //Geom::Point const b = (*i - origin); // vector to original point switch (transformation_type) { - case TRANSLATION: + case TRANSLATE: result = snapped_point.getPoint() - (*i).getPoint(); /* Consider the case in which a box is almost aligned with a grid in both * horizontal and vertical directions. The distance to the intersection of @@ -687,12 +767,20 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( snapped_point.setSecondSnapDistance(Geom::infinity()); break; case SKEW: - result[0] = (snapped_point.getPoint()[dim] - ((*i).getPoint())[dim]) / (((*i).getPoint())[1 - dim] - origin[1 - dim]); // skew factor + result[0] = (snapped_point.getPoint()[dim] - ((*i).getPoint())[dim]) / b[1 - dim]; // skew factor result[1] = transformation[1]; // scale factor // Store the metric for this transformation as a virtual distance snapped_point.setSnapDistance(std::abs(result[0] - transformation[0])); snapped_point.setSecondSnapDistance(Geom::infinity()); break; + case ROTATE: + // a is vector to snapped point; b is vector to original point; now lets calculate angle between a and b + result[0] = atan2(Geom::dot(Geom::rot90(b), a), Geom::dot(b, a)); + result[1] = result[1]; // how else should we store an angle in a point ;-) + // Store the metric for this transformation as a virtual distance (we're storing an angle) + snapped_point.setSnapDistance(std::abs(result[0] - transformation[0])); + snapped_point.setSecondSnapDistance(NR_HUGE); + break; default: g_assert_not_reached(); } @@ -738,22 +826,23 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, +Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Point const &tr) const { if (p.size() == 1) { - Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + Geom::Point pt = _transformPoint(p.at(0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + + + return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); } /** * \brief Apply a translation to a set of points and try to snap along a constraint * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param constraint The direction or line along which snapping must occur. @@ -761,24 +850,23 @@ Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::Sn * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, +Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &tr) const { if (p.size() == 1) { - Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + Geom::Point pt = _transformPoint(p.at(0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(p, pointer, true, constraint, TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + return _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false); } /** * \brief Apply a scaling to a set of points and try to snap freely in 2 degrees-of-freedom * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param s Proposed scaling; the final scaling can only be calculated after snapping has occurred @@ -803,7 +891,6 @@ Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCand /** * \brief Apply a scaling to a set of points and snap such that the aspect ratio of the selection is preserved * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param s Proposed scaling; the final scaling can only be calculated after snapping has occurred @@ -828,7 +915,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::S /** * \brief Apply a stretch to a set of points and snap such that the direction of the stretch is preserved * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param s Proposed stretch; the final stretch can only be calculated after snapping has occurred @@ -856,7 +942,6 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape: /** * \brief Apply a skew to a set of points and snap such that the direction of the skew is preserved * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param constraint The direction or line along which snapping must occur. @@ -868,7 +953,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape: Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &s, Geom::Point const &o, Geom::Dim2 d) const @@ -893,6 +978,36 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::Sn } /** + * \brief Apply a rotation to a set of points and snap, without scaling + * + * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. + * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). + * \param angle Proposed rotation (in radians); the final rotation can only be calculated after snapping has occurred + * \param o Origin of the rotation + * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. + */ + +Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p, + Geom::Point const &pointer, + Geom::Coord const &angle, + Geom::Point const &o) const +{ + // Snapping the nodes of the bounding box of a selection that is being transformed, will only work if + // the transformation of the bounding box is equal to the transformation of the individual nodes. This is + // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew, + // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping + // of bounding boxes is not allowed here. + + if (p.size() == 1) { + Geom::Point pt = _transformPoint(p.at(0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); + } + + return _snapTransformed(p, pointer, true, Geom::Point(0,0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false); + +} + +/** * \brief Given a set of possible snap targets, find the best target (which is not necessarily * also the nearest target), and show the snap indicator if requested * @@ -1038,6 +1153,7 @@ void SnapManager::setup(SPDesktop const *desktop, _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; + _rotation_center_source_item = NULL; } /** @@ -1068,6 +1184,7 @@ void SnapManager::setup(SPDesktop const *desktop, _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; + _rotation_center_source_item = NULL; } /// Setup, taking the list of items to ignore from the desktop's selection. @@ -1080,6 +1197,7 @@ void SnapManager::setupIgnoreSelection(SPDesktop const *desktop, _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; + _rotation_center_source_item = NULL; _items_to_ignore.clear(); Inkscape::Selection *sel = _desktop->selection; @@ -1116,7 +1234,7 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p, /* Work out the transformed version of this point */ Geom::Point transformed; switch (transformation_type) { - case TRANSLATION: + case TRANSLATE: transformed = p.getPoint() + transformation; break; case SCALE: @@ -1141,6 +1259,10 @@ Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p, // Apply that scale factor here transformed[1-dim] = (p.getPoint() - origin)[1 - dim] * transformation[1] + origin[1 - dim]; break; + case ROTATE: + // for rotations: transformation[0] stores the angle in radians + transformed = (p.getPoint() - origin) * Geom::Rotate(transformation[0]) + origin; + break; default: g_assert_not_reached(); } @@ -1161,8 +1283,9 @@ void SnapManager::_displaySnapsource(Inkscape::SnapCandidatePoint const &p) cons if (prefs->getBool("/options/snapclosestonly/value")) { bool p_is_a_node = p.getSourceType() & Inkscape::SNAPSOURCE_NODE_CATEGORY; bool p_is_a_bbox = p.getSourceType() & Inkscape::SNAPSOURCE_BBOX_CATEGORY; + bool p_is_other = p.getSourceType() & Inkscape::SNAPSOURCE_OTHER_CATEGORY; - if (snapprefs.getSnapEnabledGlobally() && ((p_is_a_node && snapprefs.getSnapModeNode()) || (p_is_a_bbox && snapprefs.getSnapModeBBox()))) { + if (snapprefs.getSnapEnabledGlobally() && (p_is_other || (p_is_a_node && snapprefs.getSnapModeNode()) || (p_is_a_bbox && snapprefs.getSnapModeBBox()))) { _desktop->snapindicator->set_new_snapsource(p); } else { _desktop->snapindicator->remove_snapsource(); diff --git a/src/snap.h b/src/snap.h index 8a5688bea..c85c51963 100644 --- a/src/snap.h +++ b/src/snap.h @@ -67,10 +67,11 @@ class SnapManager { public: enum Transformation { - TRANSLATION, + TRANSLATE, SCALE, STRETCH, - SKEW + SKEW, + ROTATE }; SnapManager(SPNamedView const *v); @@ -91,11 +92,19 @@ public: std::vector<SPItem const *> &items_to_ignore, std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL, SPGuide *guide_to_ignore = NULL); + void setupIgnoreSelection(SPDesktop const *desktop, bool snapindicator = true, std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL, SPGuide *guide_to_ignore = NULL); + // If we're dragging a rotation center, then setRotationCenterSource() stores the parent item + // of this rotation center; this reference is used to make sure that we do not snap a rotation + // center to itself + // NOTE: Must be called after calling setup(), not before! + void setRotationCenterSource(SPItem *item) {_rotation_center_source_item = item;} + SPItem* getRotationCenterSource() {return _rotation_center_source_item;} + // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a // point if snapping has occurred (by overwriting p); otherwise p is untouched void freeSnapReturnByRef(Geom::Point &p, @@ -113,23 +122,27 @@ public: // point, by overwriting p, if snapping has occurred; otherwise p is untouched void constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapCandidatePoint const &p, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; + Inkscape::SnappedPoint multipleConstrainedSnaps(Inkscape::SnapCandidatePoint const &p, + std::vector<Inkscape::Snapper::SnapConstraint> const &constraints, + Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; + void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const; void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const; - Inkscape::SnappedPoint freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, + Inkscape::SnappedPoint freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Point const &tr) const; - Inkscape::SnappedPoint constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, + Inkscape::SnappedPoint constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &tr) const; Inkscape::SnappedPoint freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p, @@ -151,11 +164,16 @@ public: Inkscape::SnappedPoint constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor Geom::Point const &o, Geom::Dim2 d) const; + Inkscape::SnappedPoint constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p, + Geom::Point const &pointer, + Geom::Coord const &angle, + Geom::Point const &o) const; + Inkscape::GuideSnapper guide; ///< guide snapper Inkscape::ObjectSnapper object; ///< snapper to other objects Inkscape::SnapPreferences snapprefs; @@ -177,18 +195,16 @@ protected: private: std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method + SPItem *_rotation_center_source_item; // to avoid snapping a rotation center to itself SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged SPDesktop const *_desktop; bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored - //TODO: Make _unselected_nodes type safe; in the line above int is used for Inkscape::SnapTargetType, but if I remember - //correctly then in other cases the int is being used for Inkscape::SnapSourceType, or for both. How to make - //this type safe? Inkscape::SnappedPoint _snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer, bool constrained, - Inkscape::Snapper::ConstraintLine const &constraint, + Inkscape::Snapper::SnapConstraint const &constraint, Transformation transformation_type, Geom::Point const &transformation, Geom::Point const &origin, diff --git a/src/snapped-point.h b/src/snapped-point.h index d0bcd324d..9c753acd0 100644 --- a/src/snapped-point.h +++ b/src/snapped-point.h @@ -53,7 +53,9 @@ public: * has occurred; A check should be implemented in the calling code * to check for snapping. Use this method only when really needed, e.g. * when the calling code is trying to snap multiple points and must - * determine itself which point is most appropriate + * determine itself which point is most appropriate, or when doing a + * constrainedSnap that also enforces projection onto the constraint (in + * which case you need the new point anyway, even if we didn't snap) */ Geom::Point getPoint() const {return _point;} void setPoint(Geom::Point const &p) {_point = p;} diff --git a/src/snapper.h b/src/snapper.h index b5bb17de9..d8214db80 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -15,6 +15,7 @@ #include <map> #include <list> #include <boost/optional.hpp> +#include <glib.h> // for g_assert #include "snapped-point.h" #include "snapped-line.h" @@ -63,18 +64,29 @@ public: std::vector<SPItem const *> const */*it*/, std::vector<SnapCandidatePoint> */*unselected_nodes*/) const {}; - class ConstraintLine + // Class for storing the constraint for constrained snapping; can be + // - a line (infinite line with origin, running through _point pointing in _direction) + // - a direction (infinite line without origin, i.e. only a direction vector, stored in _direction) + // - a circle (_point denotes the center, _radius doesn't need an explanation, _direction contains + // the vector from the origin to the original untransformed point); + class SnapConstraint { + private: + enum SnapConstraintType {LINE, DIRECTION, CIRCLE}; + public: - ConstraintLine(Geom::Point const &d) : _has_point(false), _direction(d) {} - ConstraintLine(Geom::Point const &p, Geom::Point const &d) : _has_point(true), _point(p), _direction(d) {} - ConstraintLine(Geom::Line const &l) : _has_point(true), _point(l.origin()), _direction(l.versor()) {} + // Constructs a direction constraint, e.g. horizontal or vertical but without a specified point + SnapConstraint(Geom::Point const &d) : _direction(d), _type(DIRECTION) {} + // Constructs a linear constraint + SnapConstraint(Geom::Point const &p, Geom::Point const &d) : _point(p), _direction(d), _type(LINE) {} + SnapConstraint(Geom::Line const &l) : _point(l.origin()), _direction(l.versor()), _type(LINE) {} + // Constructs a circular constraint + SnapConstraint(Geom::Point const &p, Geom::Point const &d, Geom::Coord const &r) : _point(p), _direction(d), _radius(r), _type(CIRCLE) {} - bool hasPoint() const { - return _has_point; - } + bool hasPoint() const {return _type != DIRECTION;} Geom::Point getPoint() const { + g_assert(_type != DIRECTION); return _point; } @@ -82,28 +94,45 @@ public: return _direction; } - void setPoint(Geom::Point const &p) { - _point = p; - _has_point = true; + Geom::Coord getRadius() const { + g_assert(_type == CIRCLE); + return _radius; } - Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraintline - Geom::Point const p1_on_cl = _has_point ? _point : p; - Geom::Point const p2_on_cl = p1_on_cl + _direction; - return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); + bool isCircular() const { return _type == CIRCLE; } + bool isLinear() const { return _type == LINE; } + bool isDirection() const { return _type == DIRECTION; } + + Geom::Point projection(Geom::Point const &p) const { // returns the projection of p on this constraint + if (_type == CIRCLE) { + // project on to a circular constraint + Geom::Point v_orig = p - _point; + Geom::Coord l = Geom::L2(v_orig); + if (l > 0) { + return _point + _radius * v_orig/l; // Length of _direction is equal to the radius + } else { + // point to be projected is exactly at the center of the circle, so any point on the circle is a projection + return _point + Geom::Point(_radius, 0); + } + } else { + // project on to a linear constraint + Geom::Point const p1_on_cl = (_type == LINE) ? _point : p; + Geom::Point const p2_on_cl = p1_on_cl + _direction; + return Geom::projection(p, Geom::Line(p1_on_cl, p2_on_cl)); + } } private: - - bool _has_point; Geom::Point _point; Geom::Point _direction; + Geom::Coord _radius; + SnapConstraintType _type; }; virtual void constrainedSnap(SnappedConstraints &/*sc*/, Inkscape::SnapCandidatePoint const &/*p*/, Geom::OptRect const &/*bbox_to_snap*/, - ConstraintLine const &/*c*/, + SnapConstraint const &/*c*/, std::vector<SPItem const *> const */*it*/) const {}; protected: diff --git a/src/sp-gradient-fns.h b/src/sp-gradient-fns.h index 0fe10a5a1..9d1b84158 100644 --- a/src/sp-gradient-fns.h +++ b/src/sp-gradient-fns.h @@ -13,19 +13,6 @@ class SPGradient; -#define SP_GRADIENT_STATE_IS_SET(g) (SP_GRADIENT(g)->state != SP_GRADIENT_STATE_UNKNOWN) -#define SP_GRADIENT_IS_VECTOR(g) (SP_GRADIENT(g)->state == SP_GRADIENT_STATE_VECTOR) -#define SP_GRADIENT_IS_PRIVATE(g) (SP_GRADIENT(g)->state == SP_GRADIENT_STATE_PRIVATE) -#define SP_GRADIENT_HAS_STOPS(g) (SP_GRADIENT(g)->has_stops) -#define SP_GRADIENT_SPREAD(g) (SP_GRADIENT(g)->spread) -#define SP_GRADIENT_UNITS(g) (SP_GRADIENT(g)->units) - -/** Forces vector to be built, if not present (i.e. changed) */ -void sp_gradient_ensure_vector(SPGradient *gradient); - -void sp_gradient_set_units(SPGradient *gr, SPGradientUnits units); -void sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread); - SPGradientSpread sp_gradient_get_spread (SPGradient *gradient); /* Gradient repr methods */ diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 1b168c5e6..8a05992eb 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -1,5 +1,3 @@ -#define __SP_GRADIENT_C__ - /** \file * SPGradient, SPStop, SPLinearGradient, SPRadialGradient. */ @@ -34,6 +32,7 @@ #include "svg/css-ostringstream.h" #include "attributes.h" #include "document-private.h" +#include "sp-gradient.h" #include "gradient-chemistry.h" #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" @@ -42,6 +41,7 @@ #include "streq.h" #include "uri.h" #include "xml/repr.h" +#include "style.h" #define SP_MACROS_SILENT #include "macros.h" @@ -58,6 +58,29 @@ static Inkscape::XML::Node *sp_stop_write(SPObject *object, Inkscape::XML::Docum static SPObjectClass *stop_parent_class; +class SPGradientImpl +{ + friend class SPGradient; + + static void classInit(SPGradientClass *klass); + + static void init(SPGradient *gr); + static void build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); + static void release(SPObject *object); + static void modified(SPObject *object, guint flags); + static Inkscape::XML::Node *write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags); + + static void gradientRefModified(SPObject *href, guint flags, SPGradient *gradient); + static void gradientRefChanged(SPObject *old_ref, SPObject *ref, SPGradient *gr); + + static void childAdded(SPObject *object, + Inkscape::XML::Node *child, + Inkscape::XML::Node *ref); + static void removeChild(SPObject *object, Inkscape::XML::Node *child); + + static void setGradientAttr(SPObject *object, unsigned key, gchar const *value); +}; + /** * Registers SPStop class and returns its type. */ @@ -146,8 +169,7 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value) if (streq(p, "currentColor")) { stop->currentColor = true; } else { - guint32 const color = sp_svg_read_color(p, 0); - stop->specified_color.set( color ); + stop->specified_color = SPStop::readStopColor( p ); } } { @@ -165,8 +187,7 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value) stop->currentColor = true; } else { stop->currentColor = false; - guint32 const color = sp_svg_read_color(p, 0); - stop->specified_color.set( color ); + stop->specified_color = SPStop::readStopColor( p ); } } object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); @@ -206,11 +227,12 @@ sp_stop_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML: repr = xml_doc->createElement("svg:stop"); } - guint32 specifiedcolor = stop->specified_color.toRGBA32( 255 ); + Glib::ustring colorStr = stop->specified_color.toString(); gfloat opacity = stop->opacity; - if (((SPObjectClass *) stop_parent_class)->write) + if (((SPObjectClass *) stop_parent_class)->write) { (* ((SPObjectClass *) stop_parent_class)->write)(object, xml_doc, repr, flags); + } // Since we do a hackish style setting here (because SPStyle does not support stop-color and // stop-opacity), we must do it AFTER calling the parent write method; otherwise @@ -221,9 +243,7 @@ sp_stop_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML: if (stop->currentColor) { os << "currentColor"; } else { - gchar c[64]; - sp_svg_write_color(c, sizeof(c), specifiedcolor); - os << c; + os << colorStr; } os << ";stop-opacity:" << opacity; repr->setAttribute("style", os.str().c_str()); @@ -236,6 +256,43 @@ sp_stop_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML: return repr; } + +bool SPGradient::hasStops() const +{ + return has_stops; +} + +bool SPGradient::isUnitsSet() const +{ + return units_set; +} + +SPGradientUnits SPGradient::getUnits() const +{ + return units; +} + +bool SPGradient::isSpreadSet() const +{ + return spread_set; +} + +SPGradientSpread SPGradient::getSpread() const +{ + return spread; +} + +void SPGradient::setSwatch( bool swatch ) +{ + if ( swatch != isSwatch() ) { + this->swatch = swatch; // to make isSolid() work, this happens first + gchar const* paintVal = swatch ? (isSolid() ? "solid" : "gradient") : 0; + sp_object_setAttribute( this, "osb:paint", paintVal, 0 ); + + requestModified( SP_OBJECT_MODIFIED_FLAG ); + } +} + /** * Return stop's color as 32bit value. */ @@ -260,75 +317,27 @@ sp_stop_get_rgba32(SPStop const *const stop) } } -/** - * Return stop's color as SPColor. - */ -static SPColor -sp_stop_get_color(SPStop const *const stop) -{ - if (stop->currentColor) { - char const *str = sp_object_get_style_property(stop, "color", NULL); - guint32 const dfl = 0; - /* Default value: arbitrarily black. (SVG1.1 and CSS2 both say that the initial - * value depends on user agent, and don't give any further restrictions that I can - * see.) */ - guint32 color = dfl; - if (str) { - color = sp_svg_read_color(str, dfl); - } - SPColor ret( color ); - return ret; - } else { - return stop->specified_color; - } -} - /* * Gradient */ -static void sp_gradient_class_init(SPGradientClass *klass); -static void sp_gradient_init(SPGradient *gr); - -static void sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); -static void sp_gradient_release(SPObject *object); -static void sp_gradient_set(SPObject *object, unsigned key, gchar const *value); -static void sp_gradient_child_added(SPObject *object, - Inkscape::XML::Node *child, - Inkscape::XML::Node *ref); -static void sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child); -static void sp_gradient_modified(SPObject *object, guint flags); -static Inkscape::XML::Node *sp_gradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, - guint flags); - -static void gradient_ref_modified(SPObject *href, guint flags, SPGradient *gradient); - -static bool sp_gradient_invalidate_vector(SPGradient *gr); -static void sp_gradient_rebuild_vector(SPGradient *gr); - -static void gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gradient); - -SPGradientSpread sp_gradient_get_spread(SPGradient *gradient); -SPGradientUnits sp_gradient_get_units(SPGradient *gradient); - static SPPaintServerClass *gradient_parent_class; /** * Registers SPGradient class and returns its type. */ -GType -sp_gradient_get_type() +GType SPGradient::getType() { static GType gradient_type = 0; if (!gradient_type) { GTypeInfo gradient_info = { sizeof(SPGradientClass), NULL, NULL, - (GClassInitFunc) sp_gradient_class_init, + (GClassInitFunc) SPGradientImpl::classInit, NULL, NULL, sizeof(SPGradient), 16, - (GInstanceInitFunc) sp_gradient_init, + (GInstanceInitFunc) SPGradientImpl::init, NULL, /* value_table */ }; gradient_type = g_type_register_static(SP_TYPE_PAINT_SERVER, "SPGradient", @@ -340,30 +349,28 @@ sp_gradient_get_type() /** * SPGradient vtable initialization. */ -static void -sp_gradient_class_init(SPGradientClass *klass) +void SPGradientImpl::classInit(SPGradientClass *klass) { SPObjectClass *sp_object_class = (SPObjectClass *) klass; gradient_parent_class = (SPPaintServerClass *)g_type_class_ref(SP_TYPE_PAINT_SERVER); - sp_object_class->build = sp_gradient_build; - sp_object_class->release = sp_gradient_release; - sp_object_class->set = sp_gradient_set; - sp_object_class->child_added = sp_gradient_child_added; - sp_object_class->remove_child = sp_gradient_remove_child; - sp_object_class->modified = sp_gradient_modified; - sp_object_class->write = sp_gradient_write; + sp_object_class->build = SPGradientImpl::build; + sp_object_class->release = SPGradientImpl::release; + sp_object_class->set = SPGradientImpl::setGradientAttr; + sp_object_class->child_added = SPGradientImpl::childAdded; + sp_object_class->remove_child = SPGradientImpl::removeChild; + sp_object_class->modified = SPGradientImpl::modified; + sp_object_class->write = SPGradientImpl::write; } /** * Callback for SPGradient object initialization. */ -static void -sp_gradient_init(SPGradient *gr) +void SPGradientImpl::init(SPGradient *gr) { gr->ref = new SPGradientReference(SP_OBJECT(gr)); - gr->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(gradient_ref_changed), gr)); + gr->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(SPGradientImpl::gradientRefChanged), gr)); /** \todo * Fixme: reprs being rearranged (e.g. via the XML editor) @@ -391,16 +398,20 @@ sp_gradient_init(SPGradient *gr) /** * Virtual build: set gradient attributes from its associated repr. */ -static void -sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +void SPGradientImpl::build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) { SPGradient *gradient = SP_GRADIENT(object); - if (((SPObjectClass *) gradient_parent_class)->build) + // Work-around in case a swatch had been marked for immediate collection: + if ( repr->attribute("osb:paint") && repr->attribute("inkscape:collect") ) { + repr->setAttribute("inkscape:collect", 0); + } + + if (((SPObjectClass *) gradient_parent_class)->build) { (* ((SPObjectClass *) gradient_parent_class)->build)(object, document, repr); + } - SPObject *ochild; - for ( ochild = sp_object_first_child(object) ; ochild ; ochild = SP_OBJECT_NEXT(ochild) ) { + for ( SPObject *ochild = sp_object_first_child(object); ochild; ochild = ochild->next ) { if (SP_IS_STOP(ochild)) { gradient->has_stops = TRUE; break; @@ -411,6 +422,7 @@ sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *r sp_object_read_attr(object, "gradientTransform"); sp_object_read_attr(object, "spreadMethod"); sp_object_read_attr(object, "xlink:href"); + sp_object_read_attr(object, "osb:paint"); /* Register ourselves */ sp_document_add_resource(document, "gradient", object); @@ -419,8 +431,7 @@ sp_gradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *r /** * Virtual release of SPGradient members before destruction. */ -static void -sp_gradient_release(SPObject *object) +void SPGradientImpl::release(SPObject *object) { SPGradient *gradient = (SPGradient *) object; @@ -449,8 +460,7 @@ sp_gradient_release(SPObject *object) /** * Set gradient attribute to value. */ -static void -sp_gradient_set(SPObject *object, unsigned key, gchar const *value) +void SPGradientImpl::setGradientAttr(SPObject *object, unsigned key, gchar const *value) { SPGradient *gr = SP_GRADIENT(object); @@ -508,9 +518,31 @@ sp_gradient_set(SPObject *object, unsigned key, gchar const *value) gr->ref->detach(); } break; + case SP_ATTR_OSB_SWATCH: + { + bool newVal = (value != 0); + bool modified = false; + if (newVal != gr->swatch) { + gr->swatch = newVal; + modified = true; + } + if (newVal) { + // Might need to flip solid/gradient + Glib::ustring paintVal = ( gr->hasStops() && (gr->getStopCount() == 0) ) ? "solid" : "gradient"; + if ( paintVal != value ) { + sp_object_setAttribute( gr, "osb:paint", paintVal.c_str(), 0 ); + modified = true; + } + } + if (modified) { + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + } + } + break; default: - if (((SPObjectClass *) gradient_parent_class)->set) + if (((SPObjectClass *) gradient_parent_class)->set) { ((SPObjectClass *) gradient_parent_class)->set(object, key, value); + } break; } } @@ -518,8 +550,7 @@ sp_gradient_set(SPObject *object, unsigned key, gchar const *value) /** * Gets called when the gradient is (re)attached to another gradient. */ -static void -gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gr) +void SPGradientImpl::gradientRefChanged(SPObject *old_ref, SPObject *ref, SPGradient *gr) { if (old_ref) { gr->modified_connection.disconnect(); @@ -527,38 +558,46 @@ gradient_ref_changed(SPObject *old_ref, SPObject *ref, SPGradient *gr) if ( SP_IS_GRADIENT(ref) && ref != gr ) { - gr->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&gradient_ref_modified), gr)); + gr->modified_connection = ref->connectModified(sigc::bind<2>(sigc::ptr_fun(&SPGradientImpl::gradientRefModified), gr)); } // Per SVG, all unset attributes must be inherited from linked gradient. // So, as we're now (re)linked, we assign linkee's values to this gradient if they are not yet set - // but without setting the _set flags. // FIXME: do the same for gradientTransform too - if (!gr->units_set) - gr->units = sp_gradient_get_units (gr); - if (!gr->spread_set) - gr->spread = sp_gradient_get_spread (gr); + if (!gr->units_set) { + gr->units = gr->fetchUnits(); + } + if (!gr->spread_set) { + gr->spread = gr->fetchSpread(); + } /// \todo Fixme: what should the flags (second) argument be? */ - gradient_ref_modified(ref, 0, gr); + gradientRefModified(ref, 0, gr); } /** * Callback for child_added event. */ -static void -sp_gradient_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref) +void SPGradientImpl::childAdded(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref) { SPGradient *gr = SP_GRADIENT(object); - sp_gradient_invalidate_vector(gr); + gr->invalidateVector(); - if (((SPObjectClass *) gradient_parent_class)->child_added) + if (((SPObjectClass *) gradient_parent_class)->child_added) { (* ((SPObjectClass *) gradient_parent_class)->child_added)(object, child, ref); + } SPObject *ochild = sp_object_get_child_by_repr(object, child); if ( ochild && SP_IS_STOP(ochild) ) { gr->has_stops = TRUE; + if ( gr->getStopCount() > 0 ) { + gchar const * attr = gr->repr->attribute("osb:paint"); + if ( attr && strcmp(attr, "gradient") ) { + sp_object_setAttribute( gr, "osb:paint", "gradient", 0 ); + } + } } /// \todo Fixme: should we schedule "modified" here? @@ -568,15 +607,15 @@ sp_gradient_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape:: /** * Callback for remove_child event. */ -static void -sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child) +void SPGradientImpl::removeChild(SPObject *object, Inkscape::XML::Node *child) { SPGradient *gr = SP_GRADIENT(object); - sp_gradient_invalidate_vector(gr); + gr->invalidateVector(); - if (((SPObjectClass *) gradient_parent_class)->remove_child) + if (((SPObjectClass *) gradient_parent_class)->remove_child) { (* ((SPObjectClass *) gradient_parent_class)->remove_child)(object, child); + } gr->has_stops = FALSE; SPObject *ochild; @@ -587,6 +626,13 @@ sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child) } } + if ( gr->getStopCount() == 0 ) { + gchar const * attr = gr->repr->attribute("osb:paint"); + if ( attr && strcmp(attr, "solid") ) { + sp_object_setAttribute( gr, "osb:paint", "solid", 0 ); + } + } + /* Fixme: should we schedule "modified" here? */ object->requestModified(SP_OBJECT_MODIFIED_FLAG); } @@ -594,17 +640,16 @@ sp_gradient_remove_child(SPObject *object, Inkscape::XML::Node *child) /** * Callback for modified event. */ -static void -sp_gradient_modified(SPObject *object, guint flags) +void SPGradientImpl::modified(SPObject *object, guint flags) { SPGradient *gr = SP_GRADIENT(object); if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) { - sp_gradient_invalidate_vector(gr); + gr->invalidateVector(); } if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { - sp_gradient_ensure_vector(gr); + gr->ensureVector(); } if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; @@ -652,20 +697,21 @@ int SPGradient::getStopCount() const /** * Write gradient attributes to repr. */ -static Inkscape::XML::Node * -sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +Inkscape::XML::Node *SPGradientImpl::write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { SPGradient *gr = SP_GRADIENT(object); - if (((SPObjectClass *) gradient_parent_class)->write) + if (((SPObjectClass *) gradient_parent_class)->write) { (* ((SPObjectClass *) gradient_parent_class)->write)(object, xml_doc, repr, flags); + } if (flags & SP_OBJECT_WRITE_BUILD) { GSList *l = NULL; for (SPObject *child = sp_object_first_child(object); child; child = SP_OBJECT_NEXT(child)) { - Inkscape::XML::Node *crepr; - crepr = child->updateRepr(xml_doc, NULL, flags); - if (crepr) l = g_slist_prepend(l, crepr); + Inkscape::XML::Node *crepr = child->updateRepr(xml_doc, NULL, flags); + if (crepr) { + l = g_slist_prepend(l, crepr); + } } while (l) { repr->addChild((Inkscape::XML::Node *) l->data, NULL); @@ -699,7 +745,7 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape:: if ((flags & SP_OBJECT_WRITE_ALL) || gr->spread_set) { /* FIXME: Ensure that gr->spread is the inherited value - * if !gr->spread_set. Not currently happening: see sp_gradient_modified. + * if !gr->spread_set. Not currently happening: see SPGradient::modified. */ switch (gr->spread) { case SP_GRADIENT_SPREAD_REFLECT: @@ -714,6 +760,16 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape:: } } + if ( (flags & SP_OBJECT_WRITE_EXT) && gr->isSwatch() ) { + if ( gr->isSolid() ) { + repr->setAttribute( "osb:paint", "solid" ); + } else { + repr->setAttribute( "osb:paint", "gradient" ); + } + } else { + repr->setAttribute( "osb:paint", 0 ); + } + return repr; } @@ -722,40 +778,34 @@ sp_gradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape:: * * \pre SP_IS_GRADIENT(gradient). */ -void -sp_gradient_ensure_vector(SPGradient *gradient) +void SPGradient::ensureVector() { - g_return_if_fail(gradient != NULL); - g_return_if_fail(SP_IS_GRADIENT(gradient)); - - if (!gradient->vector.built) { - sp_gradient_rebuild_vector(gradient); + if ( !vector.built ) { + rebuildVector(); } } /** * Set units property of gradient and emit modified. */ -void -sp_gradient_set_units(SPGradient *gr, SPGradientUnits units) +void SPGradient::setUnits(SPGradientUnits units) { - if (units != gr->units) { - gr->units = units; - gr->units_set = TRUE; - SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG); + if (units != this->units) { + this->units = units; + units_set = TRUE; + requestModified(SP_OBJECT_MODIFIED_FLAG); } } /** * Set spread property of gradient and emit modified. */ -void -sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread) +void SPGradient::setSpread(SPGradientSpread spread) { - if (spread != gr->spread) { - gr->spread = spread; - gr->spread_set = TRUE; - SP_OBJECT(gr)->requestModified(SP_OBJECT_MODIFIED_FLAG); + if (spread != this->spread) { + this->spread = spread; + spread_set = TRUE; + requestModified(SP_OBJECT_MODIFIED_FLAG); } } @@ -806,16 +856,15 @@ chase_hrefs(SPGradient *const src, bool (*match)(SPGradient const *)) */ static bool has_stopsFN(SPGradient const *gr) { - return SP_GRADIENT_HAS_STOPS(gr); + return gr->hasStops(); } /** * True if gradient has spread set. */ -static bool -has_spread_set(SPGradient const *gr) +static bool has_spread_set(SPGradient const *gr) { - return gr->spread_set; + return gr->isSpreadSet(); } /** @@ -824,7 +873,7 @@ has_spread_set(SPGradient const *gr) static bool has_units_set(SPGradient const *gr) { - return gr->units_set; + return gr->isUnitsSet(); } @@ -843,12 +892,9 @@ SPGradient *SPGradient::getVector(bool force_vector) * * \pre SP_IS_GRADIENT(gradient). */ -SPGradientSpread -sp_gradient_get_spread(SPGradient *gradient) +SPGradientSpread SPGradient::fetchSpread() { - g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_SPREAD_PAD); - - SPGradient const *src = chase_hrefs(gradient, has_spread_set); + SPGradient const *src = chase_hrefs(this, has_spread_set); return ( src ? src->spread : SP_GRADIENT_SPREAD_PAD ); // pad is the default @@ -859,12 +905,9 @@ sp_gradient_get_spread(SPGradient *gradient) * * \pre SP_IS_GRADIENT(gradient). */ -SPGradientUnits -sp_gradient_get_units(SPGradient *gradient) +SPGradientUnits SPGradient::fetchUnits() { - g_return_val_if_fail(SP_IS_GRADIENT(gradient), SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX); - - SPGradient const *src = chase_hrefs(gradient, has_units_set); + SPGradient const *src = chase_hrefs(this, has_units_set); return ( src ? src->units : SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ); // bbox is the default @@ -919,9 +962,7 @@ sp_gradient_repr_write_vector(SPGradient *gr) sp_repr_set_css_double(child, "offset", gr->vector.stops[i].offset); /* strictly speaking, offset an SVG <number> rather than a CSS one, but exponents make no * sense for offset proportions. */ - gchar c[64]; - sp_svg_write_color(c, sizeof(c), gr->vector.stops[i].color.toRGBA32( 0x00 )); - os << "stop-color:" << c << ";stop-opacity:" << gr->vector.stops[i].opacity; + os << "stop-color:" << gr->vector.stops[i].color.toString() << ";stop-opacity:" << gr->vector.stops[i].opacity; child->setAttribute("style", os.str().c_str()); /* Order will be reversed here */ cl = g_slist_prepend(cl, child); @@ -939,24 +980,22 @@ sp_gradient_repr_write_vector(SPGradient *gr) } -static void -gradient_ref_modified(SPObject */*href*/, guint /*flags*/, SPGradient *gradient) +void SPGradientImpl::gradientRefModified(SPObject */*href*/, guint /*flags*/, SPGradient *gradient) { - if (sp_gradient_invalidate_vector(gradient)) { + if ( gradient->invalidateVector() ) { SP_OBJECT(gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); - /* Conditional to avoid causing infinite loop if there's a cycle in the href chain. */ + // Conditional to avoid causing infinite loop if there's a cycle in the href chain. } } -/** Return true iff change made. */ -static bool -sp_gradient_invalidate_vector(SPGradient *gr) +/** Return true if change made. */ +bool SPGradient::invalidateVector() { bool ret = false; - if (gr->vector.built) { - gr->vector.built = false; - gr->vector.stops.clear(); + if (vector.built) { + vector.built = false; + vector.stops.clear(); ret = true; } @@ -964,11 +1003,10 @@ sp_gradient_invalidate_vector(SPGradient *gr) } /** Creates normalized color vector */ -static void -sp_gradient_rebuild_vector(SPGradient *gr) +void SPGradient::rebuildVector() { gint len = 0; - for ( SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ; + for ( SPObject *child = sp_object_first_child(SP_OBJECT(this)) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_STOP(child)) { @@ -976,36 +1014,36 @@ sp_gradient_rebuild_vector(SPGradient *gr) } } - gr->has_stops = (len != 0); + has_stops = (len != 0); - gr->vector.stops.clear(); + vector.stops.clear(); - SPGradient *ref = gr->ref->getObject(); - if ( !gr->has_stops && ref ) { + SPGradient *reffed = ref->getObject(); + if ( !hasStops() && reffed ) { /* Copy vector from referenced gradient */ - gr->vector.built = true; // Prevent infinite recursion. - sp_gradient_ensure_vector(ref); - if (!ref->vector.stops.empty()) { - gr->vector.built = ref->vector.built; - gr->vector.stops.assign(ref->vector.stops.begin(), ref->vector.stops.end()); + vector.built = true; // Prevent infinite recursion. + reffed->ensureVector(); + if (!reffed->vector.stops.empty()) { + vector.built = reffed->vector.built; + vector.stops.assign(reffed->vector.stops.begin(), reffed->vector.stops.end()); return; } } - for (SPObject *child = sp_object_first_child(SP_OBJECT(gr)) ; + for (SPObject *child = sp_object_first_child(SP_OBJECT(this)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { if (SP_IS_STOP(child)) { SPStop *stop = SP_STOP(child); SPGradientStop gstop; - if (gr->vector.stops.size() > 0) { + if (vector.stops.size() > 0) { // "Each gradient offset value is required to be equal to or greater than the // previous gradient stop's offset value. If a given gradient stop's offset // value is not equal to or greater than all previous offset values, then the // offset value is adjusted to be equal to the largest of all previous offset // values." - gstop.offset = MAX(stop->offset, gr->vector.stops.back().offset); + gstop.offset = MAX(stop->offset, vector.stops.back().offset); } else { gstop.offset = stop->offset; } @@ -1015,15 +1053,15 @@ sp_gradient_rebuild_vector(SPGradient *gr) // down to 100%." gstop.offset = CLAMP(gstop.offset, 0, 1); - gstop.color = sp_stop_get_color(stop); + gstop.color = stop->getEffectiveColor(); gstop.opacity = stop->opacity; - gr->vector.stops.push_back(gstop); + vector.stops.push_back(gstop); } } // Normalize per section 13.2.4 of SVG 1.1. - if (gr->vector.stops.size() == 0) { + if (vector.stops.size() == 0) { /* "If no stops are defined, then painting shall occur as if 'none' were specified as the * paint style." */ @@ -1032,44 +1070,44 @@ sp_gradient_rebuild_vector(SPGradient *gr) gstop.offset = 0.0; gstop.color.set( 0x00000000 ); gstop.opacity = 0.0; - gr->vector.stops.push_back(gstop); + vector.stops.push_back(gstop); } { SPGradientStop gstop; gstop.offset = 1.0; gstop.color.set( 0x00000000 ); gstop.opacity = 0.0; - gr->vector.stops.push_back(gstop); + vector.stops.push_back(gstop); } } else { /* "If one stop is defined, then paint with the solid color fill using the color defined * for that gradient stop." */ - if (gr->vector.stops.front().offset > 0.0) { + if (vector.stops.front().offset > 0.0) { // If the first one is not at 0, then insert a copy of the first at 0. SPGradientStop gstop; gstop.offset = 0.0; - gstop.color = gr->vector.stops.front().color; - gstop.opacity = gr->vector.stops.front().opacity; - gr->vector.stops.insert(gr->vector.stops.begin(), gstop); + gstop.color = vector.stops.front().color; + gstop.opacity = vector.stops.front().opacity; + vector.stops.insert(vector.stops.begin(), gstop); } - if (gr->vector.stops.back().offset < 1.0) { + if (vector.stops.back().offset < 1.0) { // If the last one is not at 1, then insert a copy of the last at 1. SPGradientStop gstop; gstop.offset = 1.0; - gstop.color = gr->vector.stops.back().color; - gstop.opacity = gr->vector.stops.back().opacity; - gr->vector.stops.push_back(gstop); + gstop.color = vector.stops.back().color; + gstop.opacity = vector.stops.back().opacity; + vector.stops.push_back(gstop); } } - gr->vector.built = true; + vector.built = true; } Geom::Matrix sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Matrix const &ctm, Geom::Rect const &bbox) { - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { return ( Geom::Scale(bbox.dimensions()) * Geom::Translate(bbox.min()) * Geom::Matrix(ctm) ); @@ -1081,7 +1119,7 @@ sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Matrix const &ctm, Geom:: Geom::Matrix sp_gradient_get_gs2d_matrix(SPGradient const *gr, Geom::Matrix const &ctm, Geom::Rect const &bbox) { - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { return ( gr->gradientTransform * Geom::Scale(bbox.dimensions()) * Geom::Translate(bbox.min()) @@ -1096,7 +1134,7 @@ sp_gradient_set_gs2d_matrix(SPGradient *gr, Geom::Matrix const &ctm, Geom::Rect const &bbox, Geom::Matrix const &gs2d) { gr->gradientTransform = gs2d * ctm.inverse(); - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX ) { gr->gradientTransform = ( gr->gradientTransform * Geom::Translate(-bbox.min()) * Geom::Scale(bbox.dimensions()).inverse() ); @@ -1462,7 +1500,7 @@ sp_gradient_pattern_common_setup(cairo_pattern_t *cp, double opacity) { // set spread type - switch (sp_gradient_get_spread(gr)) { + switch (gr->getSpread()) { case SP_GRADIENT_SPREAD_REFLECT: cairo_pattern_set_extend(cp, CAIRO_EXTEND_REFLECT); break; @@ -1486,7 +1524,7 @@ sp_gradient_pattern_common_setup(cairo_pattern_t *cp, // set pattern matrix Geom::Matrix gs2user = gr->gradientTransform; - if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { + if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { Geom::Matrix bbox2user(bbox->x1 - bbox->x0, 0, 0, bbox->y1 - bbox->y0, bbox->x0, bbox->y0); gs2user *= bbox2user; } @@ -1502,7 +1540,7 @@ sp_radialgradient_create_pattern(SPPaintServer *ps, SPRadialGradient *rg = SP_RADIALGRADIENT(ps); SPGradient *gr = SP_GRADIENT(ps); - sp_gradient_ensure_vector(gr); + gr->ensureVector(); cairo_pattern_t *cp = cairo_pattern_create_radial( rg->fx.computed, rg->fy.computed, 0, @@ -1522,7 +1560,7 @@ sp_lineargradient_create_pattern(SPPaintServer *ps, SPLinearGradient *lg = SP_LINEARGRADIENT(ps); SPGradient *gr = SP_GRADIENT(ps); - sp_gradient_ensure_vector(gr); + gr->ensureVector(); cairo_pattern_t *cp = cairo_pattern_create_linear( lg->x1.computed, lg->y1.computed, @@ -1536,7 +1574,7 @@ sp_lineargradient_create_pattern(SPPaintServer *ps, cairo_pattern_t * sp_gradient_create_preview_pattern(SPGradient *gr, double width) { - sp_gradient_ensure_vector(gr); + gr->ensureVector(); cairo_pattern_t *pat = cairo_pattern_create_linear(0, 0, width, 0); diff --git a/src/sp-gradient.h b/src/sp-gradient.h index 28099961d..05aea2618 100644 --- a/src/sp-gradient.h +++ b/src/sp-gradient.h @@ -19,6 +19,7 @@ #include <gdk/gdktypes.h> #include <2geom/matrix.h> +#include <glibmm/ustring.h> #include "sp-paint-server.h" #include "sp-gradient-spread.h" #include "sp-gradient-units.h" @@ -29,14 +30,12 @@ struct SPGradientReference; -#define SP_TYPE_GRADIENT (sp_gradient_get_type()) +#define SP_TYPE_GRADIENT (SPGradient::getType()) #define SP_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_GRADIENT, SPGradient)) #define SP_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_GRADIENT, SPGradientClass)) #define SP_IS_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_GRADIENT)) #define SP_IS_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_GRADIENT)) -GType sp_gradient_get_type(); - typedef enum { SP_GRADIENT_TYPE_UNKNOWN, SP_GRADIENT_TYPE_LINEAR, @@ -78,29 +77,44 @@ struct SPGradient : public SPPaintServer { /** State in Inkscape gradient system */ guint state : 2; +private: /** gradientUnits attribute */ SPGradientUnits units; guint units_set : 1; +public: /** gradientTransform attribute */ Geom::Matrix gradientTransform; guint gradientTransform_set : 1; +private: /** spreadMethod attribute */ SPGradientSpread spread; guint spread_set : 1; /** Gradient stops */ guint has_stops : 1; +public: /** Composed vector */ SPGradientVector vector; sigc::connection modified_connection; + bool hasStops() const; + SPStop* getFirstStop(); int getStopCount() const; + + bool isUnitsSet() const; + SPGradientUnits getUnits() const; + void setUnits(SPGradientUnits units); + + + bool isSpreadSet() const; + SPGradientSpread getSpread() const; + /** * Returns private vector of given gradient (the gradient at the end of the href chain which has * stops), optionally normalizing it. @@ -109,6 +123,32 @@ struct SPGradient : public SPPaintServer { * \pre There exists a gradient in the chain that has stops. */ SPGradient *getVector(bool force_private = false); + + static GType getType(); + + /** Forces vector to be built, if not present (i.e. changed) */ + void ensureVector(); + + /** Ensures that color array is populated */ + void ensureColors(); + + /** + * Set spread property of gradient and emit modified. + */ + void setSpread(SPGradientSpread spread); + + SPGradientSpread fetchSpread(); + SPGradientUnits fetchUnits(); + + void setSwatch(bool swatch = true); + +private: + bool invalidateVector(); + void rebuildVector(); + + friend class SPGradientImpl; + friend class SPLGPainter; + friend class SPRGPainter; }; /** diff --git a/src/sp-item.cpp b/src/sp-item.cpp index a5510f203..6c14537c5 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -186,16 +186,17 @@ bool SPItem::isVisibleAndUnlocked(unsigned display_key) const { } bool SPItem::isLocked() const { - for (SPObject *o = SP_OBJECT(this); o != NULL; o = SP_OBJECT_PARENT(o)) { - if (SP_IS_ITEM(o) && !(SP_ITEM(o)->sensitive)) + for (SPObject const *o = this; o != NULL; o = o->parent) { + if (SP_IS_ITEM(o) && !(SP_ITEM(o)->sensitive)) { return true; + } } return false; } void SPItem::setLocked(bool locked) { - SP_OBJECT_REPR(this)->setAttribute("sodipodi:insensitive", - ( locked ? "1" : NULL )); + this->repr->setAttribute("sodipodi:insensitive", + ( locked ? "1" : NULL )); updateRepr(); } @@ -245,7 +246,7 @@ void SPItem::resetEvaluated() { requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); } } if ( StatusSet == _evaluated_status ) { - SPObject const *const parent = SP_OBJECT_PARENT(this); + SPObject const *const parent = this->parent; if (SP_IS_SWITCH(parent)) { SP_SWITCH(parent)->resetChildEvaluated(); } @@ -289,7 +290,7 @@ SPItem::setExplicitlyHidden(bool const val) { void SPItem::setCenter(Geom::Point object_centre) { // for getBounds() to work - sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(this)); + sp_document_ensure_up_to_date( this->document ); Geom::OptRect bbox = getBounds(sp_item_i2d_affine(this)); if (bbox) { @@ -314,7 +315,7 @@ bool SPItem::isCenterSet() { Geom::Point SPItem::getCenter() const { // for getBounds() to work - sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(this)); + sp_document_ensure_up_to_date( this->document ); Geom::OptRect bbox = getBounds(sp_item_i2d_affine(this)); if (bbox) { @@ -337,21 +338,21 @@ void SPItem::raiseToTop() { using Inkscape::Algorithms::find_last_if; SPObject *topmost=find_last_if<SPObject::SiblingIterator>( - SP_OBJECT_NEXT(this), NULL, &is_item + this->next, NULL, &is_item ); if (topmost) { - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - sp_repr_parent(repr)->changeOrder(repr, SP_OBJECT_REPR(topmost)); + Inkscape::XML::Node *repr = this->repr; + sp_repr_parent(repr)->changeOrder( repr, topmost->repr ); } } void SPItem::raiseOne() { SPObject *next_higher=std::find_if<SPObject::SiblingIterator>( - SP_OBJECT_NEXT(this), NULL, &is_item + this->next, NULL, &is_item ); if (next_higher) { - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - Inkscape::XML::Node *ref=SP_OBJECT_REPR(next_higher); + Inkscape::XML::Node *repr = this->repr; + Inkscape::XML::Node *ref = next_higher->repr; sp_repr_parent(repr)->changeOrder(repr, ref); } } @@ -362,15 +363,15 @@ void SPItem::lowerOne() { MutableList<SPObject &> next_lower=std::find_if( reverse_list<SPObject::SiblingIterator>( - SP_OBJECT_PARENT(this)->firstChild(), this + this->parent->firstChild(), this ), MutableList<SPObject &>(), &is_item ); if (next_lower) { ++next_lower; - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - Inkscape::XML::Node *ref=( next_lower ? SP_OBJECT_REPR(&*next_lower) : NULL ); + Inkscape::XML::Node *repr = this->repr; + Inkscape::XML::Node *ref = ( next_lower ? next_lower->repr : NULL ); sp_repr_parent(repr)->changeOrder(repr, ref); } } @@ -382,15 +383,15 @@ void SPItem::lowerToBottom() { MutableList<SPObject &> bottom=find_last_if( reverse_list<SPObject::SiblingIterator>( - SP_OBJECT_PARENT(this)->firstChild(), this + this->parent->firstChild(), this ), MutableList<SPObject &>(), &is_item ); if (bottom) { ++bottom; - Inkscape::XML::Node *repr=SP_OBJECT_REPR(this); - Inkscape::XML::Node *ref=( bottom ? SP_OBJECT_REPR(&*bottom) : NULL ); + Inkscape::XML::Node *repr = this->repr; + Inkscape::XML::Node *ref = ( bottom ? bottom->repr : NULL ); sp_repr_parent(repr)->changeOrder(repr, ref); } } @@ -573,7 +574,7 @@ clip_ref_changed(SPObject *old_clip, SPObject *clip, SPItem *item) nr_arena_item_set_clip(v->arenaitem, ai); nr_arena_item_unref(ai); sp_clippath_set_bbox(SP_CLIPPATH(clip), NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox); - SP_OBJECT(clip)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + clip->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } } @@ -601,7 +602,7 @@ mask_ref_changed(SPObject *old_mask, SPObject *mask, SPItem *item) nr_arena_item_set_mask(v->arenaitem, ai); nr_arena_item_unref(ai); sp_mask_set_bbox(SP_MASK(mask), NR_ARENA_ITEM_GET_KEY(v->arenaitem), &bbox); - SP_OBJECT(mask)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } } } @@ -676,7 +677,7 @@ sp_item_write(SPObject *const object, Inkscape::XML::Document *xml_doc, Inkscape Inkscape::XML::Node *crepr; GSList *l; l = NULL; - for (child = sp_object_first_child(object); child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (child = sp_object_first_child(object); child != NULL; child = child->next ) { if (!SP_IS_TITLE(child) && !SP_IS_DESC(child)) continue; crepr = child->updateRepr(xml_doc, NULL, flags); if (crepr) l = g_slist_prepend (l, crepr); @@ -687,7 +688,7 @@ sp_item_write(SPObject *const object, Inkscape::XML::Document *xml_doc, Inkscape l = g_slist_remove (l, l->data); } } else { - for (child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + for (child = sp_object_first_child(object) ; child != NULL; child = child->next ) { if (!SP_IS_TITLE(child) && !SP_IS_DESC(child)) continue; child->updateRepr(flags); } @@ -783,8 +784,8 @@ sp_item_invoke_bbox_full(SPItem const *item, Geom::OptRect &bbox, Geom::Matrix c // unless this is geometric bbox, extend by filter area and crop the bbox by clip path, if any if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) { - if (SP_OBJECT_STYLE(item) && SP_OBJECT_STYLE(item)->filter.href) { - SPObject *filter = SP_OBJECT_STYLE(item)->getFilter(); + if ( item->style && item->style->filter.href ) { + SPObject *filter = item->style->getFilter(); if (filter && SP_IS_FILTER(filter)) { // default filer area per the SVG spec: double x = -0.1; @@ -907,14 +908,14 @@ unsigned sp_item_pos_in_parent(SPItem *item) g_assert(item != NULL); g_assert(SP_IS_ITEM(item)); - SPObject *parent = SP_OBJECT_PARENT(item); + SPObject *parent = item->parent; g_assert(parent != NULL); g_assert(SP_IS_OBJECT(parent)); - SPObject *object = SP_OBJECT(item); + SPObject *object = item; - unsigned pos=0; - for ( SPObject *iter = sp_object_first_child(parent) ; iter ; iter = SP_OBJECT_NEXT(iter)) { + unsigned pos = 0; + for ( SPObject *iter = sp_object_first_child(parent) ; iter ; iter = iter->next) { if ( iter == object ) { return pos; } @@ -967,8 +968,15 @@ static void sp_item_private_snappoints(SPItem const *item, std::vector<Inkscape: void sp_item_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { - g_assert (item != NULL); - g_assert (SP_IS_ITEM(item)); + if (item == NULL) { + g_warning("sp_item_snappoints: cannot snap because no item is being provided"); + return; + } + + if (!SP_IS_ITEM(item)) { + g_warning("sp_item_snappoints: cannot snap because this is not a SP_ITEM"); + return; + } // Get the snappoints of the item SPItemClass const &item_class = *(SPItemClass const *) G_OBJECT_GET_CLASS(item); @@ -984,8 +992,8 @@ void sp_item_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidateP // Get the snappoints of clipping paths and mask, if any std::list<SPObject const *> clips_and_masks; - clips_and_masks.push_back(SP_OBJECT(item->clip_ref->getObject())); - clips_and_masks.push_back(SP_OBJECT(item->mask_ref->getObject())); + clips_and_masks.push_back(item->clip_ref->getObject()); + clips_and_masks.push_back(item->mask_ref->getObject()); SPDesktop *desktop = inkscape_active_desktop(); for (std::list<SPObject const *>::const_iterator o = clips_and_masks.begin(); o != clips_and_masks.end(); o++) { @@ -1015,9 +1023,9 @@ sp_item_invoke_print(SPItem *item, SPPrintContext *ctx) if (!item->isHidden()) { if (((SPItemClass *) G_OBJECT_GET_CLASS(item))->print) { if (!item->transform.isIdentity() - || SP_OBJECT_STYLE(item)->opacity.value != SP_SCALE24_MAX) + || item->style->opacity.value != SP_SCALE24_MAX) { - sp_print_bind(ctx, item->transform, SP_SCALE24_TO_FLOAT(SP_OBJECT_STYLE(item)->opacity.value)); + sp_print_bind(ctx, item->transform, SP_SCALE24_TO_FLOAT(item->style->opacity.value)); ((SPItemClass *) G_OBJECT_GET_CLASS(item))->print(item, ctx); sp_print_release(ctx); } else { @@ -1056,8 +1064,8 @@ sp_item_description(SPItem *item) g_free (s); s = snew; } - if (SP_OBJECT_STYLE(item) && SP_OBJECT_STYLE(item)->filter.href && SP_OBJECT_STYLE(item)->filter.href->getObject()) { - const gchar *label = SP_OBJECT_STYLE(item)->filter.href->getObject()->label(); + if ( item->style && item->style->filter.href && item->style->filter.href->getObject() ) { + const gchar *label = item->style->filter.href->getObject()->label(); gchar *snew; if (label) { snew = g_strdup_printf (_("%s; <i>filtered (%s)</i>"), s, _(label)); @@ -1106,7 +1114,7 @@ sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags) if (ai != NULL) { item->display = sp_item_view_new_prepend(item->display, item, flags, key, ai); nr_arena_item_set_transform(ai, item->transform); - nr_arena_item_set_opacity(ai, SP_SCALE24_TO_FLOAT(SP_OBJECT_STYLE(item)->opacity.value)); + nr_arena_item_set_opacity(ai, SP_SCALE24_TO_FLOAT(item->style->opacity.value)); nr_arena_item_set_visible(ai, !item->isHidden()); nr_arena_item_set_sensitive(ai, item->sensitive); if (item->clip_ref->getObject()) { @@ -1126,7 +1134,7 @@ sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags) NRRect bbox; sp_item_invoke_bbox(item, &bbox, Geom::identity(), TRUE); sp_clippath_set_bbox(SP_CLIPPATH(cp), clip_key, &bbox); - SP_OBJECT(cp)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + cp->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } if (item->mask_ref->getObject()) { SPMask *mask = item->mask_ref->getObject(); @@ -1145,7 +1153,7 @@ sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags) NRRect bbox; sp_item_invoke_bbox(item, &bbox, Geom::identity(), TRUE); sp_mask_set_bbox(SP_MASK(mask), mask_key, &bbox); - SP_OBJECT(mask)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + mask->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } NR_ARENA_ITEM_SET_DATA(ai, item); Geom::OptRect item_bbox; @@ -1199,34 +1207,32 @@ sp_item_invoke_hide(SPItem *item, unsigned key) void sp_item_adjust_pattern (SPItem *item, Geom::Matrix const &postmul, bool set) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; - if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_PATTERN (server)) { - SPPattern *pattern = sp_pattern_clone_if_necessary (item, SP_PATTERN (server), "fill"); - sp_pattern_transform_multiply (pattern, postmul, set); + if ( style && style->fill.isPaintserver() ) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_PATTERN(server) ) { + SPPattern *pattern = sp_pattern_clone_if_necessary(item, SP_PATTERN(server), "fill"); + sp_pattern_transform_multiply(pattern, postmul, set); } } - if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_PATTERN (server)) { - SPPattern *pattern = sp_pattern_clone_if_necessary (item, SP_PATTERN (server), "stroke"); - sp_pattern_transform_multiply (pattern, postmul, set); + if ( style && style->stroke.isPaintserver() ) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_PATTERN(server) ) { + SPPattern *pattern = sp_pattern_clone_if_necessary(item, SP_PATTERN(server), "stroke"); + sp_pattern_transform_multiply(pattern, postmul, set); } } - } -void -sp_item_adjust_gradient (SPItem *item, Geom::Matrix const &postmul, bool set) +void sp_item_adjust_gradient( SPItem *item, Geom::Matrix const &postmul, bool set ) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; - if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_GRADIENT (server)) { + if ( style && style->fill.isPaintserver() ) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { /** * \note Bbox units for a gradient are generally a bad idea because @@ -1237,40 +1243,38 @@ sp_item_adjust_gradient (SPItem *item, Geom::Matrix const &postmul, bool set) * \todo FIXME: convert back to bbox units after transforming with * the item, so as to preserve the original units. */ - SPGradient *gradient = sp_gradient_convert_to_userspace (SP_GRADIENT (server), item, "fill"); + SPGradient *gradient = sp_gradient_convert_to_userspace( SP_GRADIENT(server), item, "fill" ); - sp_gradient_transform_multiply (gradient, postmul, set); + sp_gradient_transform_multiply( gradient, postmul, set ); } } - if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_GRADIENT (server)) { - SPGradient *gradient = sp_gradient_convert_to_userspace (SP_GRADIENT (server), item, "stroke"); - sp_gradient_transform_multiply (gradient, postmul, set); + if ( style && style->stroke.isPaintserver() ) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { + SPGradient *gradient = sp_gradient_convert_to_userspace( SP_GRADIENT(server), item, "stroke" ); + sp_gradient_transform_multiply( gradient, postmul, set ); } } } -void -sp_item_adjust_stroke (SPItem *item, gdouble ex) +void sp_item_adjust_stroke( SPItem *item, gdouble ex ) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (style && !style->stroke.isNone() && !Geom::are_near(ex, 1.0, Geom::EPSILON)) { style->stroke_width.computed *= ex; style->stroke_width.set = TRUE; - if (style->stroke_dash.n_dash != 0) { - int i; - for (i = 0; i < style->stroke_dash.n_dash; i++) { + if ( style->stroke_dash.n_dash != 0 ) { + for (int i = 0; i < style->stroke_dash.n_dash; i++) { style->stroke_dash.dash[i] *= ex; } style->stroke_dash.offset *= ex; } - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); } } @@ -1281,7 +1285,7 @@ Geom::Matrix sp_item_transform_repr (SPItem *item) { Geom::Matrix t_old(Geom::identity()); - gchar const *t_attr = SP_OBJECT_REPR(item)->attribute("transform"); + gchar const *t_attr = item->repr->attribute("transform"); if (t_attr) { Geom::Matrix t; if (sp_svg_transform_read(t_attr, &t)) { @@ -1305,7 +1309,7 @@ sp_item_adjust_stroke_width_recursive(SPItem *item, double expansion) if (item && SP_IS_USE(item)) return; - for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { + for (SPObject *o = item->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) sp_item_adjust_stroke_width_recursive(SP_ITEM(o), expansion); } @@ -1321,7 +1325,7 @@ sp_item_adjust_rects_recursive(SPItem *item, Geom::Matrix advertized_transform) sp_rect_compensate_rxry (SP_RECT(item), advertized_transform); } - for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { + for (SPObject *o = item->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) sp_item_adjust_rects_recursive(SP_ITEM(o), advertized_transform); } @@ -1343,7 +1347,7 @@ sp_item_adjust_paint_recursive (SPItem *item, Geom::Matrix advertized_transform, // also we do not recurse into clones, because a clone's child is the ghost of its original - // we must not touch it if (!(item && (SP_IS_TEXT(item) || SP_IS_USE(item)))) { - for (SPObject *o = SP_OBJECT(item)->children; o != NULL; o = o->next) { + for (SPObject *o = item->children; o != NULL; o = o->next) { if (SP_IS_ITEM(o)) { // At the level of the transformed item, t_ancestors is identity; // below it, it is the accmmulated chain of transforms from this level to the top level @@ -1447,7 +1451,7 @@ sp_item_write_transform(SPItem *item, Inkscape::XML::Node *repr, Geom::Matrix co !preserve && // user did not chose to preserve all transforms !item->clip_ref->getObject() && // the object does not have a clippath !item->mask_ref->getObject() && // the object does not have a mask - !(!transform.isTranslation() && SP_OBJECT_STYLE(item) && SP_OBJECT_STYLE(item)->getFilter()) + !(!transform.isTranslation() && item->style && item->style->getFilter()) // the object does not have a filter, or the transform is translation (which is supposed to not affect filters) ) { transform_attr = ((SPItemClass *) G_OBJECT_GET_CLASS(item))->set_transform(item, transform); @@ -1458,7 +1462,7 @@ sp_item_write_transform(SPItem *item, Inkscape::XML::Node *repr, Geom::Matrix co // it causes clone SPUse's copy of the original object to brought up to // date with the original. Otherwise, sp_use_bbox returns incorrect // values if called in code handling the transformed signal. - SP_OBJECT(item)->updateRepr(); + item->updateRepr(); // send the relative transform with a _transformed_signal item->_transformed_signal.emit(&advertized_transform, item); @@ -1529,7 +1533,7 @@ i2anc_affine(SPObject const *object, SPObject const *const ancestor) { } else { ret *= SP_ITEM(object)->transform; } - object = SP_OBJECT_PARENT(object); + object = object->parent; } return ret; } @@ -1564,7 +1568,7 @@ Geom::Matrix sp_item_i2d_affine(SPItem const *item) Geom::Matrix const ret( sp_item_i2doc_affine(item) * Geom::Scale(1, -1) - * Geom::Translate(0, sp_document_height(SP_OBJECT_DOCUMENT(item))) ); + * Geom::Translate(0, sp_document_height(item->document)) ); return ret; } @@ -1574,10 +1578,10 @@ void sp_item_set_i2d_affine(SPItem *item, Geom::Matrix const &i2dt) g_return_if_fail( SP_IS_ITEM(item) ); Geom::Matrix dt2p; /* desktop to item parent transform */ - if (SP_OBJECT_PARENT(item)) { - dt2p = sp_item_i2d_affine((SPItem *) SP_OBJECT_PARENT(item)).inverse(); + if (item->parent) { + dt2p = sp_item_i2d_affine(static_cast<SPItem *>(item->parent)).inverse(); } else { - dt2p = ( Geom::Translate(0, -sp_document_height(SP_OBJECT_DOCUMENT(item))) + dt2p = ( Geom::Translate(0, -sp_document_height(item->document)) * Geom::Scale(1, -1) ); } @@ -1655,16 +1659,17 @@ sp_item_get_arenaitem(SPItem *item, unsigned key) int sp_item_repr_compare_position(SPItem *first, SPItem *second) { - return sp_repr_compare_position(SP_OBJECT_REPR(first), - SP_OBJECT_REPR(second)); + return sp_repr_compare_position(first->repr, + second->repr); } SPItem * sp_item_first_item_child (SPObject *obj) { - for ( SPObject *iter = sp_object_first_child(obj) ; iter ; iter = SP_OBJECT_NEXT(iter)) { - if (SP_IS_ITEM (iter)) + for ( SPObject *iter = sp_object_first_child(obj) ; iter ; iter = iter->next) { + if ( SP_IS_ITEM(iter) ) { return SP_ITEM (iter); + } } return NULL; } diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 420c7b4a6..fd17b3c12 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -38,6 +38,7 @@ #include "helper/sp-marshal.h" #include "xml/node-event-vector.h" #include "attributes.h" +#include "color-profile-fns.h" #include "document.h" #include "style.h" #include "sp-object-repr.h" @@ -539,29 +540,33 @@ SPObject::setLabel(gchar const *label) { /** Queues the object for orphan collection */ -void -SPObject::requestOrphanCollection() { +void SPObject::requestOrphanCollection() { g_return_if_fail(document != NULL); // do not remove style or script elements (Bug #276244) - if (SP_IS_STYLE_ELEM(this)) - return; - if (SP_IS_SCRIPT(this)) - return; - - document->queueForOrphanCollection(this); + if (SP_IS_STYLE_ELEM(this)) { + // leave it + } else if (SP_IS_SCRIPT(this)) { + // leave it + } else if (SP_IS_PAINT_SERVER(this) && static_cast<SPPaintServer*>(this)->isSwatch() ) { + // leave it + } else if (IS_COLORPROFILE(this)) { + // leave it + } else { + document->queueForOrphanCollection(this); - /** \todo - * This is a temporary hack added to make fill&stroke rebuild its - * gradient list when the defs are vacuumed. gradient-vector.cpp - * listens to the modified signal on defs, and now we give it that - * signal. Mental says that this should be made automatic by - * merging SPObjectGroup with SPObject; SPObjectGroup would issue - * this signal automatically. Or maybe just derive SPDefs from - * SPObjectGroup? - */ + /** \todo + * This is a temporary hack added to make fill&stroke rebuild its + * gradient list when the defs are vacuumed. gradient-vector.cpp + * listens to the modified signal on defs, and now we give it that + * signal. Mental says that this should be made automatic by + * merging SPObjectGroup with SPObject; SPObjectGroup would issue + * this signal automatically. Or maybe just derive SPDefs from + * SPObjectGroup? + */ - this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG); + this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG); + } } /** Sends the delete signal to all children of this object recursively */ diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index 35a5ff1f1..010d98818 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -20,15 +20,12 @@ #include "xml/node.h" static void sp_paint_server_class_init(SPPaintServerClass *psc); -static void sp_paint_server_init(SPPaintServer *ps); - -static void sp_paint_server_release(SPObject *object); static cairo_pattern_t *sp_paint_server_create_dummy_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity); static SPObjectClass *parent_class; -GType sp_paint_server_get_type (void) +GType SPPaintServer::get_type(void) { static GType type = 0; if (!type) { @@ -41,7 +38,7 @@ GType sp_paint_server_get_type (void) NULL, /* class_data */ sizeof(SPPaintServer), 16, /* n_preallocs */ - (GInstanceInitFunc) sp_paint_server_init, + (GInstanceInitFunc) SPPaintServer::init, NULL, /* value_table */ }; type = g_type_register_static(SP_TYPE_OBJECT, "SPPaintServer", &info, (GTypeFlags) 0); @@ -51,23 +48,15 @@ GType sp_paint_server_get_type (void) static void sp_paint_server_class_init(SPPaintServerClass *psc) { - SPObjectClass *sp_object_class = (SPObjectClass *) psc; - sp_object_class->release = sp_paint_server_release; psc->pattern_new = sp_paint_server_create_dummy_pattern; + parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT); } -static void sp_paint_server_init(SPPaintServer *ps) +void SPPaintServer::init(SPPaintServer *ps) { } -static void sp_paint_server_release(SPObject *object) -{ - if (((SPObjectClass *) parent_class)->release) { - ((SPObjectClass *) parent_class)->release(object); - } -} - cairo_pattern_t *sp_paint_server_create_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, @@ -100,29 +89,16 @@ sp_paint_server_create_dummy_pattern(SPPaintServer */*ps*/, bool SPPaintServer::isSwatch() const { - bool swatch = false; - if (SP_IS_GRADIENT(this)) { - SPGradient *grad = SP_GRADIENT(this); - if ( SP_GRADIENT_HAS_STOPS(grad) ) { - gchar const * attr = repr->attribute("osb:paint"); - if (attr && !strcmp(attr, "solid")) { - swatch = true; - } - } - } return swatch; } bool SPPaintServer::isSolid() const { bool solid = false; - if (SP_IS_GRADIENT(this)) { + if (swatch && SP_IS_GRADIENT(this)) { SPGradient *grad = SP_GRADIENT(this); - if ( SP_GRADIENT_HAS_STOPS(grad) && (grad->getStopCount() == 0) ) { - gchar const * attr = repr->attribute("osb:paint"); - if (attr && !strcmp(attr, "solid")) { - solid = true; - } + if ( grad->hasStops() && (grad->getStopCount() == 0) ) { + solid = true; } } return solid; diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index 8c9af9f55..f18a7e689 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -22,15 +22,24 @@ struct NRPixBlock; struct NRRect; -#define SP_TYPE_PAINT_SERVER (sp_paint_server_get_type ()) +#define SP_TYPE_PAINT_SERVER (SPPaintServer::get_type()) #define SP_PAINT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_PAINT_SERVER, SPPaintServer)) #define SP_PAINT_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_PAINT_SERVER, SPPaintServerClass)) #define SP_IS_PAINT_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_PAINT_SERVER)) #define SP_IS_PAINT_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_PAINT_SERVER)) struct SPPaintServer : public SPObject { +protected: + bool swatch; +public: + + static GType get_type(void); + bool isSwatch() const; bool isSolid() const; + +private: + static void init(SPPaintServer *ps); }; struct SPPaintServerClass { @@ -39,8 +48,6 @@ struct SPPaintServerClass { cairo_pattern_t *(*pattern_new)(SPPaintServer *ps, cairo_t *ct, const NRRect *bbox, double opacity); }; -GType sp_paint_server_get_type (void); - cairo_pattern_t *sp_paint_server_create_pattern(SPPaintServer *ps, cairo_t *ct, NRRect const *bbox, double opacity); class SPPaintServerReference : public Inkscape::URIReference { diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index c6826bfb1..76ef7f73d 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -1204,49 +1204,55 @@ static void sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCa for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { if (snapprefs->getSnapToItemNode()) { + // Add the first point of the path p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve - while (curve_it2 != path_it->end_closed()) + while (curve_it1 != path_it->end_default()) { - /* Test whether to add the node between curve_it1 and curve_it2. - * Loop to end_closed (so always including closing segment); the last node to be added - * is the node between the closing segment and the segment before that, regardless - * of the path being closed or not. If the path is closed, the final point was already added by - * adding the initial point. */ - - Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2); - - bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE); - bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM); - - if (c1 || c2) { - Inkscape::SnapSourceType sst; - Inkscape::SnapTargetType stt; - switch (nodetype) { - case Geom::NODE_CUSP: - sst = Inkscape::SNAPSOURCE_NODE_CUSP; - stt = Inkscape::SNAPTARGET_NODE_CUSP; - break; - case Geom::NODE_SMOOTH: - case Geom::NODE_SYMM: - sst = Inkscape::SNAPSOURCE_NODE_SMOOTH; - stt = Inkscape::SNAPTARGET_NODE_SMOOTH; - break; - default: - sst = Inkscape::SNAPSOURCE_UNDEFINED; - stt = Inkscape::SNAPTARGET_UNDEFINED; - break; + // For each path: consider midpoints of line segments for snapping + if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforces strict snapping) + if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) { + p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); } - p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt)); } - // Consider midpoints of line segments for snapping - if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) - if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) { - p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); + if (curve_it2 == path_it->end_default()) { // Test will only pass for the last iteration of the while loop + if (snapprefs->getSnapToItemNode() && !path_it->closed()) { + // Add the last point of the path, but only for open paths + // (for closed paths the first and last point will coincide) + p.push_back(Inkscape::SnapCandidatePoint((*curve_it1).finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); + } + } else { + /* Test whether to add the node between curve_it1 and curve_it2. + * Loop to end_default (so only iterating through the stroked part); */ + + Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2); + + bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE); + bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM); + + if (c1 || c2) { + Inkscape::SnapSourceType sst; + Inkscape::SnapTargetType stt; + switch (nodetype) { + case Geom::NODE_CUSP: + sst = Inkscape::SNAPSOURCE_NODE_CUSP; + stt = Inkscape::SNAPTARGET_NODE_CUSP; + break; + case Geom::NODE_SMOOTH: + case Geom::NODE_SYMM: + sst = Inkscape::SNAPSOURCE_NODE_SMOOTH; + stt = Inkscape::SNAPTARGET_NODE_SMOOTH; + break; + default: + sst = Inkscape::SNAPSOURCE_UNDEFINED; + stt = Inkscape::SNAPTARGET_UNDEFINED; + break; + } + p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, sst, stt)); } } diff --git a/src/sp-stop.cpp b/src/sp-stop.cpp index 740cfef78..71f937927 100644 --- a/src/sp-stop.cpp +++ b/src/sp-stop.cpp @@ -15,7 +15,7 @@ #include "sp-stop.h" - +#include "style.h" // A stop might have some non-stop siblings SPStop* SPStop::getNextStop() @@ -52,6 +52,34 @@ SPStop* SPStop::getPrevStop() return result; } +SPColor SPStop::readStopColor( Glib::ustring const &styleStr, guint32 dfl ) +{ + SPColor color(dfl); + SPStyle* style = sp_style_new(0); + SPIPaint paint; + paint.read( styleStr.c_str(), *style ); + if ( paint.isColor() ) { + color = paint.value.color; + } + sp_style_unref(style); + return color; +} + +SPColor SPStop::getEffectiveColor() const +{ + SPColor ret; + if (currentColor) { + char const *str = sp_object_get_style_property(this, "color", NULL); + /* Default value: arbitrarily black. (SVG1.1 and CSS2 both say that the initial + * value depends on user agent, and don't give any further restrictions that I can + * see.) */ + ret = readStopColor( str, 0 ); + } else { + ret = specified_color; + } + return ret; +} + /* diff --git a/src/sp-stop.h b/src/sp-stop.h index bf6893db1..2cf8ad690 100644 --- a/src/sp-stop.h +++ b/src/sp-stop.h @@ -9,6 +9,7 @@ */ #include <glib/gtypes.h> +#include <glibmm/ustring.h> #include "sp-object.h" #include "color.h" @@ -43,8 +44,12 @@ struct SPStop : public SPObject { gfloat opacity; + static SPColor readStopColor( Glib::ustring const &styleStr, guint32 dfl = 0 ); + SPStop* getNextStop(); SPStop* getPrevStop(); + + SPColor getEffectiveColor() const; }; /// The SPStop vtable. diff --git a/src/sp-text.cpp b/src/sp-text.cpp index dd0225ac4..c6071bf0f 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -527,7 +527,7 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio } else if (SP_IS_TSPAN(root)) { SPTSpan *tspan = SP_TSPAN(root); - // x, y attributes are stripped from some tspans as we do our own line layout + // x, y attributes are stripped from some tspans marked with role="line" as we do our own line layout. // This should be checked carefully, as it can undo line layout in imported SVG files. bool use_xy = !in_textpath && (tspan->role == SP_TSPAN_ROLE_UNSPECIFIED || !tspan->attributes.singleXYCoordinates()); tspan->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, true); diff --git a/src/style.cpp b/src/style.cpp index 2b5804671..a4094621f 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -1,5 +1,3 @@ -#define __SP_STYLE_C__ - /** @file * @brief SVG stylesheets implementation. */ @@ -28,7 +26,6 @@ #include "svg/svg.h" #include "svg/svg-color.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #include "display/canvas-bpath.h" #include "attributes.h" @@ -86,7 +83,6 @@ static void sp_style_read_ilength(SPILength *val, gchar const *str); static void sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str); static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str); static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); -static void sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str); static void sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str); static void sp_style_read_ifilter(gchar const *str, SPStyle *style, SPDocument *document); @@ -489,11 +485,11 @@ sp_style_new_from_object(SPObject *object) g_return_val_if_fail(object != NULL, NULL); g_return_val_if_fail(SP_IS_OBJECT(object), NULL); - SPStyle *style = sp_style_new(SP_OBJECT_DOCUMENT(object)); + SPStyle *style = sp_style_new( object->document ); style->object = object; style->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_style_object_release), style)); - if (object && SP_OBJECT_IS_CLONED(object)) { + if (object && object->cloned) { style->cloned = true; } @@ -575,7 +571,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) sp_style_clear(style); - if (object && SP_OBJECT_IS_CLONED(object)) { + if (object && object->cloned) { style->cloned = true; } @@ -663,7 +659,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) if (!style->fill.set) { val = repr->attribute("fill"); if (val) { - sp_style_read_ipaint(&style->fill, val, style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL); + style->fill.read( val, *style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL ); } } /* fill-opacity */ @@ -679,7 +675,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) if (!style->stroke.set) { val = repr->attribute("stroke"); if (val) { - sp_style_read_ipaint(&style->stroke, val, style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL); + style->stroke.read( val, *style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL ); } } SPS_READ_PLENGTH_IF_UNSET(&style->stroke_width, repr, "stroke-width"); @@ -1084,7 +1080,7 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) } case SP_PROP_FILL: if (!style->fill.set) { - sp_style_read_ipaint(&style->fill, val, style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL); + style->fill.read( val, *style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL ); } break; case SP_PROP_FILL_OPACITY: @@ -1151,7 +1147,7 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) case SP_PROP_STROKE: if (!style->stroke.set) { - sp_style_read_ipaint(&style->stroke, val, style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL); + style->stroke.read( val, *style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL ); } break; case SP_PROP_STROKE_WIDTH: @@ -3201,18 +3197,17 @@ sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume * * \pre paint == \&style.fill || paint == \&style.stroke. */ -static void -sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document) +void SPIPaint::read( gchar const *str, SPStyle &style, SPDocument *document ) { while (g_ascii_isspace(*str)) { ++str; } - paint->clear(); + clear(); if (streq(str, "inherit")) { - paint->set = TRUE; - paint->inherit = TRUE; + set = TRUE; + inherit = TRUE; } else { if ( strneq(str, "url", 3) ) { gchar *uri = extract_uri( str, &str ); @@ -3220,33 +3215,33 @@ sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume ++str; } // TODO check on and comment the comparrison "paint != &style->color". - if ( uri && *uri && (paint != &style->color) ) { - paint->set = TRUE; + if ( uri && *uri && (this != &style.color) ) { + set = TRUE; // it may be that this style's SPIPaint has not yet created its URIReference; // now that we have a document, we can create it here - if (!paint->value.href && document) { - paint->value.href = new SPPaintServerReference(document); - paint->value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((paint == &style->fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), style)); + if (!value.href && document) { + value.href = new SPPaintServerReference(document); + value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((this == &style.fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), &style)); } // TODO check what this does in light of move away from union - sp_style_set_ipaint_to_uri_string (style, paint, uri); + sp_style_set_ipaint_to_uri_string (&style, this, uri); } g_free( uri ); } - if (streq(str, "currentColor") && paint != &style->color) { - paint->set = TRUE; - paint->currentcolor = TRUE; - } else if (streq(str, "none") && paint != &style->color) { - paint->set = TRUE; - paint->noneSet = TRUE; + if (streq(str, "currentColor") && (this != &style.color)) { + set = TRUE; + currentcolor = TRUE; + } else if (streq(str, "none") && (this != &style.color)) { + set = TRUE; + noneSet = TRUE; } else { guint32 const rgb0 = sp_svg_read_color(str, &str, 0xff); if (rgb0 != 0xff) { - paint->setColor( rgb0 ); - paint->set = TRUE; + setColor( rgb0 ); + set = TRUE; while (g_ascii_isspace(*str)) { ++str; @@ -3257,18 +3252,7 @@ sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume delete tmp; tmp = 0; } - paint->value.color.icc = tmp; - } - if (strneq(str, "device-gray(", 12) || - strneq(str, "device-rgb(", 11) || - strneq(str, "device-cmyk(", 12) || - strneq(str, "device-nchannel(", 16)) { - SVGDeviceColor* tmp = new SVGDeviceColor(); - if ( ! sp_svg_read_device_color( str, &str, tmp ) ) { - delete tmp; - tmp = 0; - } - paint->value.color.device = tmp; + value.color.icc = tmp; } } } @@ -4037,6 +4021,17 @@ sp_style_write_ifilter(gchar *p, gint const len, gchar const *key, return 0; } +SPIPaint::SPIPaint() : + set(0), + inherit(0), + currentcolor(0), + colorSet(0), + noneSet(0), + value() +{ + value.color.set( 0 ); + value.href = 0; +} void SPIPaint::clear() { diff --git a/src/style.h b/src/style.h index 02ef41bf8..f937fe33b 100644 --- a/src/style.h +++ b/src/style.h @@ -144,8 +144,6 @@ struct SPILength { #define SP_STYLE_FILL_SERVER(s) (((SPStyle *) (s))->getFillPaintServer()) #define SP_STYLE_STROKE_SERVER(s) (((SPStyle *) (s))->getStrokePaintServer()) -#define SP_OBJECT_STYLE_FILL_SERVER(o) (SP_OBJECT (o)->style->getFillPaintServer()) -#define SP_OBJECT_STYLE_STROKE_SERVER(o) (SP_OBJECT (o)->style->getStrokePaintServer()) class SVGICCColor; @@ -161,6 +159,7 @@ struct SPIPaint { SPColor color; } value; + SPIPaint(); bool isSet() const { return true; /* set || colorSet*/} bool isSameType( SPIPaint const & other ) const {return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor);} @@ -176,6 +175,15 @@ struct SPIPaint { void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} void setColor( SPColor const& color ) {value.color = color; colorSet = true;} + + void read( gchar const *str, SPStyle &tyle, SPDocument *document = 0); + + // Win32 is a temp work-around until the emf extension is fixed: +#ifndef WIN32 +private: + SPIPaint(SPIPaint const&); + SPIPaint &operator=(SPIPaint const &); +#endif // WIN32 }; /// Filter type internal to SPStyle diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index c27b2291c..265210a45 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -14,7 +14,6 @@ ink_common_sources += \ svg/svg-affine.cpp \ svg/svg-color.cpp \ svg/svg-color.h \ - svg/svg-device-color.h \ svg/svg-icc-color.h \ svg/svg-length.cpp \ svg/svg-length.h \ diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 7d43b68ee..04f387798 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -35,7 +35,6 @@ #include "preferences.h" #include "svg-color.h" #include "svg-icc-color.h" -#include "svg-device-color.h" #if ENABLE_LCMS #include <lcms.h> @@ -606,110 +605,6 @@ bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ) return sp_svg_read_icc_color(str, NULL, dest); } -bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest) -{ - bool good = true; - unsigned int max_colors; - - if ( end_ptr ) { - *end_ptr = str; - } - if ( dest ) { - dest->colors.clear(); - } - - if ( !str ) { - // invalid input - good = false; - } else { - while ( g_ascii_isspace(*str) ) { - str++; - } - - dest->type = DEVICE_COLOR_INVALID; - if (strneq( str, "device-gray(", 12 )){ - dest->type = DEVICE_GRAY; - max_colors=1; - str += 12; - } - - if (strneq( str, "device-rgb(", 11 )){ - dest->type = DEVICE_RGB; - max_colors=3; - str += 11; - } - - if (strneq( str, "device-cmyk(", 12 )){ - dest->type = DEVICE_CMYK; - max_colors=4; - str += 12; - } - - if (strneq( str, "device-nchannel(", 16 )){ - dest->type = DEVICE_NCHANNEL; - max_colors=0; - str += 16; - } - - if ( dest->type != DEVICE_COLOR_INVALID ) { - while ( g_ascii_isspace(*str) ) { - str++; - } - - while ( *str && *str != ')' ) { - if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') { - gchar* endPtr = 0; - gdouble dbl = g_ascii_strtod( str, &endPtr ); - if ( !errno ) { - if ( dest ) { - dest->colors.push_back( dbl ); - } - str = endPtr; - } else { - good = false; - break; - } - - while ( g_ascii_isspace(*str) || *str == ',' ) { - str++; - } - } else { - break; - } - } - } - - // We need to have ended on a closing parenthesis - if ( good ) { - while ( g_ascii_isspace(*str) ) { - str++; - } - good &= (*str == ')'); - } - } - - if ( dest->colors.size() == 0) good=false; - if ( dest->type != DEVICE_NCHANNEL && (dest->colors.size() != max_colors)) good=false; - - if ( good ) { - if ( end_ptr ) { - *end_ptr = str; - } - } else { - if ( dest ) { - dest->type = DEVICE_COLOR_INVALID; - dest->colors.clear(); - } - } - - return good; -} - - -bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest) -{ - return sp_svg_read_device_color(str, NULL, dest); -} /* Local Variables: diff --git a/src/svg/svg-color.h b/src/svg/svg-color.h index f4e534652..a3868c149 100644 --- a/src/svg/svg-color.h +++ b/src/svg/svg-color.h @@ -4,7 +4,6 @@ #include <glib/gtypes.h> class SVGICCColor; -class SVGDeviceColor; guint32 sp_svg_read_color(gchar const *str, unsigned int dfl); guint32 sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def); @@ -12,8 +11,6 @@ void sp_svg_write_color(char *buf, unsigned int buflen, unsigned int rgba32); bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest ); bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ); -bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest ); -bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest ); void icc_color_to_sRGB(SVGICCColor* dest, guchar* r, guchar* g, guchar* b); #endif /* !SVG_SVG_COLOR_H_SEEN */ diff --git a/src/svg/svg-device-color.h b/src/svg/svg-device-color.h deleted file mode 100644 index 305133ed3..000000000 --- a/src/svg/svg-device-color.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef SVG_DEVICE_COLOR_H_SEEN -#define SVG_DEVICE_COLOR_H_SEEN - -#include <string> -#include <vector> - -typedef enum {DEVICE_COLOR_INVALID, DEVICE_GRAY, DEVICE_RGB, DEVICE_CMYK, DEVICE_NCHANNEL} SVGDeviceColorType; - -struct SVGDeviceColor { - SVGDeviceColorType type; - std::vector<double> colors; -}; - - -#endif /* !SVG_DEVICE_COLOR_H_SEEN */ - -/* - 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:encoding=utf-8:textwidth=99 : diff --git a/src/trace/quantize.cpp b/src/trace/quantize.cpp index 8acfe0c34..2db1bbf34 100644 --- a/src/trace/quantize.cpp +++ b/src/trace/quantize.cpp @@ -545,44 +545,48 @@ IndexedMap *rgbMapQuantize(RgbMap *rgbmap, int ncolor) assert(rgbmap); assert(ncolor > 0); + IndexedMap *newmap = 0; + pool<Ocnode> pool; - Ocnode *tree; + Ocnode *tree = 0; try { - tree = octreeBuild(&pool, rgbmap, ncolor); + tree = octreeBuild(&pool, rgbmap, ncolor); } - catch (std::bad_alloc& ex) { - return NULL; //should do smthg else? + catch (std::bad_alloc &ex) { + //should do smthg else? } - RGB *rgbpal = new RGB[ncolor]; - int indexes = 0; - octreeIndex(tree, rgbpal, &indexes); - - octreeDelete(&pool, tree); + if ( tree ) { + RGB *rgbpal = new RGB[ncolor]; + int indexes = 0; + octreeIndex(tree, rgbpal, &indexes); - // stacking with increasing contrasts - qsort((void *)rgbpal, indexes, sizeof(RGB), compRGB); + octreeDelete(&pool, tree); - // make the new map - IndexedMap *newmap = IndexedMapCreate(rgbmap->width, rgbmap->height); - if (!newmap) { delete rgbpal; return NULL; } + // stacking with increasing contrasts + qsort((void *)rgbpal, indexes, sizeof(RGB), compRGB); - // fill in the color lookup table - for (int i = 0; i < indexes; i++) newmap->clut[i] = rgbpal[i]; - newmap->nrColors = indexes; - - // fill in new map pixels - for (int y = 0; y < rgbmap->height; y++) - { - for (int x = 0; x < rgbmap->width; x++) - { - RGB rgb = rgbmap->getPixel(rgbmap, x, y); - int index = findRGB(rgbpal, ncolor, rgb); - newmap->setPixel(newmap, x, y, index); + // make the new map + newmap = IndexedMapCreate(rgbmap->width, rgbmap->height); + if (newmap) { + // fill in the color lookup table + for (int i = 0; i < indexes; i++) { + newmap->clut[i] = rgbpal[i]; + } + newmap->nrColors = indexes; + + // fill in new map pixels + for (int y = 0; y < rgbmap->height; y++) { + for (int x = 0; x < rgbmap->width; x++) { + RGB rgb = rgbmap->getPixel(rgbmap, x, y); + int index = findRGB(rgbpal, ncolor, rgb); + newmap->setPixel(newmap, x, y, index); + } } } + delete[] rgbpal; + } - delete rgbpal; return newmap; } diff --git a/src/tweak-context.cpp b/src/tweak-context.cpp index 6e9ec6fb4..d49c75a6d 100644 --- a/src/tweak-context.cpp +++ b/src/tweak-context.cpp @@ -834,20 +834,24 @@ tweak_colors_in_gradient (SPItem *item, bool fill_or_stroke, // Normalize pos to 0..1, taking into accound gradient spread: double pos_e = pos; - if (gradient->spread == SP_GRADIENT_SPREAD_PAD) { - if (pos > 1) + if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) { + if (pos > 1) { pos_e = 1; - if (pos < 0) + } + if (pos < 0) { pos_e = 0; - } else if (gradient->spread == SP_GRADIENT_SPREAD_REPEAT) { - if (pos > 1 || pos < 0) + } + } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) { + if (pos > 1 || pos < 0) { pos_e = pos - floor(pos); - } else if (gradient->spread == SP_GRADIENT_SPREAD_REFLECT) { + } + } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REFLECT) { if (pos > 1 || pos < 0) { bool odd = ((int)(floor(pos)) % 2 == 1); pos_e = pos - floor(pos); - if (odd) + if (odd) { pos_e = 1 - pos_e; + } } } diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index 9ce2ac5ba..0a64e7fa7 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -653,23 +653,23 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) { // copy fill and stroke styles (patterns and gradients) - SPStyle *style = SP_OBJECT_STYLE(item); + SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server) ) { _copyGradient(SP_GRADIENT(server)); } - if (SP_IS_PATTERN(server)) { + if ( SP_IS_PATTERN(server) ) { _copyPattern(SP_PATTERN(server)); } } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server) ) { _copyGradient(SP_GRADIENT(server)); } - if (SP_IS_PATTERN(server)) { + if ( SP_IS_PATTERN(server) ) { _copyPattern(SP_PATTERN(server)); } } diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index a75a8d68d..048c46a20 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -518,8 +518,8 @@ public: guint row, guint column, AlignAndDistribute &dialog) : - Action(id, tiptext, row, column + 4, - dialog.graphLayout_table(), dialog.tooltips(), dialog) + Action(id, tiptext, row, column, + dialog.rearrange_table(), dialog.tooltips(), dialog) {} private : @@ -542,6 +542,101 @@ private : } }; +class ActionExchangePositions : public Action { +public: + enum SortOrder { + None, + ZOrder, + Clockwise + }; + + ActionExchangePositions(Glib::ustring const &id, + Glib::ustring const &tiptext, + guint row, + guint column, + AlignAndDistribute &dialog, SortOrder order = None) : + Action(id, tiptext, row, column, + dialog.rearrange_table(), dialog.tooltips(), dialog), + sortOrder(order) + {}; + + +private : + const SortOrder sortOrder; + static boost::optional<Geom::Point> center; + + static bool sort_compare(const SPItem * a,const SPItem * b) { + if (a == NULL) return false; + if (b == NULL) return true; + if (center) { + Geom::Point point_a = a->getCenter() - (*center); + Geom::Point point_b = b->getCenter() - (*center); + // First criteria: Sort according to the angle to the center point + double angle_a = atan2(double(point_a[Geom::Y]), double(point_a[Geom::X])); + double angle_b = atan2(double(point_b[Geom::Y]), double(point_b[Geom::X])); + if (angle_a != angle_b) return (angle_a < angle_b); + // Second criteria: Sort according to the distance the center point + Geom::Coord length_a = point_a.length(); + Geom::Coord length_b = point_b.length(); + if (length_a != length_b) return (length_a > length_b); + } + // Last criteria: Sort according to the z-coordinate + return (a->isSiblingOf(b)); + } + + virtual void on_button_click() + { + SPDesktop *desktop = _dialog.getDesktop(); + if (!desktop) return; + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + if (!selection) return; + + using Inkscape::Util::GSListConstIterator; + std::list<SPItem *> selected; + selected.insert<GSListConstIterator<SPItem *> >(selected.end(), selection->itemList(), NULL); + if (selected.empty()) return; + + //Check 2 or more selected objects + if (selected.size() < 2) return; + + // see comment in ActionAlign above + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); + prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED); + + // sort the list + if (sortOrder != None) { + if (sortOrder == Clockwise) { + center = selection->center(); + } else { // sorting by ZOrder is outomatically done by not setting the center + center.reset(); + } + selected.sort(ActionExchangePositions::sort_compare); + } + std::list<SPItem *>::iterator it(selected.begin()); + Geom::Point p1 = (*it)->getCenter(); + for (++it ;it != selected.end(); ++it) + { + Geom::Point p2 = (*it)->getCenter(); + Geom::Point delta = p1 - p2; + sp_item_move_rel((*it),Geom::Translate(delta[Geom::X],delta[Geom::Y] )); + p1 = p2; + } + Geom::Point p2 = selected.front()->getCenter(); + Geom::Point delta = p1 - p2; + sp_item_move_rel(selected.front(),Geom::Translate(delta[Geom::X],delta[Geom::Y] )); + + // restore compensation setting + prefs->setInt("/options/clonecompensation/value", saved_compensation); + + sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, + _("Exchange Positions")); + } +}; +// instantiae the private static member +boost::optional<Geom::Point> ActionExchangePositions::center; + class ActionUnclump : public Action { public : ActionUnclump(const Glib::ustring &id, @@ -550,7 +645,7 @@ public : guint column, AlignAndDistribute &dialog): Action(id, tiptext, row, column, - dialog.distribute_table(), dialog.tooltips(), dialog) + dialog.rearrange_table(), dialog.tooltips(), dialog) {} private : @@ -581,7 +676,7 @@ public : guint column, AlignAndDistribute &dialog): Action(id, tiptext, row, column, - dialog.distribute_table(), dialog.tooltips(), dialog) + dialog.rearrange_table(), dialog.tooltips(), dialog) {} private : @@ -792,13 +887,13 @@ AlignAndDistribute::AlignAndDistribute() randomize_bbox(), _alignFrame(_("Align")), _distributeFrame(_("Distribute")), + _rearrangeFrame(_("Rearrange")), _removeOverlapFrame(_("Remove overlaps")), - _graphLayoutFrame(_("Connector network layout")), _nodesFrame(_("Nodes")), _alignTable(2, 6, true), - _distributeTable(3, 6, true), + _distributeTable(2, 6, true), + _rearrangeTable(1, 5, false), _removeOverlapTable(1, 5, false), - _graphLayoutTable(1, 5, false), _nodesTable(1, 4, true), _anchorLabel(_("Relative to: ")), _selgrpLabel(_("Treat selection as group: ")) @@ -882,22 +977,33 @@ AlignAndDistribute::AlignAndDistribute() _("Distribute baselines of texts vertically"), 1, 5, this->distribute_table(), Geom::Y, true); + // Rearrange + //Graph Layout + addGraphLayoutButton(INKSCAPE_ICON_DISTRIBUTE_GRAPH, + _("Nicely arrange selected connector network"), + 0, 0); + addExchangePositionsButton(INKSCAPE_ICON_EXCHANGE_POSITIONS, + _("Exchange positions of selected objects - selection order"), + 0, 1); + addExchangePositionsByZOrderButton(INKSCAPE_ICON_EXCHANGE_POSITIONS_ZORDER, + _("Exchange positions of selected objects - stacking order"), + 0, 2); + addExchangePositionsClockwiseButton(INKSCAPE_ICON_EXCHANGE_POSITIONS_CLOCKWISE, + _("Exchange positions of selected objects - clockwise rotate"), + 0, 3); + //Randomize & Unclump addRandomizeButton(INKSCAPE_ICON_DISTRIBUTE_RANDOMIZE, _("Randomize centers in both dimensions"), - 2, 2); + 0, 4); addUnclumpButton(INKSCAPE_ICON_DISTRIBUTE_UNCLUMP, _("Unclump objects: try to equalize edge-to-edge distances"), - 2, 4); + 0, 5); //Remove overlaps addRemoveOverlapsButton(INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS, _("Move objects as little as possible so that their bounding boxes do not overlap"), 0, 0); - //Graph Layout - addGraphLayoutButton(INKSCAPE_ICON_DISTRIBUTE_GRAPH, - _("Nicely arrange selected connector network"), - 0, 0); //Node Mode buttons // NOTE: "align nodes vertically" means "move nodes vertically until they align on a common @@ -943,8 +1049,8 @@ AlignAndDistribute::AlignAndDistribute() _alignFrame.add(_alignBox); _distributeFrame.add(_distributeTable); + _rearrangeFrame.add(_rearrangeTable); _removeOverlapFrame.add(_removeOverlapTable); - _graphLayoutFrame.add(_graphLayoutTable); _nodesFrame.add(_nodesTable); Gtk::Box *contents = _getContents(); @@ -954,8 +1060,8 @@ AlignAndDistribute::AlignAndDistribute() contents->pack_start(_alignFrame, true, true); contents->pack_start(_distributeFrame, true, true); + contents->pack_start(_rearrangeFrame, true, true); contents->pack_start(_removeOverlapFrame, true, true); - contents->pack_start(_graphLayoutFrame, true, true); contents->pack_start(_nodesFrame, true, true); //Connect to the global tool change signal @@ -1010,8 +1116,8 @@ void AlignAndDistribute::setMode(bool nodeEdit) ((_alignFrame).*(mSel))(); ((_distributeFrame).*(mSel))(); + ((_rearrangeFrame).*(mSel))(); ((_removeOverlapFrame).*(mSel))(); - ((_graphLayoutFrame).*(mSel))(); ((_nodesFrame).*(mNode))(); } @@ -1063,6 +1169,33 @@ void AlignAndDistribute::addGraphLayoutButton(const Glib::ustring &id, const Gli ); } +void AlignAndDistribute::addExchangePositionsButton(const Glib::ustring &id, const Glib::ustring tiptext, + guint row, guint col) +{ + _actionList.push_back( + new ActionExchangePositions( + id, tiptext, row, col, *this) + ); +} + +void AlignAndDistribute::addExchangePositionsByZOrderButton(const Glib::ustring &id, const Glib::ustring tiptext, + guint row, guint col) +{ + _actionList.push_back( + new ActionExchangePositions( + id, tiptext, row, col, *this, ActionExchangePositions::ZOrder) + ); +} + +void AlignAndDistribute::addExchangePositionsClockwiseButton(const Glib::ustring &id, const Glib::ustring tiptext, + guint row, guint col) +{ + _actionList.push_back( + new ActionExchangePositions( + id, tiptext, row, col, *this, ActionExchangePositions::Clockwise) + ); +} + void AlignAndDistribute::addUnclumpButton(const Glib::ustring &id, const Glib::ustring tiptext, guint row, guint col) { diff --git a/src/ui/dialog/align-and-distribute.h b/src/ui/dialog/align-and-distribute.h index 297b3d2a1..f55998385 100644 --- a/src/ui/dialog/align-and-distribute.h +++ b/src/ui/dialog/align-and-distribute.h @@ -55,8 +55,8 @@ public: Gtk::Table &align_table(){return _alignTable;} Gtk::Table &distribute_table(){return _distributeTable;} + Gtk::Table &rearrange_table(){return _rearrangeTable;} Gtk::Table &removeOverlap_table(){return _removeOverlapTable;} - Gtk::Table &graphLayout_table(){return _graphLayoutTable;} Gtk::Table &nodes_table(){return _nodesTable;} Gtk::Tooltips &tooltips(){return _tooltips;} @@ -82,6 +82,15 @@ protected: void addGraphLayoutButton(const Glib::ustring &id, const Glib::ustring tiptext, guint row, guint col); + void addExchangePositionsButton(const Glib::ustring &id, + const Glib::ustring tiptext, + guint row, guint col); + void addExchangePositionsByZOrderButton(const Glib::ustring &id, + const Glib::ustring tiptext, + guint row, guint col); + void addExchangePositionsClockwiseButton(const Glib::ustring &id, + const Glib::ustring tiptext, + guint row, guint col); void addUnclumpButton(const Glib::ustring &id, const Glib::ustring tiptext, guint row, guint col); void addRandomizeButton(const Glib::ustring &id, const Glib::ustring tiptext, @@ -90,8 +99,8 @@ protected: guint row, guint col, Gtk::Table &table, Geom::Dim2 orientation, bool distribute); std::list<Action *> _actionList; - Gtk::Frame _alignFrame, _distributeFrame, _removeOverlapFrame, _graphLayoutFrame, _nodesFrame; - Gtk::Table _alignTable, _distributeTable, _removeOverlapTable, _graphLayoutTable, _nodesTable; + Gtk::Frame _alignFrame, _distributeFrame, _rearrangeFrame, _removeOverlapFrame, _nodesFrame; + Gtk::Table _alignTable, _distributeTable, _rearrangeTable, _removeOverlapTable, _nodesTable; Gtk::HBox _anchorBox; Gtk::HBox _selgrpBox; Gtk::VBox _alignBox; diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 6d3bc817e..aab9d89d7 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -41,7 +41,7 @@ #include "ui/dialog/icon-preview.h" #include "ui/dialog/floating-behavior.h" #include "ui/dialog/dock-behavior.h" -#include "ui/dialog/print-colors-preview-dialog.h" +//#include "ui/dialog/print-colors-preview-dialog.h" #include "preferences.h" #ifdef ENABLE_SVG_FONTS @@ -104,7 +104,7 @@ DialogManager::DialogManager() { registerFactory("LivePathEffect", &create<LivePathEffectEditor, FloatingBehavior>); registerFactory("Memory", &create<Memory, FloatingBehavior>); registerFactory("Messages", &create<Messages, FloatingBehavior>); - registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, FloatingBehavior>); +// registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, FloatingBehavior>); registerFactory("Script", &create<ScriptDialog, FloatingBehavior>); #ifdef ENABLE_SVG_FONTS registerFactory("SvgFontsDialog", &create<SvgFontsDialog, FloatingBehavior>); @@ -132,7 +132,7 @@ DialogManager::DialogManager() { registerFactory("LivePathEffect", &create<LivePathEffectEditor, DockBehavior>); registerFactory("Memory", &create<Memory, DockBehavior>); registerFactory("Messages", &create<Messages, DockBehavior>); - registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, DockBehavior>); +// registerFactory("PrintColorsPreviewDialog", &create<PrintColorsPreviewDialog, DockBehavior>); registerFactory("Script", &create<ScriptDialog, DockBehavior>); #ifdef ENABLE_SVG_FONTS registerFactory("SvgFontsDialog", &create<SvgFontsDialog, DockBehavior>); diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 4ec719bf4..dab42d897 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -176,7 +176,7 @@ public: if (tip_text) { _tt.set_tip(*this, tip_text); } - combo = new ComboBoxEnum<T>(default_value, c, a); + combo = new ComboBoxEnum<T>(default_value, c, a, false); add(*combo); show_all(); } @@ -2177,13 +2177,14 @@ void FilterEffectsDialog::init_settings_widgets() _settings->type(NR_FILTER_COMPONENTTRANSFER); _settings->add_notimplemented(); + /* //TRANSLATORS: for info on "Slope" and "Intercept", see http://id.mind.net/~zona/mmts/functionInstitute/linearFunctions/lsif.html - /*_settings->add_combo(COMPONENTTRANSFER_TYPE_IDENTITY, SP_ATTR_TYPE, _("Type"), ComponentTransferTypeConverter); - _ct_slope = _settings->add_spinslider(SP_ATTR_SLOPE, _("Slope"), -100, 100, 1, 0.01, 1); - _ct_intercept = _settings->add_spinslider(SP_ATTR_INTERCEPT, _("Intercept"), -100, 100, 1, 0.01, 1); - _ct_amplitude = _settings->add_spinslider(SP_ATTR_AMPLITUDE, _("Amplitude"), 0, 100, 1, 0.01, 1); - _ct_exponent = _settings->add_spinslider(SP_ATTR_EXPONENT, _("Exponent"), 0, 100, 1, 0.01, 1); - _ct_offset = _settings->add_spinslider(SP_ATTR_OFFSET, _("Offset"), -100, 100, 1, 0.01, 1);*/ + _settings->add_combo(COMPONENTTRANSFER_TYPE_IDENTITY, SP_ATTR_TYPE, _("Type"), ComponentTransferTypeConverter); + _ct_slope = _settings->add_spinslider(1, SP_ATTR_SLOPE, _("Slope"), -10, 10, 0.1, 0.01, 2); + _ct_intercept = _settings->add_spinslider(0, SP_ATTR_INTERCEPT, _("Intercept"), -10, 10, 0.1, 0.01, 2); + _ct_amplitude = _settings->add_spinslider(1, SP_ATTR_AMPLITUDE, _("Amplitude"), 0, 10, 0.1, 0.01, 2); + _ct_exponent = _settings->add_spinslider(1, SP_ATTR_EXPONENT, _("Exponent"), 0, 10, 0.1, 0.01, 2); + _ct_offset = _settings->add_spinslider(0, SP_ATTR_OFFSET, _("Offset"), -10, 10, 0.1, 0.01, 2);*/ _settings->type(NR_FILTER_COMPOSITE); _settings->add_combo(COMPOSITE_OVER, SP_ATTR_OPERATOR, _("Operator:"), CompositeOperatorConverter); @@ -2205,8 +2206,8 @@ void FilterEffectsDialog::init_settings_widgets() _settings->type(NR_FILTER_DIFFUSELIGHTING); _settings->add_color(/*default: white*/ 0xffffffff, SP_PROP_LIGHTING_COLOR, _("Diffuse Color:"), _("Defines the color of the light source")); - _settings->add_spinslider(1, SP_ATTR_SURFACESCALE, _("Surface Scale:"), -1000, 1000, 1, 0.01, 1, _("This value amplifies the heights of the bump map defined by the input alpha channel")); - _settings->add_spinslider(1, SP_ATTR_DIFFUSECONSTANT, _("Constant:"), 0, 100, 0.1, 0.01, 2, _("This constant affects the Phong lighting model.")); + _settings->add_spinslider(1, SP_ATTR_SURFACESCALE, _("Surface Scale:"), -5, 5, 0.01, 0.001, 3, _("This value amplifies the heights of the bump map defined by the input alpha channel")); + _settings->add_spinslider(1, SP_ATTR_DIFFUSECONSTANT, _("Constant:"), 0, 5, 0.1, 0.01, 2, _("This constant affects the Phong lighting model.")); _settings->add_dualspinslider(SP_ATTR_KERNELUNITLENGTH, _("Kernel Unit Length:"), 0.01, 10, 1, 0.01, 1); _settings->add_lightsource(); @@ -2238,9 +2239,9 @@ void FilterEffectsDialog::init_settings_widgets() _settings->type(NR_FILTER_SPECULARLIGHTING); _settings->add_color(/*default: white*/ 0xffffffff, SP_PROP_LIGHTING_COLOR, _("Specular Color:"), _("Defines the color of the light source")); - _settings->add_spinslider(1, SP_ATTR_SURFACESCALE, _("Surface Scale:"), -1000, 1000, 1, 0.01, 1, _("This value amplifies the heights of the bump map defined by the input alpha channel")); - _settings->add_spinslider(1, SP_ATTR_SPECULARCONSTANT, _("Constant:"), 0, 100, 0.1, 0.01, 2, _("This constant affects the Phong lighting model.")); - _settings->add_spinslider(1, SP_ATTR_SPECULAREXPONENT, _("Exponent:"), 1, 128, 1, 0.01, 1, _("Exponent for specular term, larger is more \"shiny\".")); + _settings->add_spinslider(1, SP_ATTR_SURFACESCALE, _("Surface Scale:"), -5, 5, 0.1, 0.01, 2, _("This value amplifies the heights of the bump map defined by the input alpha channel")); + _settings->add_spinslider(1, SP_ATTR_SPECULARCONSTANT, _("Constant:"), 0, 5, 0.1, 0.01, 2, _("This constant affects the Phong lighting model.")); + _settings->add_spinslider(1, SP_ATTR_SPECULAREXPONENT, _("Exponent:"), 1, 50, 1, 0.01, 1, _("Exponent for specular term, larger is more \"shiny\".")); _settings->add_dualspinslider(SP_ATTR_KERNELUNITLENGTH, _("Kernel Unit Length:"), 0.01, 10, 1, 0.01, 1); _settings->add_lightsource(); diff --git a/src/ui/dialog/icon-preview.cpp b/src/ui/dialog/icon-preview.cpp index c7ed9f92b..02e8cda9c 100644 --- a/src/ui/dialog/icon-preview.cpp +++ b/src/ui/dialog/icon-preview.cpp @@ -41,6 +41,8 @@ sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root, const gchar *name, unsigned int psize, unsigned &stride); } +#define noICON_VERBOSE 1 + namespace Inkscape { namespace UI { namespace Dialog { @@ -85,7 +87,10 @@ IconPreviewPanel::IconPreviewPanel() : desktop(0), document(0), timer(0), + renderTimer(0), pending(false), + minDelay(0.1), + targetId(), hot(1), selectionButton(0), desktopChangeConn(), @@ -96,6 +101,8 @@ IconPreviewPanel::IconPreviewPanel() : Inkscape::Preferences *prefs = Inkscape::Preferences::get(); numEntries = 0; + bool pack = prefs->getBool("/iconpreview/pack", true); + std::vector<Glib::ustring> pref_sizes = prefs->getAllDirs("/iconpreview/sizes/default"); std::vector<int> rawSizes; @@ -169,10 +176,14 @@ IconPreviewPanel::IconPreviewPanel() : Glib::ustring label(*labels[i]); buttons[i] = new Gtk::ToggleToolButton(label); buttons[i]->set_active( i == hot ); - Gtk::Frame *frame = new Gtk::Frame(); - frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN); - frame->add(*images[i]); - buttons[i]->set_icon_widget(*Gtk::manage(frame)); + if ( prefs->getBool("/iconpreview/showFrames", true) ) { + Gtk::Frame *frame = new Gtk::Frame(); + frame->set_shadow_type(Gtk::SHADOW_ETCHED_IN); + frame->add(*images[i]); + buttons[i]->set_icon_widget(*Gtk::manage(frame)); + } else { + buttons[i]->set_icon_widget(*images[i]); + } tips.set_tip((*buttons[i]), label); @@ -183,7 +194,7 @@ IconPreviewPanel::IconPreviewPanel() : align->add(*buttons[i]); int pad = 12; - if ((avail == 0) && (previous == 0)) { + if ( !pack || ( (avail == 0) && (previous == 0) ) ) { verts->pack_end(*align, Gtk::PACK_SHRINK); previous = sizes[i]; avail = sizes[i]; @@ -243,6 +254,11 @@ IconPreviewPanel::~IconPreviewPanel() delete timer; timer = 0; } + if ( renderTimer ) { + renderTimer->stop(); + delete renderTimer; + renderTimer = 0; + } selChangedConn.disconnect(); docModConn.disconnect(); @@ -256,6 +272,22 @@ IconPreviewPanel::~IconPreviewPanel() //######################################################################### +#if ICON_VERBOSE +static Glib::ustring getTimestr() +{ + Glib::ustring str; + GTimeVal now = {0, 0}; + g_get_current_time(&now); + glong secs = now.tv_sec % 60; + glong mins = (now.tv_sec / 60) % 60; + gchar *ptr = g_strdup_printf(":%02ld:%02ld.%06ld", mins, secs, now.tv_usec); + str = ptr; + g_free(ptr); + ptr = 0; + return str; +} +#endif // ICON_VERBOSE + void IconPreviewPanel::setDesktop( SPDesktop* desktop ) { Panel::setDesktop(desktop); @@ -269,7 +301,7 @@ void IconPreviewPanel::setDesktop( SPDesktop* desktop ) this->desktop = Panel::getDesktop(); if ( this->desktop ) { docReplacedConn = this->desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(this, &IconPreviewPanel::setDocument))); - if (this->desktop->selection) { + if ( this->desktop->selection && Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) { selChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh))); } } @@ -285,7 +317,9 @@ void IconPreviewPanel::setDocument( SPDocument *document ) this->document = document; if (this->document) { - docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh))); + if ( Inkscape::Preferences::get()->getBool("/iconpreview/autoRefresh", true) ) { + docModConn = this->document->connectModified(sigc::hide(sigc::mem_fun(this, &IconPreviewPanel::queueRefresh))); + } queueRefresh(); } } @@ -297,38 +331,50 @@ void IconPreviewPanel::refreshPreview() if (!timer) { timer = new Glib::Timer(); } - if (timer->elapsed() < 0.1) { + if (timer->elapsed() < minDelay) { +#if ICON_VERBOSE + g_message( "%s Deferring refresh as too soon. calling queueRefresh()", getTimestr().c_str() ); +#endif //ICON_VERBOSE // Do not refresh too quickly queueRefresh(); } else if ( desktop ) { +#if ICON_VERBOSE + g_message( "%s Refreshing preview.", getTimestr().c_str() ); +#endif // ICON_VERBOSE + bool hold = Inkscape::Preferences::get()->getBool("/iconpreview/selectionHold", true); + SPObject *target = 0; if ( selectionButton && selectionButton->get_active() ) { - Inkscape::Selection * sel = sp_desktop_selection(desktop); - if ( sel ) { - //g_message("found a selection to play with"); - - GSList const *items = sel->itemList(); - SPObject *target = 0; - while ( items && !target ) { - SPItem* item = SP_ITEM( items->data ); - SPObject * obj = SP_OBJECT(item); - gchar const *id = obj->getId(); - if ( id ) { - target = obj; + target = (hold && !targetId.empty()) ? desktop->doc()->getObjectById( targetId.c_str() ) : 0; + if ( !target ) { + targetId.clear(); + Inkscape::Selection * sel = sp_desktop_selection(desktop); + if ( sel ) { + //g_message("found a selection to play with"); + + GSList const *items = sel->itemList(); + while ( items && !target ) { + SPItem* item = SP_ITEM( items->data ); + SPObject * obj = SP_OBJECT(item); + gchar const *id = obj->getId(); + if ( id ) { + targetId = id; + target = obj; + } + + items = g_slist_next(items); } - - items = g_slist_next(items); - } - if ( target ) { - renderPreview(target); } } } else { - SPObject *target = desktop->currentRoot(); - if ( target ) { - renderPreview(target); - } + target = desktop->currentRoot(); } + if ( target ) { + renderPreview(target); + } +#if ICON_VERBOSE + g_message( "%s resetting timer", getTimestr().c_str() ); +#endif // ICON_VERBOSE timer->reset(); } } @@ -339,9 +385,15 @@ bool IconPreviewPanel::refreshCB() if (!timer) { timer = new Glib::Timer(); } - if ( timer->elapsed() > 0.1 ) { + if ( timer->elapsed() > minDelay ) { +#if ICON_VERBOSE + g_message( "%s refreshCB() timer has progressed", getTimestr().c_str() ); +#endif // ICON_VERBOSE callAgain = false; refreshPreview(); +#if ICON_VERBOSE + g_message( "%s refreshCB() setting pending false", getTimestr().c_str() ); +#endif // ICON_VERBOSE pending = false; } return callAgain; @@ -351,6 +403,9 @@ void IconPreviewPanel::queueRefresh() { if (!pending) { pending = true; +#if ICON_VERBOSE + g_message( "%s queueRefresh() Setting pending true", getTimestr().c_str() ); +#endif // ICON_VERBOSE if (!timer) { timer = new Glib::Timer(); } @@ -361,7 +416,11 @@ void IconPreviewPanel::queueRefresh() void IconPreviewPanel::modeToggled() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/iconpreview/selectionOnly", (selectionButton && selectionButton->get_active())); + bool selectionOnly = (selectionButton && selectionButton->get_active()); + prefs->setBool("/iconpreview/selectionOnly", selectionOnly); + if ( !selectionOnly ) { + targetId.clear(); + } refreshPreview(); } @@ -370,8 +429,14 @@ void IconPreviewPanel::renderPreview( SPObject* obj ) { SPDocument * doc = SP_OBJECT_DOCUMENT(obj); gchar const * id = obj->getId(); + if ( !renderTimer ) { + renderTimer = new Glib::Timer(); + } + renderTimer->reset(); -// g_message(" setting up to render '%s' as the icon", id ); +#if ICON_VERBOSE + g_message("%s setting up to render '%s' as the icon", getTimestr().c_str(), id ); +#endif // ICON_VERBOSE NRArenaItem *root = NULL; @@ -402,6 +467,11 @@ void IconPreviewPanel::renderPreview( SPObject* obj ) sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), visionkey); nr_object_unref((NRObject *) arena); + renderTimer->stop(); + minDelay = std::max( 0.1, renderTimer->elapsed() * 3.0 ); +#if ICON_VERBOSE + g_message(" render took %f seconds.", renderTimer->elapsed()); +#endif // ICON_VERBOSE } void IconPreviewPanel::updateMagnify() diff --git a/src/ui/dialog/icon-preview.h b/src/ui/dialog/icon-preview.h index 9de882569..f8957086a 100644 --- a/src/ui/dialog/icon-preview.h +++ b/src/ui/dialog/icon-preview.h @@ -61,13 +61,16 @@ private: SPDesktop *desktop; SPDocument *document; Glib::Timer *timer; + Glib::Timer *renderTimer; bool pending; + gdouble minDelay; Gtk::Tooltips tips; Gtk::VBox iconBox; Gtk::HPaned splitter; + Glib::ustring targetId; int hot; int numEntries; int* sizes; diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 3c48a7972..8f5194eda 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -540,8 +540,9 @@ void InkscapePreferences::initPageTools() this->AddNewObjectsStyle(_page_eraser, "/tools/eraser"); //LPETool - this->AddPage(_page_lpetool, _("LPE Tool"), iter_tools, PREFS_PAGE_TOOLS_LPETOOL); - this->AddNewObjectsStyle(_page_lpetool, "/tools/lpetool"); + // commented out, because the LPETool is not finished yet. + //this->AddPage(_page_lpetool, _("LPE Tool"), iter_tools, PREFS_PAGE_TOOLS_LPETOOL); + //this->AddNewObjectsStyle(_page_lpetool, "/tools/lpetool"); //Text this->AddPage(_page_text, _("Text"), iter_tools, PREFS_PAGE_TOOLS_TEXT); @@ -1116,11 +1117,11 @@ void InkscapePreferences::initPageUI() _("Mongolian (mn)"), _("Nepali (ne)"), _("Norwegian BokmĂ„l (nb)"), _("Norwegian Nynorsk (nn)"), _("Panjabi (pa)"), _("Polish (pl)"), _("Portuguese (pt)"), _("Portuguese/Brazil (pt_BR)"), _("Romanian (ro)"), _("Russian (ru)"), _("Serbian (sr)"), _("Serbian in Latin script (sr@latin)"), _("Slovak (sk)"), _("Slovenian (sl)"), _("Spanish (es)"), _("Spanish/Mexico (es_MX)"), - _("Swedish (sv)"), _("Thai (th)"), _("Turkish (tr)"), _("Ukrainian (uk)"), _("Vietnamese (vi)")}; + _("Swedish (sv)"),_("Telugu (te_IN)"), _("Thai (th)"), _("Turkish (tr)"), _("Ukrainian (uk)"), _("Vietnamese (vi)")}; Glib::ustring langValues[] = {"", "sq", "am", "ar", "hy", "az", "eu", "be", "bg", "bn", "br", "ca", "ca@valencia", "zh_CN", "zh_TW", "hr", "cs", "da", "nl", "dz", "de", "el", "en", "en_AU", "en_CA", "en_GB", "en_US@piglatin", "eo", "et", "fa", "fi", "fr", "ga", "gl", "he", "hu", "id", "it", "ja", "km", "rw", "ko", "lt", "mk", "mn", "ne", "nb", "nn", "pa", - "pl", "pt", "pt_BR", "ro", "ru", "sr", "sr@latin", "sk", "sl", "es", "es_MX", "sv", "th", "tr", "uk", "vi" }; + "pl", "pt", "pt_BR", "ro", "ru", "sr", "sr@latin", "sk", "sl", "es", "es_MX", "sv", "te_IN", "th", "tr", "uk", "vi" }; _ui_languages.init( "/ui/language", languages, langValues, G_N_ELEMENTS(languages), languages[0]); _page_ui.add_line( false, _("Language (requires restart):"), _ui_languages, "", diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp index 62ed4e639..fb24d8e72 100644 --- a/src/ui/dialog/livepatheffect-editor.cpp +++ b/src/ui/dialog/livepatheffect-editor.cpp @@ -299,6 +299,10 @@ LivePathEffectEditor::effect_list_reload(SPLPEItem *lpeitem) PathEffectList::iterator it; for( it = effectlist.begin() ; it!=effectlist.end(); it++ ) { + if ( !(*it)->lpeobject ) { + continue; + } + if ((*it)->lpeobject->get_lpe()) { Gtk::TreeModel::Row row = *(effectlist_store->append()); row[columns.col_name] = (*it)->lpeobject->get_lpe()->getName(); diff --git a/src/ui/dialog/print-colors-preview-dialog.cpp b/src/ui/dialog/print-colors-preview-dialog.cpp index f4d83c271..1f999f692 100644 --- a/src/ui/dialog/print-colors-preview-dialog.cpp +++ b/src/ui/dialog/print-colors-preview-dialog.cpp @@ -7,7 +7,7 @@ * Copyright (C) 2009 Authors * Released under GNU GPLv2 (or later). Read the file 'COPYING' for more information. */ - +/* #include "desktop.h" #include "print-colors-preview-dialog.h" #include "preferences.h" @@ -98,3 +98,4 @@ PrintColorsPreviewDialog::~PrintColorsPreviewDialog(){} } // namespace Dialog } // namespace UI } // namespace Inkscape +*/ diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 163b49867..96910821e 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -14,6 +14,7 @@ #include <errno.h> #include <map> +#include <algorithm> #include <gtk/gtkdialog.h> //for GTK_RESPONSE* types #include <gtk/gtkdnd.h> @@ -44,11 +45,12 @@ #include "swatches.h" #include "style.h" #include "ui/previewholder.h" +#include "widgets/desktop-widget.h" #include "widgets/gradient-vector.h" #include "widgets/eek-preview.h" #include "display/cairo-utils.h" #include "sp-gradient-reference.h" - +#include "dialog-manager.h" namespace Inkscape { namespace UI { @@ -68,6 +70,12 @@ static std::vector<DocTrack*> docTrackings; static std::map<SwatchesPanel*, SPDocument*> docPerPanel; +class SwatchesPanelHook : public SwatchesPanel +{ +public: + static void convertGradient( GtkMenuItem *menuitem, gpointer userData ); + static void deleteGradient( GtkMenuItem *menuitem, gpointer userData ); +}; static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) { ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); @@ -84,6 +92,9 @@ static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer } static GtkWidget* popupMenu = 0; +static GtkWidget *popupSubHolder = 0; +static GtkWidget *popupSub = 0; +static std::vector<Glib::ustring> popupItems; static std::vector<GtkWidget*> popupExtras; static ColorItem* bounceTarget = 0; static SwatchesPanel* bouncePanel = 0; @@ -102,11 +113,37 @@ static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ ) } } -static void editGradientImpl( SPGradient* gr ) +static void editGradientImpl( SPDesktop* desktop, SPGradient* gr ) { if ( gr ) { - GtkWidget *dialog = sp_gradient_vector_editor_new( gr ); - gtk_widget_show( dialog ); + bool shown = false; + if ( desktop && desktop->doc() ) { + Inkscape::Selection *selection = sp_desktop_selection( desktop ); + GSList const *items = selection->itemList(); + if (items) { + SPStyle *query = sp_style_new( desktop->doc() ); + int result = objects_query_fillstroke(const_cast<GSList *>(items), query, true); + if ( (result == QUERY_STYLE_MULTIPLE_SAME) || (result == QUERY_STYLE_SINGLE) ) { + // could be pertinent + if (query->fill.isPaintserver()) { + SPPaintServer* server = query->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { + SPGradient* grad = SP_GRADIENT(server); + if ( grad->isSwatch() && grad->getId() == gr->getId()) { + desktop->_dlg_mgr->showDialog("FillAndStroke"); + shown = true; + } + } + } + } + sp_style_unref(query); + } + } + + if (!shown) { + GtkWidget *dialog = sp_gradient_vector_editor_new( gr ); + gtk_widget_show( dialog ); + } } } @@ -122,7 +159,7 @@ static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); if ( targetName == grad->getId() ) { - editGradientImpl( grad ); + editGradientImpl( desktop, grad ); break; } } @@ -130,31 +167,48 @@ static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) } } -static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) +void SwatchesPanelHook::convertGradient( GtkMenuItem * /*menuitem*/, gpointer userData ) { if ( bounceTarget ) { SwatchesPanel* swp = bouncePanel; SPDesktop* desktop = swp ? swp->getDesktop() : 0; SPDocument *doc = desktop ? desktop->doc() : 0; - if (doc) { - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); - - Inkscape::XML::Node *repr = xml_doc->createElement("svg:linearGradient"); - repr->setAttribute("osb:paint", "solid"); - Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop"); - stop->setAttribute("offset", "0"); - stop->setAttribute("style", "stop-color:#000;stop-opacity:1;"); - repr->appendChild(stop); - Inkscape::GC::release(stop); - - SP_OBJECT_REPR( SP_DOCUMENT_DEFS(doc) )->addChild(repr, NULL); - - SPGradient * gr = static_cast<SPGradient *>(doc->getObjectByRepr(repr)); - - Inkscape::GC::release(repr); + gint index = GPOINTER_TO_INT(userData); + if ( doc && (index >= 0) && (static_cast<guint>(index) < popupItems.size()) ) { + Glib::ustring targetName = popupItems[index]; + const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( targetName == grad->getId() ) { + grad->setSwatch(); + sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT, + _("Add gradient stop")); + break; + } + } + } + } +} - editGradientImpl( gr ); +void SwatchesPanelHook::deleteGradient( GtkMenuItem */*menuitem*/, gpointer /*userData*/ ) +{ + if ( bounceTarget ) { + SwatchesPanel* swp = bouncePanel; + SPDesktop* desktop = swp ? swp->getDesktop() : 0; + SPDocument *doc = desktop ? desktop->doc() : 0; + if (doc) { + std::string targetName(bounceTarget->def.descr); + const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); + for (const GSList *item = gradients; item; item = item->next) { + SPGradient* grad = SP_GRADIENT(item->data); + if ( targetName == grad->getId() ) { + grad->setSwatch(false); + sp_document_done(doc, SP_VERB_CONTEXT_GRADIENT, + _("Delete")); + break; + } + } } } } @@ -177,7 +231,12 @@ static SwatchesPanel* findContainingPanel( GtkWidget *widget ) return swp; } -gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data) +static void removeit( GtkWidget *widget, gpointer data ) +{ + gtk_container_remove( GTK_CONTAINER(data), widget ); +} + +gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data ) { gboolean handled = FALSE; @@ -209,17 +268,13 @@ gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, g gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); popupExtras.push_back(child); - child = gtk_menu_item_new_with_label(_("Add")); + child = gtk_menu_item_new_with_label(_("Delete")); g_signal_connect( G_OBJECT(child), "activate", - G_CALLBACK(addNewGradient), + G_CALLBACK(SwatchesPanelHook::deleteGradient), user_data ); gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); popupExtras.push_back(child); - - child = gtk_menu_item_new_with_label(_("Delete")); - gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); - //popupExtras.push_back(child); gtk_widget_set_sensitive( child, FALSE ); child = gtk_menu_item_new_with_label(_("Edit...")); @@ -237,7 +292,12 @@ gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, g child = gtk_menu_item_new_with_label(_("Convert")); gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); //popupExtras.push_back(child); - gtk_widget_set_sensitive( child, FALSE ); + //gtk_widget_set_sensitive( child, FALSE ); + { + popupSubHolder = child; + popupSub = gtk_menu_new(); + gtk_menu_item_set_submenu( GTK_MENU_ITEM(child), popupSub ); + } gtk_widget_show_all(popupMenu); } @@ -251,7 +311,39 @@ gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, g bounceTarget = item; bouncePanel = swp; + popupItems.clear(); if ( popupMenu ) { + gtk_container_foreach(GTK_CONTAINER(popupSub), removeit, popupSub); + bool processed = false; + GtkWidget *wdgt = gtk_widget_get_ancestor(widget, SP_TYPE_DESKTOP_WIDGET); + if ( wdgt ) { + SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(wdgt); + if ( dtw && dtw->desktop ) { + // Pick up all gradients with vectors + const GSList *gradients = sp_document_get_resource_list(dtw->desktop->doc(), "gradient"); + gint index = 0; + for (const GSList *curr = gradients; curr; curr = curr->next) { + SPGradient* grad = SP_GRADIENT(curr->data); + if ( grad->hasStops() && !grad->isSwatch() ) { + //gl = g_slist_prepend(gl, curr->data); + processed = true; + GtkWidget *child = gtk_menu_item_new_with_label(grad->getId()); + gtk_menu_shell_append(GTK_MENU_SHELL(popupSub), child); + + popupItems.push_back(grad->getId()); + g_signal_connect( G_OBJECT(child), + "activate", + G_CALLBACK(SwatchesPanelHook::convertGradient), + GINT_TO_POINTER(index) ); + index++; + } + } + + gtk_widget_show_all(popupSub); + } + } + gtk_widget_set_sensitive( popupSubHolder, processed ); + gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time); handled = TRUE; } @@ -728,9 +820,11 @@ static void recalcSwatchContents(SPDocument* doc, } if ( !newList.empty() ) { + std::reverse(newList.begin(), newList.end()); for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it ) { SPGradient* grad = *it; + cairo_surface_t *preview = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, PREVIEW_PIXBUF_WIDTH, VBLOCK); cairo_t *ct = cairo_create(preview); @@ -807,21 +901,25 @@ void SwatchesPanel::handleDefsModified(SPDocument *document) std::map<ColorItem*, SPGradient*> tmpGrads; recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads); - int cap = std::min(docPalette->_colors.size(), tmpColors.size()); - for (int i = 0; i < cap; i++) { - ColorItem* newColor = tmpColors[i]; - ColorItem* oldColor = docPalette->_colors[i]; - if ( (newColor->def.getType() != oldColor->def.getType()) || - (newColor->def.getR() != oldColor->def.getR()) || - (newColor->def.getG() != oldColor->def.getG()) || - (newColor->def.getB() != oldColor->def.getB()) ) { - oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB()); - } - if (tmpGrads.find(newColor) != tmpGrads.end()) { - oldColor->setGradient(tmpGrads[newColor]); - } - if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) { - oldColor->setPattern(tmpPrevs[newColor]); + if ( tmpColors.size() != docPalette->_colors.size() ) { + handleGradientsChange(document); + } else { + int cap = std::min(docPalette->_colors.size(), tmpColors.size()); + for (int i = 0; i < cap; i++) { + ColorItem* newColor = tmpColors[i]; + ColorItem* oldColor = docPalette->_colors[i]; + if ( (newColor->def.getType() != oldColor->def.getType()) || + (newColor->def.getR() != oldColor->def.getR()) || + (newColor->def.getG() != oldColor->def.getG()) || + (newColor->def.getB() != oldColor->def.getB()) ) { + oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB()); + } + if (tmpGrads.find(newColor) != tmpGrads.end()) { + oldColor->setGradient(tmpGrads[newColor]); + } + if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) { + oldColor->setPattern(tmpPrevs[newColor]); + } } } @@ -865,11 +963,12 @@ void SwatchesPanel::_updateFromSelection() if ( SP_IS_GRADIENT(server) ) { SPGradient* target = 0; SPGradient* grad = SP_GRADIENT(server); - if (grad->repr->attribute("osb:paint")) { + + if ( grad->isSwatch() ) { target = grad; } else if ( grad->ref ) { SPGradient *tmp = grad->ref->getObject(); - if ( tmp && tmp->repr->attribute("osb:paint") ) { + if ( tmp && tmp->isSwatch() ) { target = tmp; } } @@ -896,11 +995,11 @@ void SwatchesPanel::_updateFromSelection() if ( SP_IS_GRADIENT(server) ) { SPGradient* target = 0; SPGradient* grad = SP_GRADIENT(server); - if (grad->repr->attribute("osb:paint")) { + if ( grad->isSwatch() ) { target = grad; } else if ( grad->ref ) { SPGradient *tmp = grad->ref->getObject(); - if ( tmp && tmp->repr->attribute("osb:paint") ) { + if ( tmp && tmp->isSwatch() ) { target = tmp; } } diff --git a/src/ui/dialog/swatches.h b/src/ui/dialog/swatches.h index b18fd6cad..93bbe81d8 100644 --- a/src/ui/dialog/swatches.h +++ b/src/ui/dialog/swatches.h @@ -43,6 +43,8 @@ public: virtual int getSelectedIndex() {return _currentIndex;} // temporary protected: + static void handleGradientsChange(SPDocument *document); + virtual void _updateFromSelection(); virtual void _handleAction( int setId, int itemId ); virtual void _setDocument( SPDocument *document ); @@ -55,7 +57,6 @@ private: SwatchesPanel &operator=(SwatchesPanel const &); // no assign static void _trackDocument( SwatchesPanel *panel, SPDocument *document ); - static void handleGradientsChange(SPDocument *document); static void handleDefsModified(SPDocument *document); PreviewHolder* _holder; diff --git a/src/ui/icon-names.h b/src/ui/icon-names.h index d36d4a41a..92fd86a48 100644 --- a/src/ui/icon-names.h +++ b/src/ui/icon-names.h @@ -206,6 +206,12 @@ "edit-select-original" #define INKSCAPE_ICON_EDIT_UNDO_HISTORY \ "edit-undo-history" +#define INKSCAPE_ICON_EXCHANGE_POSITIONS \ + "exchange-positions" +#define INKSCAPE_ICON_EXCHANGE_POSITIONS_ZORDER \ + "exchange-positions-zorder" +#define INKSCAPE_ICON_EXCHANGE_POSITIONS_CLOCKWISE \ + "exchange-positions-clockwise" #define INKSCAPE_ICON_FILL_RULE_EVEN_ODD \ "fill-rule-even-odd" #define INKSCAPE_ICON_FILL_RULE_NONZERO \ diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp index f880d2ddf..615587eeb 100644 --- a/src/ui/tool/control-point-selection.cpp +++ b/src/ui/tool/control-point-selection.cpp @@ -273,8 +273,11 @@ void ControlPointSelection::_pointGrabbed(SelectableControlPoint *point) _grabbed_point = point; _farthest_point = point; double maxdist = 0; + Geom::Matrix m; + m.setIdentity(); for (iterator i = _points.begin(); i != _points.end(); ++i) { _original_positions.insert(std::make_pair(*i, (*i)->position())); + _last_trans.insert(std::make_pair(*i, m)); double dist = Geom::distance(*_grabbed_point, **i); if (dist > maxdist) { maxdist = dist; @@ -288,12 +291,52 @@ void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion * Geom::Point abs_delta = new_pos - _original_positions[_grabbed_point]; double fdist = Geom::distance(_original_positions[_grabbed_point], _original_positions[_farthest_point]); if (held_alt(*event) && fdist > 0) { - // sculpting + // Sculpting for (iterator i = _points.begin(); i != _points.end(); ++i) { SelectableControlPoint *cur = (*i); + Geom::Matrix trans; + trans.setIdentity(); double dist = Geom::distance(_original_positions[cur], _original_positions[_grabbed_point]); double deltafrac = 0.5 + 0.5 * cos(M_PI * dist/fdist); - cur->move(_original_positions[cur] + abs_delta * deltafrac); + if (dist != 0.0) { + // The sculpting transformation is not affine, but it can be + // locally approximated by one. Here we compute the local + // affine approximation of the sculpting transformation near + // the currently transformed point. We then transform the point + // by this approximation. This gives us sensible behavior for node handles. + // NOTE: probably it would be better to transform the node handles, + // but ControlPointSelection is supposed to work for any + // SelectableControlPoints, not only Nodes. We could create a specialized + // NodeSelection class that inherits from this one and move sculpting there. + Geom::Point origdx(Geom::EPSILON, 0); + Geom::Point origdy(0, Geom::EPSILON); + Geom::Point origp = _original_positions[cur]; + Geom::Point origpx = _original_positions[cur] + origdx; + Geom::Point origpy = _original_positions[cur] + origdy; + double distdx = Geom::distance(origpx, _original_positions[_grabbed_point]); + double distdy = Geom::distance(origpy, _original_positions[_grabbed_point]); + double deltafracdx = 0.5 + 0.5 * cos(M_PI * distdx/fdist); + double deltafracdy = 0.5 + 0.5 * cos(M_PI * distdy/fdist); + Geom::Point newp = origp + abs_delta * deltafrac; + Geom::Point newpx = origpx + abs_delta * deltafracdx; + Geom::Point newpy = origpy + abs_delta * deltafracdy; + Geom::Point newdx = (newpx - newp) / Geom::EPSILON; + Geom::Point newdy = (newpy - newp) / Geom::EPSILON; + + Geom::Matrix itrans(newdx[Geom::X], newdx[Geom::Y], newdy[Geom::X], newdy[Geom::Y], 0, 0); + if (itrans.isSingular()) + itrans.setIdentity(); + + trans *= Geom::Translate(-cur->position()); + trans *= _last_trans[cur].inverse(); + trans *= itrans; + trans *= Geom::Translate(_original_positions[cur] + abs_delta * deltafrac); + _last_trans[cur] = itrans; + } else { + trans *= Geom::Translate(-cur->position() + _original_positions[cur] + abs_delta * deltafrac); + } + cur->transform(trans); + //cur->move(_original_positions[cur] + abs_delta * deltafrac); } } else { Geom::Point delta = new_pos - _grabbed_point->position(); @@ -309,6 +352,7 @@ void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion * void ControlPointSelection::_pointUngrabbed() { _original_positions.clear(); + _last_trans.clear(); _dragging = false; _grabbed_point = _farthest_point = NULL; _updateBounds(); diff --git a/src/ui/tool/control-point-selection.h b/src/ui/tool/control-point-selection.h index 514ecb2e3..8023c3e28 100644 --- a/src/ui/tool/control-point-selection.h +++ b/src/ui/tool/control-point-selection.h @@ -1,5 +1,5 @@ /** @file - * Node selection - stores a set of nodes and applies transformations + * Control point selection - stores a set of control points and applies transformations * to them */ /* Authors: @@ -9,8 +9,8 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifndef SEEN_UI_TOOL_NODE_SELECTION_H -#define SEEN_UI_TOOL_NODE_SELECTION_H +#ifndef SEEN_UI_TOOL_CONTROL_POINT_SELECTION_H +#define SEEN_UI_TOOL_CONTROL_POINT_SELECTION_H #include <memory> #include <boost/optional.hpp> @@ -132,6 +132,7 @@ private: set_type _points; set_type _all_points; INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Point> _original_positions; + INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Matrix> _last_trans; boost::optional<double> _rot_radius; boost::optional<double> _mouseover_rot_radius; Geom::OptRect _bounds; diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index 9c9c58ff1..a8582ccc5 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -266,13 +266,11 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) Node *node_away = (this == &_parent->_front ? _parent->_prev() : _parent->_next()); if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { - Inkscape::Snapper::ConstraintLine cl(_parent->position(), + Inkscape::Snapper::SnapConstraint cl(_parent->position(), _parent->position() - node_away->position()); Inkscape::SnappedPoint p; p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), cl); - if (p.getSnapped()) { - p.getPoint(new_pos); - } + new_pos = p.getPoint(); } else { sm.freeSnapReturnByRef(new_pos, SNAPSOURCE_NODE_HANDLE); } @@ -945,7 +943,7 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) // For a note on how snapping is implemented in Inkscape, see snap.h. SnapManager &sm = _desktop->namedview->snap_manager; bool snap = sm.someSnapperMightSnap(); - std::vector<Inkscape::SnapCandidatePoint> unselected; + Inkscape::SnappedPoint sp; if (snap) { /* setup * TODO We are doing this every time a snap happens. It should once be done only once @@ -955,6 +953,7 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) * TODO Snapping to unselected segments of selected paths doesn't work yet. */ // Build the list of unselected nodes. + std::vector<Inkscape::SnapCandidatePoint> unselected; typedef ControlPointSelection::Set Set; Set &nodes = _selection.allPoints(); for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { @@ -964,17 +963,22 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) unselected.push_back(p); } } - sm.setupIgnoreSelection(_desktop, false, &unselected); + sm.setupIgnoreSelection(_desktop, true, &unselected); } if (held_control(*event)) { Geom::Point origin = _last_drag_origin(); - Inkscape::SnappedPoint fp, bp; + std::vector<Inkscape::Snapper::SnapConstraint> constraints; if (held_alt(*event)) { // with Ctrl+Alt, constrain to handle lines - // project the new position onto a handle line that is closer - boost::optional<Geom::Point> front_point, back_point; - boost::optional<Inkscape::Snapper::ConstraintLine> line_front, line_back; + // project the new position onto a handle line that is closer; + // also snap to perpendiculars of handle lines + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + double min_angle = M_PI / snaps; + + boost::optional<Geom::Point> front_point, back_point, fperp_point, bperp_point; if (_front.isDegenerate()) { if (_is_line_segment(this, _next())) front_point = _next()->position() - origin; @@ -987,72 +991,40 @@ void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) } else { back_point = _back.relativePos(); } - if (front_point) - line_front = Inkscape::Snapper::ConstraintLine(origin, *front_point); - if (back_point) - line_back = Inkscape::Snapper::ConstraintLine(origin, *back_point); - - // TODO: combine the snap and non-snap branches by modifying snap.h / snap.cpp - if (snap) { - if (line_front) { - fp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, - _snapSourceType()), *line_front); - } - if (line_back) { - bp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, - _snapSourceType()), *line_back); - } + if (front_point) { + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, *front_point)); + fperp_point = Geom::rot90(*front_point); } - if (fp.getSnapped() || bp.getSnapped()) { - if (fp.isOtherSnapBetter(bp, false)) { - fp = bp; - } - fp.getPoint(new_pos); - _desktop->snapindicator->set_new_snaptarget(fp); - } else { - boost::optional<Geom::Point> pos; - if (line_front) { - pos = line_front->projection(new_pos); - } - if (line_back) { - Geom::Point pos2 = line_back->projection(new_pos); - if (!pos || (pos && Geom::distance(new_pos, *pos) > Geom::distance(new_pos, pos2))) - pos = pos2; - } - if (pos) { - new_pos = *pos; - } else { - new_pos = origin; - } + if (back_point) { + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, *back_point)); + bperp_point = Geom::rot90(*back_point); } - } else { - // with Ctrl, constrain to axes - // TODO combine the two branches - if (snap) { - Inkscape::Snapper::ConstraintLine line_x(origin, Geom::Point(1, 0)); - Inkscape::Snapper::ConstraintLine line_y(origin, Geom::Point(0, 1)); - fp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_x); - bp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_y); + // perpendiculars only snap when they are further than snap increment away + // from the second handle constraint + if (fperp_point && (!back_point || + (fabs(Geom::angle_between(*fperp_point, *back_point)) > min_angle && + fabs(Geom::angle_between(*fperp_point, *back_point)) < M_PI - min_angle))) + { + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, *fperp_point)); } - if (fp.getSnapped() || bp.getSnapped()) { - if (fp.isOtherSnapBetter(bp, false)) { - fp = bp; - } - fp.getPoint(new_pos); - _desktop->snapindicator->set_new_snaptarget(fp); - } else { - Geom::Point origin = _last_drag_origin(); - Geom::Point delta = new_pos - origin; - Geom::Dim2 d = (fabs(delta[Geom::X]) < fabs(delta[Geom::Y])) ? Geom::X : Geom::Y; - new_pos[d] = origin[d]; + if (bperp_point && (!front_point || + (fabs(Geom::angle_between(*bperp_point, *front_point)) > min_angle && + fabs(Geom::angle_between(*bperp_point, *front_point)) < M_PI - min_angle))) + { + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, *bperp_point)); } + + sp = sm.multipleConstrainedSnaps(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), constraints); + } else { + // with Ctrl, constrain to axes + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, Geom::Point(1, 0))); + constraints.push_back(Inkscape::Snapper::SnapConstraint(origin, Geom::Point(0, 1))); + sp = sm.multipleConstrainedSnaps(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), constraints); } + new_pos = sp.getPoint(); } else if (snap) { - Inkscape::SnappedPoint p = sm.freeSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType())); - if (p.getSnapped()) { - p.getPoint(new_pos); - _desktop->snapindicator->set_new_snaptarget(p); - } + sp = sm.freeSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType())); + new_pos = sp.getPoint(); } SelectableControlPoint::dragged(new_pos, event); diff --git a/src/ui/uxmanager.cpp b/src/ui/uxmanager.cpp index fbe000de9..a3ccb3471 100644 --- a/src/ui/uxmanager.cpp +++ b/src/ui/uxmanager.cpp @@ -131,7 +131,7 @@ UXManagerImpl::UXManagerImpl() : int width = defaultScreen->get_width(); int height = defaultScreen->get_height(); gdouble aspect = static_cast<gdouble>(width) / static_cast<gdouble>(height); - if (aspect > 1.4) { + if (aspect > 1.65) { _widescreen = true; } } diff --git a/src/verbs.cpp b/src/verbs.cpp index 24c17aad8..c2af399c5 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1682,9 +1682,9 @@ ZoomVerb::perform(SPAction *action, void *data, void */*pdata*/) case SP_VERB_VIEW_MODE_OUTLINE: dt->setDisplayModeOutline(); break; - case SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW: - dt->setDisplayModePrintColorsPreview(); - break; +// case SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW: +// dt->setDisplayModePrintColorsPreview(); +// break; case SP_VERB_VIEW_MODE_TOGGLE: dt->displayModeToggle(); break; @@ -2569,8 +2569,8 @@ Verb *Verb::_base_verbs[] = { N_("Switch to normal display without filters"), NULL), new ZoomVerb(SP_VERB_VIEW_MODE_OUTLINE, "ViewModeOutline", N_("_Outline"), N_("Switch to outline (wireframe) display mode"), NULL), - new ZoomVerb(SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, "ViewModePrintColorsPreview", N_("_Print Colors Preview"), - N_("Switch to print colors preview mode"), NULL), +// new ZoomVerb(SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, "ViewModePrintColorsPreview", N_("_Print Colors Preview"), +// N_("Switch to print colors preview mode"), NULL), new ZoomVerb(SP_VERB_VIEW_MODE_TOGGLE, "ViewModeToggle", N_("_Toggle"), N_("Toggle between normal and outline display modes"), NULL), diff --git a/src/verbs.h b/src/verbs.h index 36bb9f9d9..7c04ba3f6 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -214,7 +214,7 @@ enum { SP_VERB_VIEW_MODE_NORMAL, SP_VERB_VIEW_MODE_NO_FILTERS, SP_VERB_VIEW_MODE_OUTLINE, - SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, +// SP_VERB_VIEW_MODE_PRINT_COLORS_PREVIEW, SP_VERB_VIEW_MODE_TOGGLE, SP_VERB_VIEW_CMS_TOGGLE, SP_VERB_VIEW_ICON_PREVIEW, diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp index 15d8b6cc2..a0e343b58 100644 --- a/src/widgets/fill-style.cpp +++ b/src/widgets/fill-style.cpp @@ -268,15 +268,15 @@ void FillNStroke::performUpdate() psel->setGradientLinear( vector ); SPLinearGradient *lg = SP_LINEARGRADIENT(server); - psel->setGradientProperties( SP_GRADIENT_UNITS(lg), - SP_GRADIENT_SPREAD(lg) ); + psel->setGradientProperties( lg->getUnits(), + lg->getSpread() ); } else if (SP_IS_RADIALGRADIENT(server)) { SPGradient *vector = SP_GRADIENT(server)->getVector(); psel->setGradientRadial( vector ); SPRadialGradient *rg = SP_RADIALGRADIENT(server); - psel->setGradientProperties( SP_GRADIENT_UNITS(rg), - SP_GRADIENT_SPREAD(rg) ); + psel->setGradientProperties( rg->getUnits(), + rg->getSpread() ); } else if (SP_IS_PATTERN(server)) { SPPattern *pat = pattern_getroot(SP_PATTERN(server)); psel->updatePatternList( pat ); @@ -523,6 +523,7 @@ void FillNStroke::updateFromPaint() SPGradientType const gradient_type = ( psel->mode != SPPaintSelector::MODE_GRADIENT_RADIAL ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL ); + bool createSwatch = (psel->mode == SPPaintSelector::MODE_SWATCH); SPCSSAttr *css = 0; if (kind == FILL) { @@ -537,27 +538,34 @@ void FillNStroke::updateFromPaint() SPStyle *query = sp_style_new(desktop->doc()); int result = objects_query_fillstroke(const_cast<GSList *>(items), query, kind == FILL); - SPIPaint &targPaint = (kind == FILL) ? query->fill : query->stroke; - guint32 common_rgb = 0; if (result == QUERY_STYLE_MULTIPLE_SAME) { + SPIPaint &targPaint = (kind == FILL) ? query->fill : query->stroke; + SPColor common; if (!targPaint.isColor()) { - common_rgb = sp_desktop_get_color(desktop, kind == FILL); + common = sp_desktop_get_color(desktop, kind == FILL); } else { - common_rgb = targPaint.value.color.toRGBA32( 0xff ); + common = targPaint.value.color; + } + vector = sp_document_default_gradient_vector( document, common, createSwatch ); + if ( vector && createSwatch ) { + vector->setSwatch(); } - vector = sp_document_default_gradient_vector(document, common_rgb); } sp_style_unref(query); for (GSList const *i = items; i != NULL; i = i->next) { //FIXME: see above if (kind == FILL) { - sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style"); + sp_repr_css_change_recursive(reinterpret_cast<SPObject*>(i->data)->repr, css, "style"); } if (!vector) { + SPGradient *gr = sp_gradient_vector_for_object( document, desktop, reinterpret_cast<SPObject*>(i->data), kind == FILL, createSwatch ); + if ( gr && createSwatch ) { + gr->setSwatch(); + } sp_item_set_gradient(SP_ITEM(i->data), - sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), kind == FILL), + gr, gradient_type, kind == FILL); } else { sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, kind == FILL); @@ -570,7 +578,7 @@ void FillNStroke::updateFromPaint() for (GSList const *i = items; i != NULL; i = i->next) { //FIXME: see above if (kind == FILL) { - sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style"); + sp_repr_css_change_recursive(reinterpret_cast<SPObject*>(i->data)->repr, css, "style"); } SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, kind == FILL); @@ -600,7 +608,7 @@ void FillNStroke::updateFromPaint() */ } else { - Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern); + Inkscape::XML::Node *patrepr = pattern->repr; SPCSSAttr *css = sp_repr_css_attr_new(); gchar *urltext = g_strdup_printf("url(#%s)", patrepr->attribute("id")); sp_repr_css_set_property(css, (kind == FILL) ? "fill" : "stroke", urltext); @@ -614,17 +622,17 @@ void FillNStroke::updateFromPaint() // objects who already have the same root pattern but through a different href // chain. FIXME: move this to a sp_item_set_pattern for (GSList const *i = items; i != NULL; i = i->next) { - Inkscape::XML::Node *selrepr = SP_OBJECT_REPR(i->data); + Inkscape::XML::Node *selrepr = reinterpret_cast<SPObject*>(i->data)->repr; if ( (kind == STROKE) && !selrepr) { continue; } - SPObject *selobj = SP_OBJECT(i->data); + SPObject *selobj = reinterpret_cast<SPObject*>(i->data); - SPStyle *style = SP_OBJECT_STYLE(selobj); + SPStyle *style = selobj->style; if (style && ((kind == FILL) ? style->fill : style->stroke).isPaintserver()) { - SPObject *server = (kind == FILL) ? - SP_OBJECT_STYLE_FILL_SERVER(selobj) : - SP_OBJECT_STYLE_STROKE_SERVER(selobj); + SPPaintServer *server = (kind == FILL) ? + selobj->style->getFillPaintServer() : + selobj->style->getStrokePaintServer(); if (SP_IS_PATTERN(server) && pattern_getroot(SP_PATTERN(server)) == pattern) // only if this object's pattern is not rooted in our selected pattern, apply continue; diff --git a/src/widgets/gradient-selector.cpp b/src/widgets/gradient-selector.cpp index 9aa72164b..5b663c493 100644 --- a/src/widgets/gradient-selector.cpp +++ b/src/widgets/gradient-selector.cpp @@ -117,10 +117,10 @@ sp_gradient_selector_class_init (SPGradientSelectorClass *klass) object_class->destroy = sp_gradient_selector_destroy; } -static void -sp_gradient_selector_init (SPGradientSelector *sel) +static void sp_gradient_selector_init(SPGradientSelector *sel) { - GtkWidget *hb, *m, *mi; + sel->safelyInit = true; + new (&sel->nonsolid) std::vector<GtkWidget*>(); sel->mode = SPGradientSelector::MODE_LINEAR; @@ -134,39 +134,44 @@ sp_gradient_selector_init (SPGradientSelector *sel) g_signal_connect (G_OBJECT (sel->vectors), "vector_set", G_CALLBACK (sp_gradient_selector_vector_set), sel); /* Create box for buttons */ - hb = gtk_hbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (sel), hb, FALSE, FALSE, 0); + GtkWidget *hb = gtk_hbox_new( FALSE, 0 ); + sel->nonsolid.push_back(hb); + gtk_box_pack_start( GTK_BOX(sel), hb, FALSE, FALSE, 0 ); GtkTooltips *ttips = gtk_tooltips_new (); sel->add = gtk_button_new_with_label (_("Duplicate")); + sel->nonsolid.push_back(sel->add); gtk_box_pack_start (GTK_BOX (hb), sel->add, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (sel->add), "clicked", G_CALLBACK (sp_gradient_selector_add_vector_clicked), sel); gtk_widget_set_sensitive (sel->add, FALSE); sel->edit = gtk_button_new_with_label (_("Edit...")); + sel->nonsolid.push_back(sel->edit); gtk_box_pack_start (GTK_BOX (hb), sel->edit, TRUE, TRUE, 0); g_signal_connect (G_OBJECT (sel->edit), "clicked", G_CALLBACK (sp_gradient_selector_edit_vector_clicked), sel); gtk_widget_set_sensitive (sel->edit, FALSE); - gtk_widget_show_all (hb); + gtk_widget_show_all(hb); /* Spread selector */ - hb = gtk_hbox_new (FALSE, 0); - gtk_widget_show (hb); - gtk_box_pack_start (GTK_BOX (sel), hb, FALSE, FALSE, 0); - - sel->spread = gtk_option_menu_new (); - gtk_widget_show (sel->spread); - gtk_box_pack_end (GTK_BOX (hb), sel->spread, FALSE, FALSE, 0); - gtk_tooltips_set_tip (ttips, sel->spread, + hb = gtk_hbox_new( FALSE, 0 ); + sel->nonsolid.push_back(hb); + gtk_widget_show(hb); + gtk_box_pack_start( GTK_BOX(sel), hb, FALSE, FALSE, 0 ); + + sel->spread = gtk_option_menu_new(); + sel->nonsolid.push_back(sel->spread); + gtk_widget_show(sel->spread); + gtk_box_pack_end( GTK_BOX(hb), sel->spread, FALSE, FALSE, 0 ); + gtk_tooltips_set_tip( ttips, sel->spread, // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/pservers.html#LinearGradientSpreadMethodAttribute _("Whether to fill with flat color beyond the ends of the gradient vector " "(spreadMethod=\"pad\"), or repeat the gradient in the same direction " "(spreadMethod=\"repeat\"), or repeat the gradient in alternating opposite " "directions (spreadMethod=\"reflect\")"), NULL); - m = gtk_menu_new (); - mi = gtk_menu_item_new_with_label (_("none")); + GtkWidget *m = gtk_menu_new(); + GtkWidget *mi = gtk_menu_item_new_with_label(_("none")); gtk_menu_append (GTK_MENU (m), mi); g_object_set_data (G_OBJECT (mi), "gradientSpread", GUINT_TO_POINTER (SP_GRADIENT_SPREAD_PAD)); g_signal_connect (G_OBJECT (mi), "activate", G_CALLBACK (sp_gradient_selector_spread_activate), sel); @@ -180,22 +185,27 @@ sp_gradient_selector_init (SPGradientSelector *sel) gtk_menu_append (GTK_MENU (m), mi); gtk_widget_show_all (m); - gtk_option_menu_set_menu (GTK_OPTION_MENU (sel->spread), m); + gtk_option_menu_set_menu( GTK_OPTION_MENU(sel->spread), m ); - sel->spreadLbl = gtk_label_new (_("Repeat:")); - gtk_widget_show(sel->spreadLbl); - gtk_box_pack_end(GTK_BOX(hb), sel->spreadLbl, FALSE, FALSE, 4); + sel->spreadLbl = gtk_label_new( _("Repeat:") ); + sel->nonsolid.push_back(sel->spreadLbl); + gtk_widget_show( sel->spreadLbl ); + gtk_box_pack_end( GTK_BOX(hb), sel->spreadLbl, FALSE, FALSE, 4 ); } -static void -sp_gradient_selector_destroy (GtkObject *object) +static void sp_gradient_selector_destroy(GtkObject *object) { - SPGradientSelector *sel; + SPGradientSelector *sel = SP_GRADIENT_SELECTOR( object ); - sel = SP_GRADIENT_SELECTOR (object); + if ( sel->safelyInit ) { + sel->safelyInit = false; + using std::vector; + sel->nonsolid.~vector<GtkWidget*>(); + } - if (((GtkObjectClass *) (parent_class))->destroy) + if (((GtkObjectClass *) (parent_class))->destroy) { (* ((GtkObjectClass *) (parent_class))->destroy) (object); + } } GtkWidget * @@ -208,26 +218,15 @@ sp_gradient_selector_new (void) return (GtkWidget *) sel; } -static void removeWidget( GtkWidget *& widget ) -{ - if (widget) { - GtkWidget *parent = gtk_widget_get_parent(widget); - if (parent) { - gtk_container_remove(GTK_CONTAINER(parent), widget); - widget = 0; - } - } -} - void SPGradientSelector::setMode(SelectorMode mode) { if (mode != this->mode) { this->mode = mode; if (mode == MODE_SWATCH) { - removeWidget(add); - removeWidget(edit); - removeWidget(spread); - removeWidget(spreadLbl); + for (std::vector<GtkWidget*>::iterator it = nonsolid.begin(); it != nonsolid.end(); ++it) + { + gtk_widget_hide(*it); + } SPGradientVectorSelector* vs = SP_GRADIENT_VECTOR_SELECTOR(vectors); vs->setSwatched(); @@ -262,13 +261,27 @@ void SPGradientSelector::setVector(SPDocument *doc, SPGradient *vector) g_return_if_fail(!vector || SP_IS_GRADIENT(vector)); g_return_if_fail(!vector || (SP_OBJECT_DOCUMENT(vector) == doc)); - if (vector && !SP_GRADIENT_HAS_STOPS(vector)) { + if (vector && !vector->hasStops()) { return; } sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(vectors), doc, vector); if (vector) { + if ( (mode == MODE_SWATCH) && vector->isSwatch() ) { + if ( vector->isSolid() ) { + for (std::vector<GtkWidget*>::iterator it = nonsolid.begin(); it != nonsolid.end(); ++it) + { + gtk_widget_hide(*it); + } + } else { + for (std::vector<GtkWidget*>::iterator it = nonsolid.begin(); it != nonsolid.end(); ++it) + { + gtk_widget_show_all(*it); + } + } + } + if (edit) { gtk_widget_set_sensitive(edit, TRUE); } diff --git a/src/widgets/gradient-selector.h b/src/widgets/gradient-selector.h index 25f561a6e..860804ec6 100644 --- a/src/widgets/gradient-selector.h +++ b/src/widgets/gradient-selector.h @@ -17,6 +17,7 @@ #include <glib.h> #include <gtk/gtkvbox.h> +#include <vector> #include "sp-gradient.h" #include "sp-gradient-spread.h" #include "sp-gradient-units.h" @@ -59,6 +60,9 @@ struct SPGradientSelector { GtkWidget *spread; GtkWidget *spreadLbl; + bool safelyInit; + std::vector<GtkWidget*> nonsolid; + void setMode(SelectorMode mode); void setUnits(SPGradientUnits units); void setSpread(SPGradientSpread spread); diff --git a/src/widgets/gradient-toolbar.cpp b/src/widgets/gradient-toolbar.cpp index 077e038e7..ce5f5fb8f 100644 --- a/src/widgets/gradient-toolbar.cpp +++ b/src/widgets/gradient-toolbar.cpp @@ -73,18 +73,17 @@ static void gr_toggle_fillstroke (GtkWidget *button, gpointer data) { } } -void -gr_apply_gradient_to_item (SPItem *item, SPGradient *gr, SPGradientType new_type, guint new_fill, bool do_fill, bool do_stroke) +void gr_apply_gradient_to_item( SPItem *item, SPGradient *gr, SPGradientType new_type, guint new_fill, bool do_fill, bool do_stroke ) { - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (do_fill) { if (style && (style->fill.isPaintserver()) && - SP_IS_GRADIENT (SP_OBJECT_STYLE_FILL_SERVER (item))) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SP_IS_GRADIENT( item->style->getFillPaintServer() )) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_LINEARGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, true); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, true); } } else if (new_fill) { @@ -94,11 +93,11 @@ gr_apply_gradient_to_item (SPItem *item, SPGradient *gr, SPGradientType new_type if (do_stroke) { if (style && (style->stroke.isPaintserver()) && - SP_IS_GRADIENT (SP_OBJECT_STYLE_STROKE_SERVER (item))) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_LINEARGRADIENT (server)) { + SP_IS_GRADIENT( item->style->getStrokePaintServer() )) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_LINEARGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, false); - } else if (SP_IS_RADIALGRADIENT (server)) { + } else if ( SP_IS_RADIALGRADIENT(server) ) { sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, false); } } else if (!new_fill) { @@ -175,7 +174,7 @@ GtkWidget *gr_vector_list(SPDesktop *desktop, bool selection_empty, SPGradient * const GSList *gradients = sp_document_get_resource_list (document, "gradient"); for (const GSList *i = gradients; i != NULL; i = i->next) { SPGradient *grad = SP_GRADIENT(i->data); - if (SP_GRADIENT_HAS_STOPS(grad) && !grad->isSolid()) { + if ( grad->hasStops() && !grad->isSolid() ) { gl = g_slist_prepend (gl, i->data); } } @@ -238,7 +237,7 @@ GtkWidget *gr_vector_list(SPDesktop *desktop, bool selection_empty, SPGradient * GtkWidget *hb = gtk_hbox_new (FALSE, 4); GtkWidget *l = gtk_label_new (""); - gchar *label = gr_prepare_label (SP_OBJECT(gradient)); + gchar *label = gr_prepare_label(gradient); gtk_label_set_markup (GTK_LABEL(l), label); g_free (label); gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); @@ -310,13 +309,13 @@ void gr_read_selection( Inkscape::Selection *selection, // If no selected dragger, read desktop selection for (GSList const* i = selection->itemList(); i; i = i->next) { SPItem *item = SP_ITEM(i->data); - SPStyle *style = SP_OBJECT_STYLE (item); + SPStyle *style = item->style; if (style && (style->fill.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_GRADIENT(server)) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { SPGradient *gradient = SP_GRADIENT(server)->getVector(); - SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server)); + SPGradientSpread spread = SP_GRADIENT(server)->fetchSpread(); if (gradient && gradient->isSolid()) { gradient = 0; @@ -339,14 +338,13 @@ void gr_read_selection( Inkscape::Selection *selection, } } if (style && (style->stroke.isPaintserver())) { - SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_GRADIENT(server)) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { SPGradient *gradient = SP_GRADIENT(server)->getVector(); - SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server)); + SPGradientSpread spread = SP_GRADIENT(server)->fetchSpread(); if (gradient && gradient->isSolid()) { gradient = 0; - } if (gradient && (gradient != gr_selected)) { diff --git a/src/widgets/gradient-vector.cpp b/src/widgets/gradient-vector.cpp index 7bfe27310..7f0256665 100644 --- a/src/widgets/gradient-vector.cpp +++ b/src/widgets/gradient-vector.cpp @@ -174,6 +174,10 @@ GtkWidget *sp_gradient_vector_selector_new(SPDocument *doc, SPGradient *gr) void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr) { +// g_message("sp_gradient_vector_selector_set_gradient(%p, %p, %p) [%s] %d %d", gvs, doc, gr, +// (gr ? gr->getId():"N/A"), +// (gr ? gr->isSwatch() : -1), +// (gr ? gr->isSolid() : -1)); static gboolean suppress = FALSE; g_return_if_fail(gvs != NULL); @@ -181,7 +185,7 @@ void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPD g_return_if_fail(!gr || (doc != NULL)); g_return_if_fail(!gr || SP_IS_GRADIENT(gr)); g_return_if_fail(!gr || (SP_OBJECT_DOCUMENT(gr) == doc)); - g_return_if_fail(!gr || SP_GRADIENT_HAS_STOPS(gr)); + g_return_if_fail(!gr || gr->hasStops()); if (doc != gvs->doc) { /* Disconnect signals */ @@ -252,7 +256,7 @@ static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs) const GSList *gradients = sp_document_get_resource_list(SP_OBJECT_DOCUMENT(gvs->gr), "gradient"); for (const GSList *curr = gradients; curr; curr = curr->next) { SPGradient* grad = SP_GRADIENT(curr->data); - if (SP_GRADIENT_HAS_STOPS(grad) && (grad->isSwatch() == gvs->swatched)) { + if ( grad->hasStops() && (grad->isSwatch() == gvs->swatched) ) { gl = g_slist_prepend(gl, curr->data); } } @@ -337,6 +341,7 @@ static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector * /* Hmmm - probably we can just re-set it as menuitem data (Lauris) */ //g_print("SPGradientVectorSelector: gradient %s activated\n", SP_OBJECT_ID(gr)); + //g_message("Setting to gradient %p swatch:%d solid:%d", gr, gr->isSwatch(), gr->isSolid()); norm = sp_gradient_ensure_vector_normalized(gr); if (norm != gr) { @@ -474,11 +479,8 @@ static void verify_grad(SPGradient *gradient) xml_doc = SP_OBJECT_REPR(gradient)->document(); if (i < 1) { - gchar c[64]; - sp_svg_write_color(c, sizeof(c), 0x00000000); - Inkscape::CSSOStringStream os; - os << "stop-color:" << c << ";stop-opacity:" << 1.0 << ";"; + os << "stop-color: #000000;stop-opacity:" << 1.0 << ";"; Inkscape::XML::Node *child; @@ -535,7 +537,7 @@ static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_ GtkWidget *m = gtk_menu_new(); gtk_widget_show(m); GSList *sl = NULL; - if (gradient->has_stops) { + if ( gradient->hasStops() ) { for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { if (SP_IS_STOP(ochild)) { sl = g_slist_append(sl, ochild); @@ -550,11 +552,9 @@ static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_ } else { for (; sl != NULL; sl = sl->next){ - SPStop *stop; - GtkWidget *i; if (SP_IS_STOP(sl->data)){ - stop = SP_STOP(sl->data); - i = gtk_menu_item_new(); + SPStop *stop = SP_STOP(sl->data); + GtkWidget *i = gtk_menu_item_new(); gtk_widget_show(i); g_object_set_data(G_OBJECT(i), "stop", stop); GtkWidget *hb = gtk_hbox_new(FALSE, 4); @@ -600,11 +600,8 @@ static void sp_grad_edit_select(GtkOptionMenu *mnu, GtkWidget *tbl) blocked = TRUE; SPColorSelector *csel = (SPColorSelector*)g_object_get_data(G_OBJECT(tbl), "cselector"); - guint32 const c = sp_stop_get_rgba32(stop); - csel->base->setAlpha(SP_RGBA32_A_F(c)); - SPColor color( SP_RGBA32_R_F(c), SP_RGBA32_G_F(c), SP_RGBA32_B_F(c) ); // set its color, from the stored array - csel->base->setColor( color ); + csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity ); GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offspn")); GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offslide")); @@ -1017,19 +1014,15 @@ static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradien if (gradient) { gtk_widget_set_sensitive(widget, TRUE); - sp_gradient_ensure_vector(gradient); + gradient->ensureVector(); GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(widget), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); - guint32 const c = sp_stop_get_rgba32(stop); - /// get the color selector + // get the color selector SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data(G_OBJECT(widget), "cselector")); - // set alpha - csel->base->setAlpha(SP_RGBA32_A_F(c)); - SPColor color( SP_RGBA32_R_F(c), SP_RGBA32_G_F(c), SP_RGBA32_B_F(c) ); - // set color - csel->base->setColor( color ); + + csel->base->setColorAlpha( stop->getEffectiveColor(), stop->opacity ); /* Fill preview */ GtkWidget *w = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "preview")); @@ -1140,7 +1133,7 @@ static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *o sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } - sp_gradient_ensure_vector(ngr); + ngr->ensureVector(); GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(object), "stopmenu")); SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); @@ -1154,10 +1147,6 @@ static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *o static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object) { - SPColor color; - float alpha; - guint32 rgb; - if (blocked) { return; } @@ -1175,7 +1164,7 @@ static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *o sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } - sp_gradient_ensure_vector(ngr); + ngr->ensureVector(); /* Set start parameters */ /* We rely on normalized vector, i.e. stops HAVE to exist */ @@ -1185,14 +1174,13 @@ static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *o SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); csel = static_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(object), "cselector")); + SPColor color; + float alpha = 0; csel->base->getColorAlpha( color, alpha ); - rgb = color.toRGBA32( 0x00 ); sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", stop->offset); Inkscape::CSSOStringStream os; - gchar c[64]; - sp_svg_write_color(c, sizeof(c), rgb); - os << "stop-color:" << c << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; + os << "stop-color:" << color.toString() << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; SP_OBJECT_REPR(stop)->setAttribute("style", os.str().c_str()); // g_snprintf(c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, static_cast<gdouble>(alpha)); //SP_OBJECT_REPR(stop)->setAttribute("style", c); diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp index d3092669a..cf6a04a3c 100644 --- a/src/widgets/paint-selector.cpp +++ b/src/widgets/paint-selector.cpp @@ -538,8 +538,8 @@ void SPPaintSelector::pushAttrsToGradient( SPGradient *gr ) const SPGradientUnits units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX; SPGradientSpread spread = SP_GRADIENT_SPREAD_PAD; getGradientProperties( units, spread ); - sp_gradient_set_units(gr, units); - sp_gradient_set_spread(gr, spread); + gr->setUnits(units); + gr->setSpread(spread); SP_OBJECT(gr)->updateRepr(); } @@ -1082,11 +1082,10 @@ static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSele SwatchSelector *swatchsel = 0; if (psel->mode == SPPaintSelector::MODE_SWATCH){ - /* Already have pattern menu */ swatchsel = static_cast<SwatchSelector*>(g_object_get_data(G_OBJECT(psel->selector), "swatch-selector")); } else { sp_paint_selector_clear_frame(psel); - /* Create new gradient selector */ + // Create new gradient selector SwatchSelector *swatchsel = new SwatchSelector(); swatchsel->show(); @@ -1102,6 +1101,7 @@ static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSele gtk_frame_set_label(GTK_FRAME(psel->frame), _("Swatch fill")); } + #ifdef SP_PS_VERBOSE g_print("Swatch req\n"); #endif diff --git a/src/widgets/sp-color-scales.cpp b/src/widgets/sp-color-scales.cpp index e41b81e5c..d20cf65ef 100644 --- a/src/widgets/sp-color-scales.cpp +++ b/src/widgets/sp-color-scales.cpp @@ -11,7 +11,6 @@ #include "../dialogs/dialog-events.h" #include "sp-color-scales.h" #include "svg/svg-icc-color.h" -#include "svg/svg-device-color.h" #define CSC_CHANNEL_R (1 << 0) #define CSC_CHANNEL_G (1 << 1) @@ -232,12 +231,6 @@ void ColorScales::_recalcColor( gboolean changing ) case SP_COLOR_SCALES_MODE_CMYK: { _getCmykaFloatv( c ); - color.device = new SVGDeviceColor(); - color.device->type=DEVICE_CMYK; - color.device->colors.clear(); - for (int i=0;i<4;i++){ - color.device->colors.push_back(c[i]); - } float rgb[3]; sp_color_cmyk_to_rgb_floatv( rgb, c[0], c[1], c[2], c[3] ); @@ -483,19 +476,12 @@ void ColorScales::setMode(SPColorScalesMode mode) gtk_widget_show (_b[4]); _updating = TRUE; - if (_color.device && _color.device->type == DEVICE_CMYK){ - setScaled( _a[0], _color.device->colors[0] ); - setScaled( _a[1], _color.device->colors[1] ); - setScaled( _a[2], _color.device->colors[2] ); - setScaled( _a[3], _color.device->colors[3] ); - } else { - //If we still dont have a device-color, convert from rbga - sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); - setScaled( _a[0], c[0] ); - setScaled( _a[1], c[1] ); - setScaled( _a[2], c[2] ); - setScaled( _a[3], c[3] ); - } + sp_color_rgb_to_cmyk_floatv (c, rgba[0], rgba[1], rgba[2]); + setScaled( _a[0], c[0] ); + setScaled( _a[1], c[1] ); + setScaled( _a[2], c[2] ); + setScaled( _a[3], c[3] ); + setScaled( _a[4], rgba[3] ); _updating = FALSE; _updateSliders( CSC_CHANNELS_ALL ); diff --git a/src/widgets/swatch-selector.cpp b/src/widgets/swatch-selector.cpp index a6f5133b7..88abca358 100644 --- a/src/widgets/swatch-selector.cpp +++ b/src/widgets/swatch-selector.cpp @@ -116,7 +116,7 @@ void SwatchSelector::_changedCb(SPColorSelector */*csel*/, void *data) // TODO replace with proper - sp_gradient_vector_widget_load_gradient(GTK_WIDGET(swsel->_gsel), ngr); } - sp_gradient_ensure_vector(ngr); + ngr->ensureVector(); SPStop* stop = ngr->getFirstStop(); @@ -169,10 +169,9 @@ void SwatchSelector::connectchangedHandler( GCallback handler, void *data ) void SwatchSelector::setVector(SPDocument */*doc*/, SPGradient *vector) { //GtkVBox * box = gobj(); - _gsel->setVector((vector) ? SP_OBJECT_DOCUMENT(vector) : 0, vector); - if (vector) { + if ( vector && vector->isSolid() ) { SPStop* stop = vector->getFirstStop(); guint32 const colorVal = sp_stop_get_rgba32(stop); @@ -180,6 +179,9 @@ void SwatchSelector::setVector(SPDocument */*doc*/, SPGradient *vector) SPColor color( SP_RGBA32_R_F(colorVal), SP_RGBA32_G_F(colorVal), SP_RGBA32_B_F(colorVal) ); // set its color, from the stored array _csel->base->setColor( color ); + gtk_widget_show_all( GTK_WIDGET(_csel) ); + } else { + gtk_widget_hide( GTK_WIDGET(_csel) ); } /* diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index e2f85a627..08e463ea4 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -992,7 +992,8 @@ static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType han gtk_widget_set_sensitive(tb, FALSE); GtkWidget *hb = 0; - if ( UXManager::getInstance()->isFloatWindowProblem() ) { + gboolean forceFloatAllowed = Inkscape::Preferences::get()->getBool("/options/workarounds/floatallowed", false); + if ( UXManager::getInstance()->isFloatWindowProblem() && !forceFloatAllowed ) { hb = gtk_event_box_new(); // A simple, neutral container. } else { hb = gtk_handle_box_new(); @@ -6860,7 +6861,7 @@ static void sp_text_wordspacing_value_changed( GtkAdjustment *adj, GObject *tbl // Set css word-spacing SPCSSAttr *css = sp_repr_css_attr_new (); Inkscape::CSSOStringStream osfs; - osfs << adj->value; + osfs << adj->value << "px"; // For now always use px sp_repr_css_set_property (css, "word-spacing", osfs.str().c_str()); // Apply word-spacing to selected objects. @@ -6899,7 +6900,7 @@ static void sp_text_letterspacing_value_changed( GtkAdjustment *adj, GObject *tb // Set css letter-spacing SPCSSAttr *css = sp_repr_css_attr_new (); Inkscape::CSSOStringStream osfs; - osfs << adj->value; + osfs << adj->value << "px"; // For now always use px sp_repr_css_set_property (css, "letter-spacing", osfs.str().c_str()); // Apply letter-spacing to selected objects. |
