diff options
| author | Jabiertxof <jtx@jtx> | 2017-01-24 14:16:06 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx> | 2017-01-24 14:16:06 +0000 |
| commit | b25ebda10578c5d697db9716e3c2b70950d33e45 (patch) | |
| tree | 4635b8b3f65600cfd54b5465b906ae42165b0674 /src | |
| parent | Fix some bugs (diff) | |
| parent | fix nodes reverting back during editing (diff) | |
| download | inkscape-b25ebda10578c5d697db9716e3c2b70950d33e45.tar.gz inkscape-b25ebda10578c5d697db9716e3c2b70950d33e45.zip | |
fixing to new trunk
(bzr r15142.1.38)
Diffstat (limited to 'src')
238 files changed, 4574 insertions, 3604 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1102bf4bf..47aa02ef1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -521,7 +521,6 @@ set(INKSCAPE_TARGET_LIBS depixelize_LIB util_LIB gc_LIB - ${INKSCAPE_LIBS} ) # Build everything except main and inkview.c in a shared library. @@ -534,7 +533,7 @@ if(WITH_DBUS) endif() # Link the inkscape_base library against all external dependencies -target_link_libraries(inkscape_base ${INKSCAPE_TARGET_LIBS}) +target_link_libraries(inkscape_base PRIVATE ${INKSCAPE_TARGET_LIBS} PUBLIC ${INKSCAPE_LIBS}) # Link inkscape and inkview against inkscape_base target_link_libraries(inkscape inkscape_base) diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 6ebf40f2c..cbd3475b4 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -307,7 +307,7 @@ void ColorProfile::set(unsigned key, gchar const *value) { //# 1. Get complete URI of document gchar const *docbase = doc->getURI(); - gchar* escaped = g_uri_escape_string(this->href, "!*'();:@=+$,/?#", TRUE); + gchar* escaped = g_uri_escape_string(this->href, "!*'();@=+$,/?#", TRUE); //g_message("docbase:%s\n", docbase); //org::w3c::dom::URI docUri(docbase); @@ -781,7 +781,7 @@ std::vector<std::pair<Glib::ustring, bool> > ColorProfile::getBaseProfileDirs() static bool isIccFile( gchar const *filepath ) { bool isIccFile = false; - struct stat st; + GStatBuf st; if ( g_stat(filepath, &st) == 0 && (st.st_size > 128) ) { //0-3 == size //36-39 == 'acsp' 0x61637370 diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 65e511245..4c07c76ea 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -209,7 +209,7 @@ sp_desktop_set_style(Inkscape::ObjectSet *set, SPDesktop *desktop, SPCSSAttr *cs if (!change) return; -// 2. Emit signal +// 2. Emit signal... See desktop->connectSetStyle in text-tool, tweak-tool, and gradient-drag. bool intercepted = desktop->_set_style_signal.emit(css); /** \todo @@ -1045,6 +1045,7 @@ objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_r bool lineheight_normal = false; bool lineheight_unit_proportional = false; bool lineheight_unit_absolute = false; + bool lineheight_set = false; // Set true if any object has lineheight set. double size_prev = 0; double letterspacing_prev = 0; @@ -1130,6 +1131,9 @@ objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_r lineheight_normal = false; lineheight += lineheight_current * doc_scale; } + if (style->line_height.set) { + lineheight_set = true; + } if ((size_prev != 0 && style->font_size.computed != size_prev) || (letterspacing_prev != 0 && style->letter_spacing.computed != letterspacing_prev) || @@ -1205,6 +1209,9 @@ objects_query_fontnumbers (const std::vector<SPItem*> &objects, SPStyle *style_r } } + // Used by text toolbar unset 'line-height' + style_res->line_height.set = lineheight_set; + if (texts > 1) { if (different || different_lineheight) { return QUERY_STYLE_MULTIPLE_AVERAGED; @@ -1907,7 +1914,7 @@ sp_desktop_query_style_from_list (const std::vector<SPItem*> &list, SPStyle *sty int sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property) { - // Used by text tool and in gradient dragging + // Used by text tool and in gradient dragging. See connectQueryStyle. int ret = desktop->_query_style_signal.emit(style, property); if (ret != QUERY_STYLE_NOTHING) diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp index a7625f4a1..8045007e7 100644 --- a/src/display/cairo-utils.cpp +++ b/src/display/cairo-utils.cpp @@ -13,7 +13,6 @@ #endif #include "display/cairo-utils.h" -#include <arpa/inet.h> #include <stdexcept> #include <glib/gstdio.h> @@ -291,7 +290,7 @@ Pixbuf *Pixbuf::create_from_file(std::string const &fn) if (!g_file_test(fn.c_str(), G_FILE_TEST_EXISTS)) { return NULL; } - struct stat stdir; + GStatBuf stdir; int val = g_stat(fn.c_str(), &stdir); if (val == 0 && stdir.st_mode & S_IFDIR){ return NULL; diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 421a39fbd..94ee4ccfd 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -107,7 +107,12 @@ attach_all(Gtk::Grid &table, Gtk::Widget const *const arr[], unsigned size, int table.attach(const_cast<Gtk::Widget&>(*arr[i+1]), 1, r, 2, 1); } else if (arr[i]) { Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i])); +#if GTK_CHECK_VERSION(3,16,0) + label.set_xalign(0.0); + label.set_yalign(0.5); +#else label.set_alignment (0.0); +#endif label.set_hexpand(); label.set_valign(Gtk::ALIGN_CENTER); table.attach(label, 0, r, 3, 1); diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index fa45fe02c..cf7d04555 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -139,7 +139,7 @@ grid_canvasitem_update (SPCanvasItem *item, Geom::Affine const &affine, unsigned }; CanvasGrid::CanvasGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument *in_doc, GridType type) - : visible(true), gridtype(type) + : visible(true), gridtype(type), legacy(false), pixel(false) { repr = in_repr; doc = in_doc; @@ -413,7 +413,12 @@ static inline void attach_all(Gtk::Grid &table, Gtk::Widget const *const arr[], table.attach(const_cast<Gtk::Widget&>(*arr[i+1]), 1, r, 2, 1); } else if (arr[i]) { Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i])); +#if GTK_CHECK_VERSION(3,16,0) + label.set_xalign(0.0); + label.set_yalign(0.5); +#else label.set_alignment (0.0); +#endif label.set_hexpand(); label.set_valign(Gtk::ALIGN_CENTER); table.attach(label, 0, r, 3, 1); @@ -528,6 +533,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' origin[Geom::X] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' origin[Geom::X] = q.quantity * scale_x; @@ -541,6 +550,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' origin[Geom::Y] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' origin[Geom::Y] = q.quantity * scale_y; @@ -559,6 +572,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' spacing[Geom::X] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' spacing[Geom::X] = q.quantity * scale_x; @@ -578,6 +595,10 @@ CanvasXYGrid::readRepr() if( q.unit->type == UNIT_TYPE_LINEAR ) { // Legacy grid not in 'user units' spacing[Geom::Y] = q.value("px"); + legacy = true; + if (q.unit->abbr == "px" ) { + pixel = true; + } } else { // Grid in 'user units' spacing[Geom::Y] = q.quantity * scale_y; @@ -798,7 +819,23 @@ CanvasXYGrid::updateWidgets() */ } +// For correcting old SVG Inkscape files +void +CanvasXYGrid::Scale (Geom::Scale const &scale ) { + origin *= scale; + spacing *= scale; + // Write out in 'user-units' + Inkscape::SVGOStringStream os_x, os_y, ss_x, ss_y; + os_x << origin[Geom::X]; + os_y << origin[Geom::Y]; + ss_x << spacing[Geom::X]; + ss_y << spacing[Geom::Y]; + repr->setAttribute("originx", os_x.str().c_str()); + repr->setAttribute("originy", os_y.str().c_str()); + repr->setAttribute("spacingx", ss_x.str().c_str()); + repr->setAttribute("spacingy", ss_y.str().c_str()); +} void CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) diff --git a/src/display/canvas-grid.h b/src/display/canvas-grid.h index 557bd6dab..bf520467a 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -101,6 +101,9 @@ public: static void on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data); + bool isLegacy() const { return legacy; } + bool isPixel() const { return pixel; } + bool isVisible() const { return (isEnabled() &&visible); }; bool isEnabled() const; @@ -118,7 +121,11 @@ protected: GridType gridtype; -private: + // For dealing with old Inkscape SVG files (pre 0.92) + bool legacy; + bool pixel; + + private: CanvasGrid(const CanvasGrid&); CanvasGrid& operator=(const CanvasGrid&); }; @@ -129,6 +136,7 @@ public: CanvasXYGrid(SPNamedView * nv, Inkscape::XML::Node * in_repr, SPDocument * in_doc); virtual ~CanvasXYGrid(); + virtual void Scale (Geom::Scale const &scale); virtual void Update (Geom::Affine const &affine, unsigned int flags); virtual void Render (SPCanvasBuf *buf); diff --git a/src/display/curve.cpp b/src/display/curve.cpp index 1998e9bec..6e662b17b 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -335,6 +335,20 @@ SPCurve::is_closed() const } /** + * True if both curves are equal + */ +bool +SPCurve::is_equal(SPCurve * other) const +{ + if(other == NULL) { + return false; + } else if(_pathv == other->get_pathvector()){ + return true; + } + return false; +} + +/** * Return last pathsegment (possibly the closing path segment) of the last path in PathVector or NULL. * If the last path is empty (contains only a moveto), the function returns NULL */ diff --git a/src/display/curve.h b/src/display/curve.h index 72e6df95c..8375e1105 100644 --- a/src/display/curve.h +++ b/src/display/curve.h @@ -45,6 +45,7 @@ public: bool is_empty() const; bool is_unset() const; bool is_closed() const; + bool is_equal(SPCurve * other) const; Geom::Curve const * last_segment() const; Geom::Path const * last_path() const; Geom::Curve const * first_segment() const; diff --git a/src/display/drawing-group.cpp b/src/display/drawing-group.cpp index 1a9cbfdcc..018f23e74 100644 --- a/src/display/drawing-group.cpp +++ b/src/display/drawing-group.cpp @@ -95,6 +95,7 @@ DrawingGroup::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne if (stop_at == NULL) { // normal rendering for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { + i->setAntialiasing(_antialias); i->render(dc, area, flags, stop_at); } } else { @@ -103,10 +104,12 @@ DrawingGroup::_renderItem(DrawingContext &dc, Geom::IntRect const &area, unsigne if (&*i == stop_at) return RENDER_OK; // do not render the stop_at item at all if (i->isAncestorOf(stop_at)) { // render its ancestors without masks, opacity or filters + i->setAntialiasing(_antialias); i->render(dc, area, flags | RENDER_FILTER_BACKGROUND, stop_at); // stop further rendering return RENDER_OK; } else { + i->setAntialiasing(_antialias); i->render(dc, area, flags, stop_at); } } diff --git a/src/display/drawing-item.cpp b/src/display/drawing-item.cpp index 89ca66dc4..c4af81efc 100644 --- a/src/display/drawing-item.cpp +++ b/src/display/drawing-item.cpp @@ -132,7 +132,7 @@ DrawingItem::DrawingItem(Drawing &drawing) , _propagate(0) // , _renders_opacity(0) , _pick_children(0) - , _antialias(1) + , _antialias(2) , _isolation(SP_CSS_ISOLATION_AUTO) , _mix_blend_mode(SP_CSS_BLEND_NORMAL) {} @@ -291,7 +291,7 @@ DrawingItem::setOpacity(float opacity) } void -DrawingItem::setAntialiasing(bool a) +DrawingItem::setAntialiasing(unsigned a) { if (_antialias != a) { _antialias = a; @@ -699,10 +699,21 @@ DrawingItem::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flag Geom::OptIntRect carea = Geom::intersect(area, _drawbox); if (!carea) return RENDER_OK; - if (_antialias) { - cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_DEFAULT); - } else { - cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE); + switch(_antialias){ + case 0: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_NONE); + break; + case 1: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_FAST); + break; + case 2: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_GOOD); + break; + case 3: + cairo_set_antialias(dc.raw(), CAIRO_ANTIALIAS_BEST); + break; + default: // should not happen + g_assert_not_reached(); } // render from cache if possible diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index 3c593ceaa..21f6ffacc 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -115,7 +115,7 @@ public: virtual void setStyle(SPStyle *style, SPStyle *context_style = NULL); virtual void setChildrenStyle(SPStyle *context_style); void setOpacity(float opacity); - void setAntialiasing(bool a); + void setAntialiasing(unsigned a); void setIsolation(unsigned isolation); // CSS Compositing and Blending void setBlendMode(unsigned blend_mode); void setTransform(Geom::Affine const &trans); @@ -222,7 +222,7 @@ protected: //unsigned _renders_opacity : 1; ///< Whether object needs temporary surface for opacity unsigned _pick_children : 1; ///< For groups: if true, children are returned from pick(), /// otherwise the group is returned - unsigned _antialias : 1; ///< Whether to use antialiasing + unsigned _antialias : 2; ///< antialiasing level (NONE/FAST/GOOD(DEFAULT)/BEST) unsigned _isolation : 1; unsigned _mix_blend_mode : 4; diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 1280a2db9..21af7b200 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -599,7 +599,7 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are } { Inkscape::DrawingContext::Save save(dc); - if (!_style || ! _style->vector_effect.computed == SP_VECTOR_EFFECT_NON_SCALING_STROKE) { + if (!_style || !(_style->vector_effect.computed == SP_VECTOR_EFFECT_NON_SCALING_STROKE)) { dc.transform(_ctm); } if (has_stroke) { diff --git a/src/display/drawing.cpp b/src/display/drawing.cpp index eadd7e528..71fb94be0 100644 --- a/src/display/drawing.cpp +++ b/src/display/drawing.cpp @@ -167,10 +167,14 @@ Drawing::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned fl } void -Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags) +Drawing::render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags, int antialiasing) { if (_root) { + int prev_a = _root->_antialias; + if(antialiasing >= 0) + _root->setAntialiasing(antialiasing); _root->render(dc, area, flags); + _root->setAntialiasing(prev_a); } if (colorMode() == COLORMODE_GRAYSCALE) { diff --git a/src/display/drawing.h b/src/display/drawing.h index 0c12b1510..e472c8f5b 100644 --- a/src/display/drawing.h +++ b/src/display/drawing.h @@ -68,7 +68,7 @@ public: void setGrayscaleMatrix(double value_matrix[20]); void update(Geom::IntRect const &area = Geom::IntRect::infinite(), UpdateContext const &ctx = UpdateContext(), unsigned flags = DrawingItem::STATE_ALL, unsigned reset = 0); - void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags = 0); + void render(DrawingContext &dc, Geom::IntRect const &area, unsigned flags = 0, int antialiasing = -1); DrawingItem *pick(Geom::Point const &p, double delta, unsigned flags); sigc::signal<void, DrawingItem *> signal_request_update; diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index 17deea16d..f2271e0c6 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -287,7 +287,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap } else if (p.getSource() != SNAPSOURCE_UNDEFINED) { tooltip_str = g_strdup(source_name); } - double fontsize = prefs->getInt("/tools/measure/fontsize"); + double fontsize = prefs->getDouble("/tools/measure/fontsize", 10.0); if (tooltip_str) { Geom::Point tooltip_pos = p.getPoint(); @@ -327,7 +327,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap SP_CTRLRECT(box)->setDashed(true); SP_CTRLRECT(box)->pickable = false; // See the extensive comment above sp_canvas_item_move_to_z(box, 0); - _snaptarget_bbox = _desktop->add_temporary_canvasitem(box, timeout_val); + _snaptarget_bbox = _desktop->add_temporary_canvasitem(box, timeout_val*1000.0); } } } diff --git a/src/display/sodipodi-ctrl.cpp b/src/display/sodipodi-ctrl.cpp index 327fbce1f..27b6988c5 100644 --- a/src/display/sodipodi-ctrl.cpp +++ b/src/display/sodipodi-ctrl.cpp @@ -18,6 +18,7 @@ enum { ARG_MODE, ARG_ANCHOR, ARG_SIZE, + ARG_ANGLE, ARG_FILLED, ARG_FILL_COLOR, ARG_STROKED, @@ -53,7 +54,7 @@ sp_ctrl_class_init (SPCtrlClass *klass) g_object_class_install_property (g_object_class, ARG_SIZE, g_param_spec_double ("size", "size", "Size", 0.0, G_MAXDOUBLE, 8.0, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, - ARG_PIXBUF, g_param_spec_pointer ("pixbuf", "pixbuf", "Pixbuf", (GParamFlags) G_PARAM_READWRITE)); + ARG_ANGLE, g_param_spec_double ("angle", "angle", "Angle", -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, ARG_FILLED, g_param_spec_boolean ("filled", "filled", "Filled", TRUE, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, @@ -62,6 +63,8 @@ sp_ctrl_class_init (SPCtrlClass *klass) ARG_STROKED, g_param_spec_boolean ("stroked", "stroked", "Stroked", FALSE, (GParamFlags) G_PARAM_READWRITE)); g_object_class_install_property (g_object_class, ARG_STROKE_COLOR, g_param_spec_int ("stroke_color", "stroke_color", "Stroke Color", G_MININT, G_MAXINT, 0x000000ff, (GParamFlags) G_PARAM_READWRITE)); + g_object_class_install_property (g_object_class, + ARG_PIXBUF, g_param_spec_pointer ("pixbuf", "pixbuf", "Pixbuf", (GParamFlags) G_PARAM_READWRITE)); item_class->destroy = sp_ctrl_destroy; item_class->update = sp_ctrl_update; @@ -95,6 +98,9 @@ sp_ctrl_set_property(GObject *object, guint prop_id, const GValue *value, GParam ctrl->height = ctrl->width; ctrl->defined = (ctrl->width > 0); break; + case ARG_ANGLE: + ctrl->angle = (double)g_value_get_double(value); + break; case ARG_FILLED: ctrl->filled = g_value_get_boolean(value); break; @@ -151,6 +157,10 @@ sp_ctrl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec g_value_set_double(value, ctrl->width); break; + case ARG_ANGLE: + g_value_set_double(value, ctrl->angle); + break; + case ARG_FILLED: g_value_set_boolean(value, ctrl->filled); break; @@ -192,6 +202,7 @@ sp_ctrl_init (SPCtrl *ctrl) ctrl->stroked = 0; ctrl->fill_color = 0x000000ff; ctrl->stroke_color = 0x000000ff; + ctrl->angle = 0.0; new (&ctrl->box) Geom::IntRect(0,0,0,0); ctrl->cache = NULL; @@ -291,6 +302,23 @@ sp_ctrl_point (SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item) return 1e18; } +bool +sp_point_inside_line(Geom::Point a, Geom::Point b, Geom::Point c, double tolerance = 0.1){ + //http://stackoverflow.com/questions/328107/how-can-you-determine-a-point-is-between-two-other-points-on-a-line-segment + return Geom::are_near(Geom::distance(a,c) + Geom::distance(c,b) , Geom::distance(a,b), tolerance); +} + +bool +sp_point_inside_triangle(Geom::Point p1,Geom::Point p2,Geom::Point p3, Geom::Point point){ + using Geom::X; + using Geom::Y; + double denominator = (p1[X]*(p2[Y] - p3[Y]) + p1[Y]*(p3[X] - p2[X]) + p2[X]*p3[Y] - p2[Y]*p3[X]); + double t1 = (point[X]*(p3[Y] - p1[Y]) + point[Y]*(p1[X] - p3[X]) - p1[X]*p3[Y] + p1[Y]*p3[X]) / denominator; + double t2 = (point[X]*(p2[Y] - p1[Y]) + point[Y]*(p1[X] - p2[X]) - p1[X]*p2[Y] + p1[Y]*p2[X]) / -denominator; + double see = t1 + t2; + return 0 <= t1 && t1 <= 1 && 0 <= t2 && t2 <= 1 && see <= 1; +} + static void sp_ctrl_build_cache (SPCtrl *ctrl) { @@ -316,7 +344,7 @@ sp_ctrl_build_cache (SPCtrl *ctrl) } else { stroke_color = fill_color; } - + gint32 stroke_color_smooth = SP_RGBA32_F_COMPOSE(SP_RGBA32_R_F(stroke_color), SP_RGBA32_G_F(stroke_color), SP_RGBA32_B_F(stroke_color), 0.15); width = (ctrl->width * 2 +1); height = (ctrl->height * 2 +1); c = ctrl->width; // Only used for pre-set square drawing @@ -325,7 +353,24 @@ sp_ctrl_build_cache (SPCtrl *ctrl) if (ctrl->cache) delete[] ctrl->cache; ctrl->cache = new guint32[size]; - + Geom::Point point; + Geom::Point p1; + Geom::Point p2; + Geom::Point p3; + if(ctrl->shape == SP_CTRL_SHAPE_TRIANGLE){ + Geom::Affine m = Geom::Translate(Geom::Point(-width/2.0,-height/2.0)); + m *= Geom::Rotate(-ctrl->angle); + m *= Geom::Translate(Geom::Point(width/2.0, height/2.0)); + p1 = Geom::Point(0,height/2); + p2 = Geom::Point(width - (width/M_PI), height/M_PI); + p3 = Geom::Point(width - (width/M_PI), height-(height/M_PI)); + p1 *= m; + p2 *= m; + p3 *= m; + p1 = p1.floor(); + p2 = p2.floor(); + p3 = p3.floor(); + } switch (ctrl->shape) { case SP_CTRL_SHAPE_SQUARE: p = ctrl->cache; @@ -407,6 +452,29 @@ sp_ctrl_build_cache (SPCtrl *ctrl) ctrl->build = TRUE; break; + case SP_CTRL_SHAPE_TRIANGLE: + p = ctrl->cache; + for(y = 0; y < height; y++) { + for(x = 0; x < width; x++) { + point = Geom::Point(x,y); + if (sp_point_inside_triangle(p1, p2, p3, point)) { + p[(y*width)+x] = fill_color; + } else if (point == p1 || point == p2 || point == p3 || sp_point_inside_line(p1, p2, point, 0.2) || + sp_point_inside_line(p3, p1, point, 0.2)) + { + p[(y*width)+x] = stroke_color; + } else if (sp_point_inside_line(p1, p2, point, 0.5) || + sp_point_inside_line(p3, p1, point, 0.5)) + { + p[(y*width)+x] = stroke_color_smooth; + } else { + p[(y*width)+x] = 0; + } + } + } + ctrl->build = TRUE; + break; + case SP_CTRL_SHAPE_CROSS: p = ctrl->cache; for (y = 0; y < height; y++) { diff --git a/src/display/sodipodi-ctrl.h b/src/display/sodipodi-ctrl.h index ecdb896a7..ac5ac9442 100644 --- a/src/display/sodipodi-ctrl.h +++ b/src/display/sodipodi-ctrl.h @@ -22,6 +22,7 @@ typedef enum { SP_CTRL_SHAPE_SQUARE, SP_CTRL_SHAPE_DIAMOND, SP_CTRL_SHAPE_CIRCLE, + SP_CTRL_SHAPE_TRIANGLE, SP_CTRL_SHAPE_CROSS, SP_CTRL_SHAPE_BITMAP, SP_CTRL_SHAPE_IMAGE @@ -46,6 +47,7 @@ struct SPCtrl : public SPCanvasItem { guint stroked : 1; guint32 fill_color; guint32 stroke_color; + gdouble angle; Geom::IntRect box; /* NB! x1 & y1 are included */ guint32 *cache; diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 427cec20a..68eae0a65 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -21,6 +21,8 @@ # include <config.h> #endif +#include <gdkmm/devicemanager.h> +#include <gdkmm/display.h> #include <gdkmm/rectangle.h> #include <cairomm/region.h> @@ -67,6 +69,14 @@ struct SPCanvasGroupClass { SPCanvasItemClass parent_class; }; +static void ungrab_default_client_pointer(guint32 const time = GDK_CURRENT_TIME) +{ + auto const display = Gdk::Display::get_default(); + auto const dm = display->get_device_manager(); + auto const device = dm->get_client_pointer(); + device->ungrab(time); +} + /** * A group of items. */ @@ -327,10 +337,7 @@ void sp_canvas_item_dispose(GObject *object) if (item == item->canvas->_grabbed_item) { item->canvas->_grabbed_item = NULL; - - auto dm = gdk_display_get_device_manager(gdk_display_get_default()); - auto device = gdk_device_manager_get_client_pointer(dm); - gdk_device_ungrab(device, GDK_CURRENT_TIME); + ungrab_default_client_pointer(); } if (item == item->canvas->_focused_item) { @@ -653,10 +660,7 @@ void sp_canvas_item_ungrab(SPCanvasItem *item, guint32 etime) } item->canvas->_grabbed_item = NULL; - - auto dm = gdk_display_get_device_manager(gdk_display_get_default()); - auto device = gdk_device_manager_get_client_pointer(dm); - gdk_device_ungrab(device, etime); + ungrab_default_client_pointer(etime); } /** @@ -965,9 +969,7 @@ void SPCanvas::shutdownTransients() if (_grabbed_item) { _grabbed_item = NULL; - auto dm = gdk_display_get_device_manager(gdk_display_get_default()); - auto device = gdk_device_manager_get_client_pointer(dm); - gdk_device_ungrab(device, GDK_CURRENT_TIME); + ungrab_default_client_pointer(); } removeIdle(); } @@ -1691,11 +1693,12 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1) // Save the mouse location gint x, y; - auto dm = gdk_display_get_device_manager(gdk_display_get_default()); - auto device = gdk_device_manager_get_client_pointer(dm); + auto const display = Gdk::Display::get_default(); + auto const dm = display->get_device_manager(); + auto const device = dm->get_client_pointer(); gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(this)), - device, + device->gobj(), &x, &y, NULL); setup.mouse_loc = sp_canvas_window_to_world(this, Geom::Point(x,y)); diff --git a/src/display/sp-ctrlcurve.cpp b/src/display/sp-ctrlcurve.cpp index 2e0e8105b..79ef20d6c 100644 --- a/src/display/sp-ctrlcurve.cpp +++ b/src/display/sp-ctrlcurve.cpp @@ -47,6 +47,8 @@ sp_ctrlcurve_init(SPCtrlCurve *ctrlcurve) // Points are initialized to 0,0 ctrlcurve->rgba = 0x0000ff7f; ctrlcurve->item=NULL; + ctrlcurve->corner0 = -1; + ctrlcurve->corner1 = -1; } namespace { diff --git a/src/display/sp-ctrlcurve.h b/src/display/sp-ctrlcurve.h index 847944f38..56b3089d9 100644 --- a/src/display/sp-ctrlcurve.h +++ b/src/display/sp-ctrlcurve.h @@ -31,6 +31,9 @@ struct SPCtrlCurve : public SPCtrlLine { Geom::Point const &q2, Geom::Point const &q3); Geom::Point p0, p1, p2, p3; + + int corner0; // Used to store index of corner for finding dragger. + int corner1; }; GType sp_ctrlcurve_get_type(); diff --git a/src/display/sp-ctrlline.cpp b/src/display/sp-ctrlline.cpp index 6c5674935..c4ced2a33 100644 --- a/src/display/sp-ctrlline.cpp +++ b/src/display/sp-ctrlline.cpp @@ -52,6 +52,7 @@ static void sp_ctrlline_init(SPCtrlLine *ctrlline) ctrlline->rgba = 0x0000ff7f; ctrlline->s[Geom::X] = ctrlline->s[Geom::Y] = ctrlline->e[Geom::X] = ctrlline->e[Geom::Y] = 0.0; ctrlline->item=NULL; + ctrlline->is_fill = true; } namespace { diff --git a/src/display/sp-ctrlline.h b/src/display/sp-ctrlline.h index b2e222437..bac0cfa54 100644 --- a/src/display/sp-ctrlline.h +++ b/src/display/sp-ctrlline.h @@ -33,6 +33,8 @@ struct SPCtrlLine : public SPCanvasItem { SPItem *item; // the item to which this line belongs in some sense; may be NULL for some users + bool is_fill; // fill or stroke... used with meshes. + guint32 rgba; Geom::Point s; Geom::Point e; diff --git a/src/document.cpp b/src/document.cpp index a371fe1e5..8072ec53d 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -956,6 +956,7 @@ SPDocument::emitReconstructionFinish(void) priv->_reconstruction_finish_signal.emit(); // indicates that gradients are reloaded (to rebuild the Auto palette) priv->resources_changed_signals[g_quark_from_string("gradient")].emit(); + priv->resources_changed_signals[g_quark_from_string("filter")].emit(); /** diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index b94b6d019..0435f1396 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -116,8 +116,8 @@ CdrImportDialog::CdrImportDialog(const std::vector<RVNGString> &vec) // CONTROLS // Buttons - cancelbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-cancel"))); - okbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-ok"))); + cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); + okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); // Labels _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:"))); @@ -214,6 +214,13 @@ void CdrImportDialog::_setPreviewPage() SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { + #ifdef WIN32 + // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows + // therefore attempt to convert uri to the system codepage + // even if this is not possible the alternate short (8.3) file name will be used if available + uri = g_win32_locale_filename_from_utf8(uri); + #endif + RVNGFileStream input(uri); if (!libcdr::CDRDocument::isSupported(&input)) { diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index d4c5d95a3..826176bc0 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -205,7 +205,7 @@ unsigned int PrintEmf::begin(Inkscape::Extension::Print *mod, SPDocument *doc) } else { p = ansi_uri; } - snprintf(buff, sizeof(buff) - 1, "Inkscape %s (%s)\1%s\1", Inkscape::version_string, __DATE__, p); + snprintf(buff, sizeof(buff) - 1, "Inkscape %s \1%s\1", Inkscape::version_string, p); uint16_t *Description = U_Utf8ToUtf16le(buff, 0, NULL); int cbDesc = 2 + wchar16len(Description); // also count the final terminator (void) U_Utf16leEdit(Description, '\1', '\0'); // swap the temporary \1 characters for nulls @@ -1064,7 +1064,7 @@ void PrintEmf::do_clip_if_present(SPStyle const *style){ g_error("Fatal programming error in PrintEmf::image at U_EMRSAVEDC_set"); } (void) draw_pathv_to_EMF(combined_pathvector, tf); - rec = U_EMRSELECTCLIPPATH_set(U_RGN_OR); + rec = U_EMRSELECTCLIPPATH_set(U_RGN_COPY); if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) { g_error("Fatal programming error in PrintEmf::do_clip_if_present at U_EMRSELECTCLIPPATH_set"); } diff --git a/src/extension/internal/filter/filter.cpp b/src/extension/internal/filter/filter.cpp index 0907845f8..0408d516e 100644 --- a/src/extension/internal/filter/filter.cpp +++ b/src/extension/internal/filter/filter.cpp @@ -143,6 +143,7 @@ void Filter::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::Vie Inkscape::XML::Node * newfilterroot = xmldoc->createElement("svg:filter"); merge_filters(newfilterroot, filterdoc->root(), xmldoc); defsrepr->appendChild(newfilterroot); + document->doc()->priv->resources_changed_signals[g_quark_from_string("filter")].emit(); Glib::ustring url = "url(#"; url += newfilterroot->attribute("id"); url += ")"; diff --git a/src/extension/internal/pdfinput/pdf-input.cpp b/src/extension/internal/pdfinput/pdf-input.cpp index 0c22fa399..a57396b17 100644 --- a/src/extension/internal/pdfinput/pdf-input.cpp +++ b/src/extension/internal/pdfinput/pdf-input.cpp @@ -85,8 +85,8 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _poppler_doc = NULL; #endif // HAVE_POPPLER_CAIRO _pdf_doc = doc; - cancelbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-cancel"))); - okbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-ok"))); + cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); + okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:"))); // Page number @@ -157,9 +157,50 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) okbutton->set_can_focus(); okbutton->set_can_default(); okbutton->set_relief(Gtk::RELIEF_NORMAL); - this->get_action_area()->property_layout_style().set_value(Gtk::BUTTONBOX_END); + +#if GTK_CHECK_VERSION(3,16,0) + _labelSelect->set_xalign(0.5); + _labelSelect->set_yalign(0.5); + _labelTotalPages->set_xalign(0.5); + _labelTotalPages->set_yalign(0.5); + _labelPrecision->set_xalign(0.0); + _labelPrecision->set_yalign(0.5); + _labelPrecisionWarning->set_xalign(0.0); + _labelPrecisionWarning->set_yalign(0.5); + _labelPrecisionComment->set_xalign(0.5); + _labelPrecisionComment->set_yalign(0.5); +#else _labelSelect->set_alignment(0.5,0.5); - _labelSelect->set_padding(4,0); + _labelTotalPages->set_alignment(0.5,0.5); + _labelPrecision->set_alignment(0,0.5); + _labelPrecisionWarning->set_alignment(0,0.5); + _labelPrecisionComment->set_alignment(0.5,0.5); +#endif + +#if GTK_CHECK_VERSION(3,12,0) + _labelSelect->set_margin_start(4); + _labelSelect->set_margin_end(4); + _labelTotalPages->set_margin_start(4); + _labelTotalPages->set_margin_end(4); + _labelPrecision->set_margin_start(4); + _labelPrecision->set_margin_end(4); + _labelPrecisionWarning->set_margin_start(4); + _labelPrecisionWarning->set_margin_end(4); + _labelPrecisionComment->set_margin_start(4); + _labelPrecisionComment->set_margin_end(4); +#else + _labelSelect->set_margin_left(4); + _labelSelect->set_margin_right(4); + _labelTotalPages->set_margin_left(4); + _labelTotalPages->set_margin_right(4); + _labelPrecision->set_margin_left(4); + _labelPrecision->set_margin_right(4); + _labelPrecisionWarning->set_margin_left(4); + _labelPrecisionWarning->set_margin_right(4); + _labelPrecisionComment->set_margin_left(4); + _labelPrecisionComment->set_margin_right(4); +#endif + _labelSelect->set_justify(Gtk::JUSTIFY_LEFT); _labelSelect->set_line_wrap(false); _labelSelect->set_use_markup(false); @@ -169,8 +210,6 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _pageNumberSpin->set_numeric(true); _pageNumberSpin->set_digits(0); _pageNumberSpin->set_wrap(false); - _labelTotalPages->set_alignment(0.5,0.5); - _labelTotalPages->set_padding(4,0); _labelTotalPages->set_justify(Gtk::JUSTIFY_LEFT); _labelTotalPages->set_line_wrap(false); _labelTotalPages->set_use_markup(false); @@ -189,14 +228,10 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) vbox2->pack_start(*hbox3); _pageSettingsFrame->add(*vbox2); _pageSettingsFrame->set_border_width(4); - _labelPrecision->set_alignment(0,0.5); - _labelPrecision->set_padding(4,0); _labelPrecision->set_justify(Gtk::JUSTIFY_LEFT); _labelPrecision->set_line_wrap(true); _labelPrecision->set_use_markup(false); _labelPrecision->set_selectable(false); - _labelPrecisionWarning->set_alignment(0,0.5); - _labelPrecisionWarning->set_padding(4,0); _labelPrecisionWarning->set_justify(Gtk::JUSTIFY_LEFT); _labelPrecisionWarning->set_line_wrap(true); _labelPrecisionWarning->set_use_markup(true); @@ -213,6 +248,14 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _importViaInternal->set_active(true); _labelViaPoppler->set_line_wrap(true); _labelViaInternal->set_line_wrap(true); + +# if GTK_CHECK_VERSION(3,16,0) + _labelViaPoppler->set_xalign(0); + _labelViaInternal->set_xalign(0); +# else + _labelViaPoppler->set_alignment(0, 0.5); + _labelViaInternal->set_alignment(0, 0.5); +# endif #endif _fallbackPrecisionSlider->set_size_request(180,-1); @@ -222,8 +265,6 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) _fallbackPrecisionSlider->set_draw_value(true); _fallbackPrecisionSlider->set_value_pos(Gtk::POS_TOP); _labelPrecisionComment->set_size_request(90,-1); - _labelPrecisionComment->set_alignment(0.5,0.5); - _labelPrecisionComment->set_padding(4,0); _labelPrecisionComment->set_justify(Gtk::JUSTIFY_LEFT); _labelPrecisionComment->set_line_wrap(false); _labelPrecisionComment->set_use_markup(false); @@ -260,10 +301,10 @@ PdfImportDialog::PdfImportDialog(PDFDoc *doc, const gchar */*uri*/) // vbox3->pack_start(*hbox5, Gtk::PACK_SHRINK, 4); _importSettingsFrame->add(*vbox3); _importSettingsFrame->set_border_width(4); - vbox1->pack_start(*_pageSettingsFrame, Gtk::PACK_EXPAND_PADDING, 0); - vbox1->pack_start(*_importSettingsFrame, Gtk::PACK_EXPAND_PADDING, 0); + vbox1->pack_start(*_pageSettingsFrame, Gtk::PACK_SHRINK, 0); + vbox1->pack_start(*_importSettingsFrame, Gtk::PACK_SHRINK, 0); hbox1->pack_start(*vbox1); - hbox1->pack_start(*_previewArea, Gtk::PACK_EXPAND_WIDGET, 4); + hbox1->pack_start(*_previewArea, Gtk::PACK_SHRINK, 4); get_content_area()->set_homogeneous(false); get_content_area()->set_spacing(0); diff --git a/src/extension/internal/pdfinput/pdf-input.h b/src/extension/internal/pdfinput/pdf-input.h index c338207c1..a70d40a23 100644 --- a/src/extension/internal/pdfinput/pdf-input.h +++ b/src/extension/internal/pdfinput/pdf-input.h @@ -32,7 +32,6 @@ class Page; class PDFDoc; namespace Gtk { - class Alignment; class Button; class CheckButton; class ComboBoxText; diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp index 2de2d0ec6..78783aa2d 100644 --- a/src/extension/internal/vsd-input.cpp +++ b/src/extension/internal/vsd-input.cpp @@ -118,8 +118,8 @@ VsdImportDialog::VsdImportDialog(const std::vector<RVNGString> &vec) // CONTROLS // Buttons - cancelbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-cancel"))); - okbutton = Gtk::manage(new class Gtk::Button(Gtk::StockID("gtk-ok"))); + cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); + okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); // Labels _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:"))); @@ -216,6 +216,13 @@ void VsdImportDialog::_setPreviewPage() SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { + #ifdef WIN32 + // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows + // therefore attempt to convert uri to the system codepage + // even if this is not possible the alternate short (8.3) file name will be used if available + uri = g_win32_locale_filename_from_utf8(uri); + #endif + RVNGFileStream input(uri); if (!libvisio::VisioDocument::isSupported(&input)) { diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp index 54a14fc72..12457791b 100644 --- a/src/extension/internal/wpg-input.cpp +++ b/src/extension/internal/wpg-input.cpp @@ -81,6 +81,13 @@ namespace Internal { SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * uri) { + #ifdef WIN32 + // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows + // therefore attempt to convert uri to the system codepage + // even if this is not possible the alternate short (8.3) file name will be used if available + uri = g_win32_locale_filename_from_utf8(uri); + #endif + RVNGInputStream* input = new RVNGFileStream(uri); #if WITH_LIBWPG03 if (input->isStructured()) { diff --git a/src/extension/param/description.cpp b/src/extension/param/description.cpp index 326e75e4a..07aaa07cc 100644 --- a/src/extension/param/description.cpp +++ b/src/extension/param/description.cpp @@ -81,7 +81,8 @@ ParamDescription::get_widget (SPDocument * /*doc*/, Inkscape::XML::Node * /*node int padding = 12 + _indent; if (_mode == HEADER) { label = Gtk::manage(new Gtk::Label(Glib::ustring("<b>") +newguitext + Glib::ustring("</b>"), Gtk::ALIGN_START)); - label->set_padding(0,5); + label->set_margin_top(5); + label->set_margin_bottom(5); label->set_use_markup(true); padding = _indent; } else { diff --git a/src/extension/prefdialog.cpp b/src/extension/prefdialog.cpp index 1ca590e85..fcc88853d 100644 --- a/src/extension/prefdialog.cpp +++ b/src/extension/prefdialog.cpp @@ -8,7 +8,6 @@ */ #include "prefdialog.h" -#include <gtkmm/stock.h> #include <gtkmm/checkbutton.h> #include <gtkmm/separator.h> #include <glibmm/i18n.h> @@ -71,11 +70,8 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co if (_help == NULL) help_button->set_sensitive(false); */ - _button_cancel = add_button(_effect == NULL ? Gtk::Stock::CANCEL : Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL); - _button_cancel->set_use_stock(true); - - _button_ok = add_button(_effect == NULL ? Gtk::Stock::OK : Gtk::Stock::APPLY, Gtk::RESPONSE_OK); - _button_ok->set_use_stock(true); + _button_cancel = add_button(_effect == NULL ? _("_Cancel") : _("_Close"), Gtk::RESPONSE_CANCEL); + _button_ok = add_button(_effect == NULL ? _("_OK") : _("_Apply"), Gtk::RESPONSE_OK); set_default_response(Gtk::RESPONSE_OK); _button_ok->grab_focus(); diff --git a/src/file.cpp b/src/file.cpp index 49d7fece5..6613b1e65 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -11,9 +11,10 @@ * Jon A. Cruz <jon@joncruz.org> * Abhishek Sharma * David Xiong + * Tavmjong Bah * * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl> - * Copyright (C) 1999-2012 Authors + * Copyright (C) 1999-2016 Authors * Copyright (C) 2004 David Turner * Copyright (C) 2001-2002 Ximian, Inc. * @@ -30,9 +31,12 @@ # include "config.h" #endif +#include <gtkmm.h> + #include "ui/dialog/ocaldialogs.h" #include "desktop.h" +#include "extension/effect.h" #include "document-private.h" #include "document-undo.h" #include "ui/tools/tool-base.h" @@ -61,9 +65,13 @@ #include "event-log.h" #include "ui/dialog/font-substitution.h" -#include <gtkmm/main.h> -#include <glibmm/miscutils.h> -#include <glibmm/convert.h> +// For updating old Inkscape SVG files +#include "display/canvas-grid.h" +#include "sp-guide.h" +#include "selection-chemistry.h" +#include "persp3d.h" +#include "proj_pt.h" +#include "ui/shape-editor.h" using Inkscape::DocumentUndo; @@ -130,15 +138,6 @@ SPDesktop *sp_file_new(const std::string &templ) DocumentUndo::setUndoSensitive(doc, true); } - // Set viewBox if it doesn't exist - if (!doc->getRoot()->viewBox_set - && (doc->getRoot()->width.unit != SVGLength::PERCENT) - && (doc->getRoot()->height.unit != SVGLength::PERCENT)) { - DocumentUndo::setUndoSensitive(doc, false); - doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDisplayUnit()), doc->getHeight().value(doc->getDisplayUnit()))); - DocumentUndo::setUndoSensitive(doc, true); - } - SPDesktop *olddesktop = SP_ACTIVE_DESKTOP; if (olddesktop) olddesktop->setWaitingCursor(); @@ -241,6 +240,43 @@ sp_file_exit() } +// Quick and dirty internal backup function +bool sp_file_save_backup( Glib::ustring uri ) { + + Glib::ustring out = uri; + out.insert(out.find(".svg"),"_backup"); + + FILE *filein = Inkscape::IO::fopen_utf8name(uri.c_str(), "rb"); + if (!filein) { + std::cerr << "sp_file_save_backup: failed to open: " << uri << std::endl; + return false; + } + + FILE *fileout = Inkscape::IO::fopen_utf8name(out.c_str(), "wb"); + if (!fileout) { + std::cerr << "sp_file_save_backup: failed to open: " << out << std::endl; + fclose( filein ); + return false; + } + + int ch; + while ((ch = fgetc(filein)) != EOF) { + fputc(ch, fileout); + } + fflush(fileout); + + bool return_value = true; + if (ferror(fileout)) { + std::cerr << "sp_file_save_backup: error when writing to: " << out << std::endl; + return_value = false; + } + + fclose(filein); + fclose(fileout); + + return return_value; +} + /*###################### ## O P E N ######################*/ @@ -281,14 +317,6 @@ bool sp_file_open(const Glib::ustring &uri, } if (doc) { - // Set viewBox if it doesn't exist - if (!doc->getRoot()->viewBox_set - && (doc->getRoot()->width.unit != SVGLength::PERCENT) - && (doc->getRoot()->height.unit != SVGLength::PERCENT)) { - DocumentUndo::setUndoSensitive(doc, false); - doc->setViewBox(Geom::Rect::from_xywh(0, 0, doc->getWidth().value(doc->getDisplayUnit()), doc->getHeight().value(doc->getDisplayUnit()))); - DocumentUndo::setUndoSensitive(doc, true); - } SPDocument *existing = desktop ? desktop->getDocument() : NULL; if (existing && existing->virgin && replace_empty) { @@ -315,6 +343,498 @@ bool sp_file_open(const Glib::ustring &uri, root->original.inkscape = root->version.inkscape; root->original.svg = root->version.svg; + if (INKSCAPE.use_gui()) { + + // See if we need to offer the user a fix for the 90->96 px per inch change. + // std::cout << "SPFileOpen:" << std::endl; + // std::cout << " Version: " << sp_version_to_string(root->version.inkscape) << std::endl; + + if ( sp_version_inside_range( root->version.inkscape, 0, 1, 0, 92 ) ) { + + // std::cout << " SVG file from old Inkscape version detected: " + // << sp_version_to_string(root->version.inkscape) << std::endl; + static const double ratio = 90.0/96.0; + + bool need_fix_viewbox = false; + bool need_fix_units = false; + bool need_fix_guides = false; + bool need_fix_grid_mm = false; + bool need_fix_box3d = false; + bool did_scaling = false; + + // Check if potentially need viewbox or unit fix + switch (root->width.unit) { + case SP_CSS_UNIT_PC: + case SP_CSS_UNIT_PT: + case SP_CSS_UNIT_MM: + case SP_CSS_UNIT_CM: + case SP_CSS_UNIT_IN: + need_fix_viewbox = true; + break; + case SP_CSS_UNIT_NONE: + case SP_CSS_UNIT_PX: + need_fix_units = true; + break; + case SP_CSS_UNIT_EM: + case SP_CSS_UNIT_EX: + case SP_CSS_UNIT_PERCENT: + // OK + break; + default: + std::cerr << "sp_file_open: Unhandled width unit!" << std::endl; + } + + switch (root->height.unit) { + case SP_CSS_UNIT_PC: + case SP_CSS_UNIT_PT: + case SP_CSS_UNIT_MM: + case SP_CSS_UNIT_CM: + case SP_CSS_UNIT_IN: + need_fix_viewbox = true; + break; + case SP_CSS_UNIT_NONE: + case SP_CSS_UNIT_PX: + need_fix_units = true; + break; + case SP_CSS_UNIT_EM: + case SP_CSS_UNIT_EX: + case SP_CSS_UNIT_PERCENT: + // OK + break; + default: + std::cerr << "sp_file_open: Unhandled height unit!" << std::endl; + } + + // std::cout << "Absolute SVG units in root? " << (need_fix_viewbox?"true":"false") << std::endl; + // std::cout << "User units in root? " << (need_fix_units ?"true":"false") << std::endl; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (!root->viewBox_set && need_fix_viewbox) { + + Glib::ustring msg = _( + "Old Inkscape files use 1in == 90px. CSS requires 1in == 96px.\n" + "Drawing elements may be too small. This can be corrected by\n" + "either setting the SVG 'viewBox' to compensate or by scaling\n" + "all the elements in the drawing."); + Gtk::Dialog scaleDialog( _("Old Inkscape file detected (90 DPI)"), false); + + Gtk::Label info; + info.set_markup(msg.c_str()); + info.show(); + scaleDialog.get_content_area()->pack_start(info, false, false, 20); + + Gtk::CheckButton backupButton( _("Create backup file (in same directory).") ); + bool backup = prefs->getBool("/options/dpifixbackup", true); + backupButton.set_active( backup ); + backupButton.show(); + scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20); + + scaleDialog.add_button(_("Set 'viewBox'"), 1); + scaleDialog.add_button(_("Scale elements"), 2); + scaleDialog.add_button(_("Ignore"), 3); + scaleDialog.add_button("Scale test - group", 4); + scaleDialog.add_button("Scale test - children", 5); + scaleDialog.add_button("Scale test - all", 6); + + gint response = scaleDialog.run(); + backup = backupButton.get_active(); + prefs->setBool("/options/dpifixbackup", backup); + + if ( backup && response != 3) { + sp_file_save_backup( uri ); + } + + if (response == 1) { + + doc->setViewBox(Geom::Rect::from_xywh( + 0, 0, + doc->getWidth().value("px") * ratio, + doc->getHeight().value("px") * ratio)); + + } else if (response == 2 ) { + + std::list<Inkscape::Extension::Effect *> effects; + Inkscape::Extension::db.get_effect_list(effects); + std::list<Inkscape::Extension::Effect *>::iterator it = effects.begin(); + bool did = false; + while (it != effects.end()) { + if (strcmp((*it)->get_id(), "org.inkscape.dpi90to96") == 0) { + Inkscape::UI::View::View *view = desktop; + (*it)->effect(view); + did = true; + break; + } + ++it; + } + if (!did) { + std::cerr << "sp_file_open: Failed to find dpi90to96 extension." << std::endl; + } + did_scaling = true; + + } else if (response == 4) { + + // Save preferences + bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); + bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); + + prefs->setBool("/options/kbselection/onlysensitive", false); + prefs->setBool("/options/kbselection/onlyvisible", false); + + Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::SelectionHelper::selectAllInAll( desktop ); + selection->group(); + SPItem * group = selection->singleItem(); + if (group) { + group->setAttribute("transform","scale(1.06666667,1.06666667)"); + } else { + std::cerr << "sp_file_open: Failed to get group!" << std::endl; + } + selection->clear(); + selection->add( group ); + selection->ungroup(); + selection->clear(); + + prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); + prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); + + did_scaling = true; + + } else if (response == 5) { + + // Save preferences + bool transform_stroke = prefs->getBool("/options/transform/stroke", true); + bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true); + bool transform_pattern = prefs->getBool("/options/transform/pattern", true); + bool transform_gradient = prefs->getBool("/options/transform/gradient", true); + + prefs->setBool("/options/transform/stroke", true); + prefs->setBool("/options/transform/rectcorners", true); + prefs->setBool("/options/transform/pattern", true); + prefs->setBool("/options/transform/gradient", true); + + Inkscape::UI::ShapeEditor::blockSetItem(true); + doc->getRoot()->scaleChildItemsRec(Geom::Scale(1/ratio),Geom::Point(0, 0), false); + Inkscape::UI::ShapeEditor::blockSetItem(false); + + // Restore preferences + prefs->setBool("/options/transform/stroke", transform_stroke); + prefs->setBool("/options/transform/rectcorners", transform_rectcorners); + prefs->setBool("/options/transform/pattern", transform_pattern); + prefs->setBool("/options/transform/gradient", transform_gradient); + + } else if (response == 6) { + + // Save preferences + bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); + bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); + + prefs->setBool("/options/kbselection/onlysensitive", false); + prefs->setBool("/options/kbselection/onlyvisible", false); + + Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::SelectionHelper::selectAllInAll( desktop ); + + double height = root->height.computed; + selection->setScaleRelative( Geom::Point(0,height), Geom::Scale(96.0/90.0,96.0/90.0) ); + selection->clear(); + + prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); + prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); + + did_scaling = true; + + } + + need_fix_box3d = false; // setScaleRelative() handles box3d + need_fix_guides = true; // Always fix guides + } + + else if (need_fix_units) { + Glib::ustring msg = _( + "Old Inkscape files use 1in == 90px. CSS requires 1in == 96px.\n" + "Drawings meant to match a physical size (e.g. Letter or A4)\n" + "will be too small. Scaling the drawing can correct for this.\n" + "Internal scaling can be handled either by setting the SVG 'viewBox'\n" + "attribute to compensate or by scaling all objects in the drawing."); + Gtk::Dialog scaleDialog( _("Old Inkscape file detected (90 DPI)"), false); + + Gtk::Label info; + info.set_markup(msg.c_str()); + info.show(); + scaleDialog.get_content_area()->pack_start(info, false, false, 20); + + Gtk::CheckButton backupButton( _("Create backup file (in same directory).") ); + bool backup = prefs->getBool("/options/dpifixbackup", true); + backupButton.set_active( backup ); + backupButton.show(); + scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20); + + scaleDialog.add_button(_("Set 'viewBox'"), 1); + scaleDialog.add_button(_("Scale elements"), 2); + scaleDialog.add_button(_("Ignore"), 3); + scaleDialog.add_button("Scale test - group", 4); + scaleDialog.add_button("Scale test - children", 5); + scaleDialog.add_button("Scale test - all", 6); + + gint response = scaleDialog.run(); + backup = backupButton.get_active(); + prefs->setBool("/options/dpifixbackup", backup); + + if ( backup && response != 3) { + sp_file_save_backup( uri ); + } + + if (response == 1) { + + if (!root->viewBox_set) { + doc->setViewBox(Geom::Rect::from_xywh( + 0, 0, + doc->getWidth().value("px"), + doc->getHeight().value("px"))); + } + Inkscape::Util::Quantity width = + Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); + Inkscape::Util::Quantity height = + Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); + doc->setWidthAndHeight( width, height, false ); + + need_fix_guides = true; // Only fix guides if drawing scaled + need_fix_box3d = true; + + } else if (response == 2) { + + std::list<Inkscape::Extension::Effect *> effects; + Inkscape::Extension::db.get_effect_list(effects); + std::list<Inkscape::Extension::Effect *>::iterator it = effects.begin(); + bool did = false; + while (it != effects.end()){ + if (strcmp((*it)->get_id(), "org.inkscape.dpi90to96") == 0) { + Inkscape::UI::View::View *view = desktop; + (*it)->effect(view); + did = true; + break; + } + ++it; + } + if (!did) { + std::cerr << "sp_file_open: Failed to find dpi90to96 extension." << std::endl; + } + need_fix_guides = true; // Only fix guides if drawing scaled + did_scaling = true; + + } else if (response == 4) { + + Inkscape::Util::Quantity width = + Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); + Inkscape::Util::Quantity height = + Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); + doc->setWidthAndHeight( width, height, false ); + + if (!root->viewBox_set) { + + // Save preferences + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); + bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); + + prefs->setBool("/options/kbselection/onlysensitive", false); + prefs->setBool("/options/kbselection/onlyvisible", false); + + Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::SelectionHelper::selectAllInAll( desktop ); + selection->group(); + SPItem * group = selection->singleItem(); + if (group) { + group->setAttribute("transform","scale(1.06666667,1.06666667)"); + } else { + std::cerr << "sp_file_open: Failed to get group!" << std::endl; + } + selection->clear(); + selection->add( group ); + selection->ungroup(); + selection->clear(); + + prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); + prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); + + did_scaling = true; + } + + need_fix_box3d = true; + need_fix_guides = true; // Only fix guides if drawing scaled + + } else if (response == 5) { + + Inkscape::Util::Quantity width = + Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); + Inkscape::Util::Quantity height = + Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); + doc->setWidthAndHeight( width, height, false ); + + if (!root->viewBox_set) { + + // Save preferences + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool transform_stroke = prefs->getBool("/options/transform/stroke", true); + bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true); + bool transform_pattern = prefs->getBool("/options/transform/pattern", true); + bool transform_gradient = prefs->getBool("/options/transform/gradient", true); + + prefs->setBool("/options/transform/stroke", true); + prefs->setBool("/options/transform/rectcorners", true); + prefs->setBool("/options/transform/pattern", true); + prefs->setBool("/options/transform/gradient", true); + + Inkscape::UI::ShapeEditor::blockSetItem(true); + doc->getRoot()->scaleChildItemsRec(Geom::Scale(1/ratio),Geom::Point(0, 0), false); + Inkscape::UI::ShapeEditor::blockSetItem(false); + + // Restore preferences + prefs->setBool("/options/transform/stroke", transform_stroke); + prefs->setBool("/options/transform/rectcorners", transform_rectcorners); + prefs->setBool("/options/transform/pattern", transform_pattern); + prefs->setBool("/options/transform/gradient", transform_gradient); + + did_scaling = true; + + } + + } else if (response == 6) { + + double old_height = root->height.computed; + Inkscape::Util::Quantity width = + Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); + Inkscape::Util::Quantity height = + Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); + doc->setWidthAndHeight( width, height, false ); + + if (!root->viewBox_set) { + + // Save preferences + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); + bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); + + prefs->setBool("/options/kbselection/onlysensitive", false); + prefs->setBool("/options/kbselection/onlyvisible", false); + + Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::SelectionHelper::selectAllInAll( desktop ); + double height = root->height.computed; + + // So far we have just enlarged the drawing but due to the + // inverted coordinate system we must scale around the old + // height position. + selection->setScaleRelative( Geom::Point(0,old_height), Geom::Scale(96.0/90.0,96.0/90.0) ); + selection->clear(); + + prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); + prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); + + did_scaling = true; + } + + need_fix_box3d = false; // setScaleRelative() handls box3s + need_fix_guides = true; // Only fix guides if drawing scaled + + } else { + // Ignore + need_fix_grid_mm = true; + } + } + + // Fix guides and grids and perspective + for (SPObject *child = root->firstChild() ; child; child = child->getNext() ) { + SPNamedView *nv = dynamic_cast<SPNamedView *>(child); + if (nv) { + if (need_fix_guides) { + // std::cout << "Fixing guides" << std::endl; + for (SPObject *child2 = nv->firstChild() ; child2; child2 = child2->getNext() ) { + SPGuide *gd = dynamic_cast<SPGuide *>(child2); + if (gd) { + gd->moveto( gd->getPoint() / ratio, true ); + } + } + } + + for(std::vector<Inkscape::CanvasGrid *>::const_iterator it=nv->grids.begin();it!=nv->grids.end();++it ) { + Inkscape::CanvasXYGrid *xy = dynamic_cast<Inkscape::CanvasXYGrid *>(*it); + if (xy) { + // std::cout << "A grid: " << xy->getSVGName() << std::endl; + // std::cout << " Origin: " << xy->origin + // << " Spacing: " << xy->spacing << std::endl; + // std::cout << (xy->isLegacy()?" Legacy":" Not Legacy") << std::endl; + Geom::Scale scale = doc->getDocumentScale(); + if (xy->isLegacy()) { + if (xy->isPixel()) { + if (need_fix_grid_mm) { + xy->Scale( Geom::Scale(1,1) ); // See note below + } else { + scale *= Geom::Scale(ratio,ratio); + xy->Scale( scale.inverse() ); /* *** */ + } + } else { + if (need_fix_grid_mm) { + xy->Scale( Geom::Scale(ratio,ratio) ); + } else { + xy->Scale( scale.inverse() ); /* *** */ + } + } + } else { + if (need_fix_guides) { + if(did_scaling){ + xy->Scale( Geom::Scale(ratio,ratio).inverse() ); + } else { + // HACK: Scaling the document does not seem to cause + // grids defined in document units to be updated. + // This forces an update. + xy->Scale( Geom::Scale(1,1) ); + } + } + } + } + } + } // If SPNamedView + + SPDefs *defs = dynamic_cast<SPDefs *>(child); + if (defs && need_fix_box3d) { + for (SPObject *child = defs->firstChild() ; child; child = child->getNext() ) { + Persp3D* persp3d = dynamic_cast<Persp3D *>(child); + if (persp3d) { + std::vector<Glib::ustring> tokens; + + const gchar* vp_x = persp3d->getAttribute("inkscape:vp_x"); + const gchar* vp_y = persp3d->getAttribute("inkscape:vp_y"); + const gchar* vp_z = persp3d->getAttribute("inkscape:vp_z"); + const gchar* vp_o = persp3d->getAttribute("inkscape:persp3d-origin"); + // std::cout << "Found Persp3d: " + // << " vp_x: " << vp_x + // << " vp_y: " << vp_y + // << " vp_z: " << vp_z << std::endl; + Proj::Pt2 pt_x (vp_x); + Proj::Pt2 pt_y (vp_y); + Proj::Pt2 pt_z (vp_z); + Proj::Pt2 pt_o (vp_o); + pt_x = pt_x * (1.0/ratio); + pt_y = pt_y * (1.0/ratio); + pt_z = pt_z * (1.0/ratio); + pt_o = pt_o * (1.0/ratio); + persp3d->setAttribute("inkscape:vp_x",pt_x.coord_string()); + persp3d->setAttribute("inkscape:vp_y",pt_y.coord_string()); + persp3d->setAttribute("inkscape:vp_z",pt_z.coord_string()); + persp3d->setAttribute("inkscape:persp3d-origin",pt_o.coord_string()); + } + } + } + } // Look for SPNamedView and SPDefs loop + + // desktop->getDocument()->ensureUpToDate(); // Does not update box3d! + DocumentUndo::done(desktop->getDocument(), SP_VERB_NONE, _("Update Document")); + + } // If old Inkscape version + } // If use_gui + // resize the window to match the document properties sp_namedview_window_from_document(desktop); sp_namedview_update_layers_from_document(desktop); @@ -1113,8 +1633,12 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) for (Inkscape::XML::Node *obj = clipboard->firstChild() ; obj ; obj = obj->next()) { if(target_document->getObjectById(obj->attribute("id"))) continue; Inkscape::XML::Node *obj_copy = obj->duplicate(target_document->getReprDoc()); - target_parent->appendChild(obj_copy); + SPObject * pasted = desktop->currentLayer()->appendChildRepr(obj_copy); Inkscape::GC::release(obj_copy); + SPLPEItem * pasted_lpe_item = dynamic_cast<SPLPEItem *>(pasted); + if (pasted_lpe_item){ + pasted_lpe_item->forkPathEffectsIfNecessary(1); + } pasted_objects_not.push_back(obj_copy); } Inkscape::Selection *selection = desktop->getSelection(); @@ -1159,6 +1683,7 @@ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) selection->moveRelative(offset); } + target_document->emitReconstructionFinish(); } @@ -1258,6 +1783,7 @@ file_import(SPDocument *in_doc, const Glib::ustring &uri, } } } + in_doc->emitReconstructionFinish(); if (newgroup) new_obj = place_to_insert->appendChildRepr(newgroup); // release some stuff diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index c133bfa7c..05f594f86 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -1583,7 +1583,7 @@ SPGradient *sp_gradient_vector_for_object( SPDocument *const doc, SPDesktop *con SPIPaint const &paint = ( (fill_or_stroke == Inkscape::FOR_FILL) ? style.fill : style.stroke ); if (paint.isPaintserver()) { SPObject *server = (fill_or_stroke == Inkscape::FOR_FILL) ? o->style->getFillPaintServer() : o->style->getStrokePaintServer(); - if ( SP_IS_GRADIENT(server) ) { + if ( SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server) ) { return SP_GRADIENT(server)->getVector(true); } else { color = sp_desktop_get_color(desktop, (fill_or_stroke == Inkscape::FOR_FILL)); diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index fc75054a0..a928cac0d 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -59,12 +59,14 @@ using Inkscape::allPaintTargets; using Inkscape::CTLINE_PRIMARY; using Inkscape::CTLINE_SECONDARY; -#define GR_KNOT_COLOR_NORMAL 0xffffff00 -#define GR_KNOT_COLOR_MOUSEOVER 0xff000000 -#define GR_KNOT_COLOR_SELECTED 0x0000ff00 +guint32 const GR_KNOT_COLOR_NORMAL = 0xffffff00; +guint32 const GR_KNOT_COLOR_MOUSEOVER = 0xff000000; +guint32 const GR_KNOT_COLOR_SELECTED = 0x0000ff00; +guint32 const GR_KNOT_COLOR_HIGHLIGHT = 0xffffff00; +guint32 const GR_KNOT_COLOR_MESHCORNER = 0xbfbfbf00; -#define GR_LINE_COLOR_FILL 0x0000ff7f -#define GR_LINE_COLOR_STROKE 0x9999007f +guint32 const GR_LINE_COLOR_FILL = 0x0000ff7f; +guint32 const GR_LINE_COLOR_STROKE = 0x9999007f; // screen pixels between knots when they snap: #define SNAP_DIST 5 @@ -116,6 +118,7 @@ static void gr_drag_sel_modified(Inkscape::Selection */*selection*/, guint /*fla { GrDrag *drag = (GrDrag *) data; if (drag->local_change) { + drag->refreshDraggers (); // Needed to move mesh handles and toggle visibility drag->local_change = false; } else { drag->updateDraggers (); @@ -731,14 +734,24 @@ SPObject *GrDraggable::getServer() static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data) { GrDragger *dragger = (GrDragger *) data; - GrDrag *drag = dragger->parent; - Geom::Point p = ppointer; + // Dragger must have at least one draggable + GrDraggable *draggable = (GrDraggable *) dragger->draggables[0]; + if (!draggable) return; + // Find mesh corner that corresponds to dragger (only checks first draggable) and highlight it. + GrDragger *dragger_corner = dragger->getMgCorner(); + if (dragger_corner) { + dragger_corner->highlightCorner(true); + } + + // Set-up snapping SPDesktop *desktop = dragger->parent->desktop; SnapManager &m = desktop->namedview->snap_manager; double snap_dist = m.snapprefs.getObjectTolerance() / dragger->parent->desktop->current_zoom(); + Geom::Point p = ppointer; + if (state & GDK_SHIFT_MASK) { // with Shift; unsnap if we carry more than one draggable if (dragger->draggables.size()>1) { @@ -890,6 +903,7 @@ static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, gui knot->moveto(p); } + GrDrag *drag = dragger->parent; // There is just one GrDrag. drag->keep_selection = (drag->selected.find(dragger)!=drag->selected.end()); bool scale_radial = (state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK); @@ -1056,6 +1070,19 @@ static void gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const & static void gr_knot_grabbed_handler(SPKnot */*knot*/, unsigned int /*state*/, gpointer data) { GrDragger *dragger = (GrDragger *) data; + GrDrag *drag = dragger->parent; + + // Turn off all mesh handle highlighting + for(std::vector<GrDragger *>::const_iterator it = drag->draggers.begin(); it != drag->draggers.end(); ++it) { //for all selected draggers + GrDragger *d = *it; + d->highlightCorner(false); + } + + // Highlight only mesh corner corresponding to grabbed corner or handle + GrDragger *dragger_corner = dragger->getMgCorner(); + if (dragger_corner) { + dragger_corner->highlightCorner(true); + } dragger->parent->desktop->canvas->forceFullRedrawAfterInterruptions(5); } @@ -1207,6 +1234,30 @@ void GrDragger::fireDraggables(bool write_repr, bool scale_radial, bool merging_ } } +void GrDragger::updateControlSizesOverload(SPKnot * knot) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int sizes[] = {4, 6, 8, 10, 12, 14, 16}; + std::vector<int> sizeTable = std::vector<int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + int size = prefs->getIntLimited("/options/grabsize/value", 3, 1, 7); + int knot_size = sizeTable[size - 1]; + if(knot->shape == SP_KNOT_SHAPE_TRIANGLE){ + knot_size *= 2.2; + knot_size = floor(knot_size); + if ( knot_size % 2 == 0 ){ + knot_size += 1; + } + } + knot->setSize(knot_size); +} + +void GrDragger::updateControlSizes() +{ + updateControlSizesOverload(this->knot); + this->knot->updateCtrl(); + this->updateKnotShape(); +} + /** * Checks if the dragger has a draggable with this point_type. */ @@ -1377,10 +1428,11 @@ GrDragger::moveMeshHandles ( Geom::Point pc_old, MeshNodeOperation op ) pcg_old *= (gradient->gradientTransform).inverse(); mg->array.update_handles( point_i, selected_corners[ gradient ], pcg_old, op ); + mg->array.write( mg ); // Move on-screen knots for( guint i = 0; i < mg->array.handles.size(); ++i ) { - GrDragger *handle = drag->getDraggerFor( item, POINT_MG_HANDLE, i, fill_or_stroke ); + GrDragger *handle = drag->getDraggerFor( item, POINT_MG_HANDLE, i, fill_or_stroke ); SPKnot *knot = handle->knot; Geom::Point pk = getGradientCoords( item, POINT_MG_HANDLE, i, fill_or_stroke ); knot->moveto(pk); @@ -1424,6 +1476,15 @@ void GrDragger::updateTip() (draggable->fill_or_stroke == Inkscape::FOR_STROKE) ? _(" (stroke)") : ""); break; + case POINT_MG_CORNER: + case POINT_MG_HANDLE: + case POINT_MG_TENSOR: + this->knot->tip = g_strdup_printf (_("%s for: %s%s"), + _(gr_knot_descr[draggable->point_type]), + item_desc, + (draggable->fill_or_stroke == Inkscape::FOR_STROKE) ? _(" (stroke)") : ""); + break; + default: this->knot->tip = g_strdup_printf (_("%s for: %s%s; drag with <b>Ctrl</b> to snap angle, with <b>Ctrl+Alt</b> to preserve angle, with <b>Ctrl+Shift</b> to scale around center"), _(gr_knot_descr[draggable->point_type]), @@ -1451,7 +1512,16 @@ void GrDragger::updateKnotShape() if (draggables.empty()) return; GrDraggable *last = draggables.back(); + g_object_set (G_OBJECT (this->knot->item), "shape", gr_knot_shapes[last->point_type], NULL); + + // For highlighting mesh handles corresponding to selected corner + if (this->knot->shape == SP_KNOT_SHAPE_TRIANGLE) { + this->knot->setFill(GR_KNOT_COLOR_HIGHLIGHT, GR_KNOT_COLOR_MOUSEOVER, GR_KNOT_COLOR_MOUSEOVER); + if (gr_knot_shapes[last->point_type] == SP_KNOT_SHAPE_CIRCLE) { + g_object_set (G_OBJECT (this->knot->item), "shape", SP_KNOT_SHAPE_TRIANGLE, NULL); + } + } } /** @@ -1590,8 +1660,13 @@ GrDragger::GrDragger(GrDrag *parent, Geom::Point p, GrDraggable *draggable) // create the knot this->knot = new SPKnot(parent->desktop, NULL); this->knot->setMode(SP_KNOT_MODE_XOR); - this->knot->setFill(GR_KNOT_COLOR_NORMAL, GR_KNOT_COLOR_MOUSEOVER, GR_KNOT_COLOR_MOUSEOVER); + guint32 fill_color = GR_KNOT_COLOR_NORMAL; + if (draggable && draggable->point_type == POINT_MG_CORNER) { + fill_color = GR_KNOT_COLOR_MESHCORNER; + } + this->knot->setFill(fill_color, GR_KNOT_COLOR_MOUSEOVER, GR_KNOT_COLOR_MOUSEOVER); this->knot->setStroke(0x0000007f, 0x0000007f, 0x0000007f); + this->updateControlSizesOverload(this->knot); this->knot->updateCtrl(); // move knot to the given point @@ -1610,6 +1685,7 @@ GrDragger::GrDragger(GrDrag *parent, Geom::Point p, GrDraggable *draggable) this->_moved_connection = this->knot->moved_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_moved_handler), this)); } + this->sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged(sigc::mem_fun(*this, &GrDragger::updateControlSizes)); this->_clicked_connection = this->knot->click_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_clicked_handler), this)); this->_doubleclicked_connection = this->knot->doubleclicked_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_doubleclicked_handler), this)); this->_grabbed_connection = this->knot->grabbed_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_grabbed_handler), this)); @@ -1632,6 +1708,7 @@ GrDragger::~GrDragger() //this->parent->setDeselected(this); // disconnect signals + this->sizeUpdatedConn.disconnect(); this->_moved_connection.disconnect(); this->_clicked_connection.disconnect(); this->_doubleclicked_connection.disconnect(); @@ -1651,6 +1728,21 @@ GrDragger::~GrDragger() /** * Select the dragger which has the given draggable. */ +GrDragger *GrDrag::getDraggerFor(GrDraggable *d) { + for (std::vector<GrDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i ) { + GrDragger *dragger = *i; + for (std::vector<GrDraggable *>::const_iterator j = dragger->draggables.begin(); j != dragger->draggables.end(); ++j ) { + if (d == *j) { + return dragger; + } + } + } + return NULL; +} + +/** + * Select the dragger which has the given draggable. + */ GrDragger *GrDrag::getDraggerFor(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke) { for (std::vector<GrDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i ) { @@ -1677,6 +1769,152 @@ void GrDragger::moveOtherToDraggable(SPItem *item, GrPointType point_type, gint } } +/** + * Find mesh corner corresponding to given dragger. + */ +GrDragger* GrDragger::getMgCorner(){ + GrDraggable *draggable = (GrDraggable *) this->draggables[0]; + if (draggable) { + + // If corner, we already found it! + if (draggable->point_type == POINT_MG_CORNER) { + return this; + } + + // The mapping between handles and corners is complex... so find corner by bruit force. + SPGradient *gradient = getGradient(draggable->item, draggable->fill_or_stroke); + SPMeshGradient *mg = dynamic_cast<SPMeshGradient *>(gradient); + if (mg) { + std::vector< std::vector< SPMeshNode* > > nodes = mg->array.nodes; + for (guint i = 0; i < nodes.size(); ++i) { + for (guint j = 0; j < nodes[i].size(); ++j) { + if (nodes[i][j]->set && nodes[i][j]->node_type == MG_NODE_TYPE_HANDLE) { + if (draggable->point_i == (gint)nodes[i][j]->draggable) { + + if (nodes.size() > i+1 && nodes[i+1].size() > j && nodes[i+1][j]->node_type == MG_NODE_TYPE_CORNER) { + return this->parent->getDraggerFor(draggable->item, POINT_MG_CORNER, nodes[i+1][j]->draggable, draggable->fill_or_stroke); + } + + if (j != 0 && nodes.size() > i && nodes[i].size() > j-1 && nodes[i][j-1]->node_type == MG_NODE_TYPE_CORNER) { + return this->parent->getDraggerFor(draggable->item, POINT_MG_CORNER, nodes[i][j-1]->draggable, draggable->fill_or_stroke); + } + + if (i != 0 && nodes.size() > i-1 && nodes[i-1].size() > j && nodes[i-1][j]->node_type == MG_NODE_TYPE_CORNER) { + return this->parent->getDraggerFor(draggable->item, POINT_MG_CORNER, nodes[i-1][j]->draggable, draggable->fill_or_stroke); + } + + if (nodes.size() > i && nodes[i].size() > j+1 && nodes[i][j+1]->node_type == MG_NODE_TYPE_CORNER) { + return this->parent->getDraggerFor(draggable->item, POINT_MG_CORNER, nodes[i][j+1]->draggable, draggable->fill_or_stroke); + } + } + } + } + } + } + } + return NULL; +} + +/** + * Highlight mesh node + */ +void GrDragger::highlightNode(SPMeshNode* node, bool highlight, Geom::Point corner_pos) +{ + GrPointType type = POINT_MG_TENSOR; + if (node->node_type == MG_NODE_TYPE_HANDLE) { + type = POINT_MG_HANDLE; + } + + GrDraggable *draggable = (GrDraggable *) this->draggables[0]; + GrDragger *d = this->parent->getDraggerFor(draggable->item, type, node->draggable, draggable->fill_or_stroke); + if (d && node->draggable < G_MAXUINT) { + Geom::Point end = d->knot->pos; + double angl = Geom::Ray(corner_pos, end).angle(); + + if (highlight && knot->fill[SP_KNOT_VISIBLE] == GR_KNOT_COLOR_HIGHLIGHT && abs(angl - knot->angle) > Geom::rad_from_deg(10.0)){ + return; + } + + SPKnot *knot = d->knot; + if (highlight) { + knot->setFill(GR_KNOT_COLOR_HIGHLIGHT, GR_KNOT_COLOR_MOUSEOVER, GR_KNOT_COLOR_MOUSEOVER); + } else { + knot->setFill(GR_KNOT_COLOR_NORMAL, GR_KNOT_COLOR_MOUSEOVER, GR_KNOT_COLOR_MOUSEOVER); + } + + if (type == POINT_MG_HANDLE) { + if (highlight) { + knot->setShape(SP_KNOT_SHAPE_TRIANGLE); + } else { + knot->setShape(SP_KNOT_SHAPE_CIRCLE); + } + } else { + //Code for tensors + return; + } + + this->updateControlSizesOverload(knot); + knot->setAngle(angl); + knot->updateCtrl(); + d->updateKnotShape(); + } +} + +/** + * Highlight handles for mesh corner corresponding to this dragger. + */ +void GrDragger::highlightCorner(bool highlight) +{ + // Must be a mesh gradient + GrDraggable *draggable = (GrDraggable *) this->draggables[0]; + if (draggable && draggable->point_type == POINT_MG_CORNER) { + SPGradient *gradient = getGradient(draggable->item, draggable->fill_or_stroke); + if (SP_IS_MESHGRADIENT( gradient )) { + Geom::Point corner_point = this->point; + gint corner = draggable->point_i; + SPMeshGradient *mg = SP_MESHGRADIENT( gradient ); + SPMeshNodeArray mg_arr = mg->array; + std::vector< std::vector< SPMeshNode* > > nodes = mg_arr.nodes; + // Find number of patch rows and columns + guint mrow = mg_arr.patch_rows(); + guint mcol = mg_arr.patch_columns(); + // Number of corners in a row of patches. + guint ncorners = mcol + 1; + // Find corner row/column + guint crow = corner / ncorners; + guint ccol = corner % ncorners; + // Find node row/column + guint nrow = crow * 3; + guint ncol = ccol * 3; + + bool patch[4]; + patch[0] = patch[1] = patch[2] = patch[3] = false; + if (ccol > 0 && crow > 0 ) patch[0] = true; + if (ccol < mcol && crow > 0 ) patch[1] = true; + if (ccol < mcol && crow < mrow ) patch[2] = true; + if (ccol > 0 && crow < mrow ) patch[3] = true; + if (patch[0] || patch[1]) { + highlightNode(nodes[nrow-1][ncol ], highlight, corner_point); + } + if (patch[1] || patch[2]) { + highlightNode(nodes[nrow ][ncol+1], highlight, corner_point); + } + if (patch[2] || patch[3]) { + highlightNode(nodes[nrow+1][ncol ], highlight, corner_point); + } + if (patch[3] || patch[0]) { + highlightNode(nodes[nrow ][ncol-1], highlight, corner_point); + } + // Highlight tensors + /* + if( patch[0] ) highlightNode(nodes[nrow-1][ncol-1], highlight, corner_point, point_i); + if( patch[1] ) highlightNode(nodes[nrow-1][ncol+1], highlight, corner_point, point_i); + if( patch[2] ) highlightNode(nodes[nrow+1][ncol+1], highlight, corner_point, point_i); + if( patch[3] ) highlightNode(nodes[nrow+1][ncol-1], highlight, corner_point, point_i); + */ + } + } +} /** * Draw this dragger as selected. @@ -1685,13 +1923,7 @@ void GrDragger::select() { this->knot->fill [SP_KNOT_STATE_NORMAL] = GR_KNOT_COLOR_SELECTED; g_object_set (G_OBJECT (this->knot->item), "fill_color", GR_KNOT_COLOR_SELECTED, NULL); - //if( isA(POINT_MG_CORNER) ) { - // for (GSList * drgble = this->draggables; drgble != NULL; drgble = drgble->next) { - // GrDraggable *draggable = (GrDraggable*) drgble->data; - // //if( draggable != NULL ) std::cout << " draggable" << std::endl; - // // MESH FIXME: TURN ON CORRESPONDING SIDE/TENSOR NODE VISIBILITY - // } - //} + highlightCorner(true); } /** @@ -1699,9 +1931,10 @@ void GrDragger::select() */ void GrDragger::deselect() { - this->knot->fill [SP_KNOT_STATE_NORMAL] = GR_KNOT_COLOR_NORMAL; - g_object_set (G_OBJECT (this->knot->item), "fill_color", GR_KNOT_COLOR_NORMAL, NULL); - // MESH FIXME: TURN OFF CORRESPONDING SIDE/TENSOR NODE VISIBILITY + guint32 fill_color = isA(POINT_MG_CORNER) ? GR_KNOT_COLOR_MESHCORNER : GR_KNOT_COLOR_NORMAL; + this->knot->fill [SP_KNOT_STATE_NORMAL] = fill_color; + g_object_set (G_OBJECT (this->knot->item), "fill_color", fill_color, NULL); + highlightCorner(false); } bool @@ -1860,6 +2093,7 @@ void GrDrag::addLine(SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::Pai sp_canvas_item_move_to_z(line, 0); line->item = item; + line->is_fill = (fill_or_stroke == Inkscape::FOR_FILL); sp_canvas_item_show(line); this->lines.push_back(line); } @@ -1869,13 +2103,35 @@ void GrDrag::addLine(SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::Pai /** * Create a curve from p0 to p3 and add it to the lines list. Used for mesh sides. */ -void GrDrag::addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, Inkscape::PaintTarget fill_or_stroke) +void GrDrag::addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, + int corner0, int corner1, int handle0, int handle1, Inkscape::PaintTarget fill_or_stroke) + { + // Highlight curve if one of its draggers has a mouse over it. + bool highlight = false; + GrDragger* dragger0 = getDraggerFor(item, POINT_MG_CORNER, corner0, fill_or_stroke); + GrDragger* dragger1 = getDraggerFor(item, POINT_MG_CORNER, corner1, fill_or_stroke); + GrDragger* dragger2 = getDraggerFor(item, POINT_MG_HANDLE, handle0, fill_or_stroke); + GrDragger* dragger3 = getDraggerFor(item, POINT_MG_HANDLE, handle1, fill_or_stroke); + if (dragger0->knot && (dragger0->knot->flags & SP_KNOT_MOUSEOVER) || + dragger1->knot && (dragger1->knot->flags & SP_KNOT_MOUSEOVER) || + dragger2->knot && (dragger2->knot->flags & SP_KNOT_MOUSEOVER) || + dragger3->knot && (dragger3->knot->flags & SP_KNOT_MOUSEOVER) ) { + highlight = true; + } + + // CtrlLineType only sets color CtrlLineType type = (fill_or_stroke == Inkscape::FOR_FILL) ? CTLINE_PRIMARY : CTLINE_SECONDARY; + if (highlight) { + type = (fill_or_stroke == Inkscape::FOR_FILL) ? CTLINE_SECONDARY : CTLINE_PRIMARY; + } SPCtrlCurve *line = ControlManager::getManager().createControlCurve(this->desktop->getControls(), p0, p1, p2, p3, type); + line->corner0 = corner0; + line->corner1 = corner1; sp_canvas_item_move_to_z(line, 0); line->item = item; + line->is_fill = (fill_or_stroke == Inkscape::FOR_FILL); sp_canvas_item_show (line); this->lines.push_back(line); } @@ -1885,7 +2141,7 @@ void GrDrag::addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point * If there already exists a dragger within MERGE_DIST of p, add the draggable to it; otherwise create * new dragger and add it to draggers list. */ -void GrDrag::addDragger(GrDraggable *draggable) +GrDragger* GrDrag::addDragger(GrDraggable *draggable) { Geom::Point p = getGradientCoords(draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke); @@ -1895,13 +2151,14 @@ void GrDrag::addDragger(GrDraggable *draggable) // distance is small, merge this draggable into dragger, no need to create new dragger dragger->addDraggable (draggable); dragger->updateKnotShape(); - return; + return dragger; } } GrDragger *new_dragger = new GrDragger(this, p, draggable); // fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end. this->draggers.push_back(new_dragger); + return new_dragger; } /** @@ -1953,15 +2210,10 @@ void GrDrag::addDraggersMesh(SPMeshGradient *mg, SPItem *item, Inkscape::PaintTa // Show/hide mesh on fill/stroke. This doesn't work at the moment... and prevents node color updating. - //Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool edit_fill = true; //abs(prefs->getBool("/tools/mesh/edit_fill", true)); - bool edit_stroke = true; //abs(prefs->getBool("/tools/mesh/edit_stroke", true)); - bool show_handles = true; //abs(prefs->getBool("/tools/mesh/show_handles", true)); - - if( (fill_or_stroke == Inkscape::FOR_FILL && !edit_fill) || - (fill_or_stroke == Inkscape::FOR_STROKE && !edit_stroke) ) { - return; - } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool show_handles = abs(prefs->getBool("/tools/mesh/show_handles", true)); + bool edit_fill = abs(prefs->getBool("/tools/mesh/edit_fill", true)); + bool edit_stroke = abs(prefs->getBool("/tools/mesh/edit_stroke", true)); // Make sure we have at least one patch defined. if( mg->array.patch_rows() == 0 || mg->array.patch_columns() == 0 ) { @@ -1977,58 +2229,141 @@ void GrDrag::addDraggersMesh(SPMeshGradient *mg, SPItem *item, Inkscape::PaintTa mg->array.handles.clear(); mg->array.tensors.clear(); + if( (fill_or_stroke == Inkscape::FOR_FILL && !edit_fill) || + (fill_or_stroke == Inkscape::FOR_STROKE && !edit_stroke) ) { + return; + } for( guint i = 0; i < nodes.size(); ++i ) { for( guint j = 0; j < nodes[i].size(); ++j ) { // std::cout << " Draggers: " << i << " " << j << " " << nodes[i][j]->node_type << std::endl; + switch ( nodes[i][j]->node_type ) { - if( nodes[i][j]->set ) { - switch ( nodes[i][j]->node_type ) { + case MG_NODE_TYPE_CORNER: + { + mg->array.corners.push_back( nodes[i][j] ); + GrDraggable *corner = new GrDraggable (item, POINT_MG_CORNER, icorner, fill_or_stroke); + addDragger ( corner ); + nodes[i][j]->draggable = icorner; + ++icorner; + break; + } - case MG_NODE_TYPE_CORNER: - { - mg->array.corners.push_back( nodes[i][j] ); - GrDraggable *corner = new GrDraggable (item, POINT_MG_CORNER, icorner, fill_or_stroke); - addDragger ( corner ); - nodes[i][j]->draggable = icorner; - ++icorner; - break; + case MG_NODE_TYPE_HANDLE: + { + mg->array.handles.push_back( nodes[i][j] ); + GrDraggable *handle = new GrDraggable (item, POINT_MG_HANDLE, ihandle, fill_or_stroke); + GrDragger* dragger = addDragger ( handle ); + + if( !show_handles || !nodes[i][j]->set ) { + dragger->knot->hide(); } + nodes[i][j]->draggable = ihandle; + ++ihandle; + break; + } - case MG_NODE_TYPE_HANDLE: - { - if( show_handles ) { - mg->array.handles.push_back( nodes[i][j] ); - GrDraggable *handle = new GrDraggable (item, POINT_MG_HANDLE, ihandle, fill_or_stroke); - addDragger ( handle ); - nodes[i][j]->draggable = ihandle; - ++ihandle; - break; - } + case MG_NODE_TYPE_TENSOR: + { + mg->array.tensors.push_back( nodes[i][j] ); + GrDraggable *tensor = new GrDraggable (item, POINT_MG_TENSOR, itensor, fill_or_stroke); + GrDragger* dragger = addDragger ( tensor ); + if( !show_handles || !nodes[i][j]->set ) { + dragger->knot->hide(); } + nodes[i][j]->draggable = itensor; + ++itensor; + break; + } - case MG_NODE_TYPE_TENSOR: - { - if( show_handles ) { - mg->array.tensors.push_back( nodes[i][j] ); - GrDraggable *tensor = new GrDraggable (item, POINT_MG_TENSOR, itensor, fill_or_stroke); - addDragger ( tensor ); - nodes[i][j]->draggable = itensor; - ++itensor; - break; + default: + std::cerr << "Bad Mesh draggable type" << std::endl; + break; + } + } + } + + mg->array.draggers_valid = true; +} + +/** + * Refresh draggers, moving and toggling visibility as necessary. + * Does not regenerate draggers (as does updateDraggersMesh()). + */ +void GrDrag::refreshDraggersMesh(SPMeshGradient *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke) +{ + mg->ensureArray(); + std::vector< std::vector< SPMeshNode* > > nodes = mg->array.nodes; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool show_handles = abs(prefs->getBool("/tools/mesh/show_handles", true)); + + // Make sure we have at least one patch defined. + if( mg->array.patch_rows() == 0 || mg->array.patch_columns() == 0 ) { + + std::cerr << "GrDrag::refreshDraggersMesh: Empty Mesh, No Draggers to refresh!" << std::endl; + return; + } + + guint ihandle = 0; + guint itensor = 0; + + for( guint i = 0; i < nodes.size(); ++i ) { + for( guint j = 0; j < nodes[i].size(); ++j ) { + + // std::cout << " Draggers: " << i << " " << j << " " << nodes[i][j]->node_type << std::endl; + + switch ( nodes[i][j]->node_type ) { + + case MG_NODE_TYPE_CORNER: + // Do nothing, corners are always shown. + break; + + case MG_NODE_TYPE_HANDLE: + { + GrDragger* dragger = getDraggerFor(item, POINT_MG_HANDLE, ihandle, fill_or_stroke); + if (dragger) { + Geom::Point pk = getGradientCoords( item, POINT_MG_HANDLE, ihandle, fill_or_stroke); + dragger->knot->moveto(pk); + if( !show_handles || !nodes[i][j]->set ) { + dragger->knot->hide(); + } else { + dragger->knot->show(); } + } else { + // This can happen if a draggable is not visible. + // std::cerr << "GrDrag::refreshDraggersMesh: No dragger!" << std::endl; } + ++ihandle; + break; + } - default: - std::cerr << "Bad Mesh draggable type" << std::endl; - break; + case MG_NODE_TYPE_TENSOR: + { + GrDragger* dragger = getDraggerFor(item, POINT_MG_TENSOR, itensor, fill_or_stroke); + if (dragger) { + Geom::Point pk = getGradientCoords( item, POINT_MG_TENSOR, itensor, fill_or_stroke); + dragger->knot->moveto(pk); + if( !show_handles || !nodes[i][j]->set ) { + dragger->knot->hide(); + } else { + dragger->knot->show(); + } + } else { + // This can happen if a draggable is not visible. + // std::cerr << "GrDrag::refreshDraggersMesh: No dragger!" << std::endl; + } + ++itensor; + break; } + + default: + std::cerr << "Bad Mesh draggable type" << std::endl; + break; } } } - - mg->array.draggers_valid = true; } /** @@ -2109,16 +2444,59 @@ void GrDrag::updateDraggers() /** + * Refresh draggers, moving and toggling visibility as necessary. + * Does not regenerate draggers (as does updateDraggers()). + * Only applies to mesh gradients. + */ +void GrDrag::refreshDraggers() +{ + + g_return_if_fail(this->selection != NULL); + auto list = this->selection->items(); + for (auto i = list.begin(); i != list.end(); ++i) { + SPItem *item = *i; + SPStyle *style = item->style; + + if (style && (style->fill.isPaintserver())) { + SPPaintServer *server = style->getFillPaintServer(); + if ( server && SP_IS_GRADIENT( server ) ) { + if ( SP_IS_MESHGRADIENT(server) ) { + refreshDraggersMesh( SP_MESHGRADIENT(server), item, Inkscape::FOR_FILL ); + } + } + } + + if (style && (style->stroke.isPaintserver())) { + SPPaintServer *server = style->getStrokePaintServer(); + if ( server && SP_IS_GRADIENT( server ) ) { + if ( SP_IS_MESHGRADIENT(server) ) { + refreshDraggersMesh( SP_MESHGRADIENT(server), item, Inkscape::FOR_STROKE ); + } + } + } + } +} + + +/** * Returns true if at least one of the draggers' knots has the mouse hovering above it. */ bool GrDrag::mouseOver() { + static bool mouse_out = false; + // added knot mouse out for future use for (std::vector<GrDragger *>::const_iterator l = this->draggers.begin(); l != this->draggers.end(); ++l) { GrDragger *d = *l; if (d->knot && (d->knot->flags & SP_KNOT_MOUSEOVER)) { + mouse_out = true; + updateLines(); return true; } } + if(mouse_out == true){ + updateLines(); + mouse_out = false; + } return false; } @@ -2156,8 +2534,12 @@ void GrDrag::updateLines() addLine(item, center, getGradientCoords(item, POINT_RG_R2, 0, Inkscape::FOR_FILL), Inkscape::FOR_FILL); } else if ( SP_IS_MESHGRADIENT(server) ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool edit_fill = abs(prefs->getBool("/tools/mesh/edit_fill", true)); + SPMeshGradient *mg = SP_MESHGRADIENT(server); + if (edit_fill) { guint rows = mg->array.patch_rows(); guint columns = mg->array.patch_columns(); for ( guint i = 0; i < rows; ++i ) { @@ -2167,12 +2549,27 @@ void GrDrag::updateLines() SPMeshPatchI patch( &(mg->array.nodes), i, j ); + // clockwise around patch, used to find corner dragger + int corner0 = i * (columns + 1) + j; + int corner1 = corner0 + 1; + int corner2 = corner1 + columns + 1; + int corner3 = corner2 - 1; + // clockwise around patch, used to find handle dragger + int handle0 = 2*j + i*(2+4*columns); + int handle1 = handle0 + 1; + int handle2 = j + i*(2+4*columns) + 2*columns + 1; + int handle3 = j + i*(2+4*columns) + 3*columns + 2; + int handle4 = handle1 + (2+4*columns); + int handle5 = handle0 + (2+4*columns); + int handle6 = handle3 - 1; + int handle7 = handle2 - 1; + // Top line h = patch.getPointsForSide( 0 ); for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL ); + addCurve (item, h[0], h[1], h[2], h[3], corner0, corner1, handle0, handle1, Inkscape::FOR_FILL ); // Right line if( j == columns - 1 ) { @@ -2180,7 +2577,7 @@ void GrDrag::updateLines() for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL ); + addCurve (item, h[0], h[1], h[2], h[3], corner1, corner2, handle2, handle3, Inkscape::FOR_FILL ); } // Bottom line @@ -2189,7 +2586,7 @@ void GrDrag::updateLines() for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL ); + addCurve (item, h[0], h[1], h[2], h[3], corner2, corner3, handle4, handle5, Inkscape::FOR_FILL ); } // Left line @@ -2197,9 +2594,10 @@ void GrDrag::updateLines() for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL ); + addCurve (item, h[0], h[1], h[2], h[3], corner3, corner0, handle6, handle7, Inkscape::FOR_FILL ); } } + } } } } @@ -2218,6 +2616,11 @@ void GrDrag::updateLines() addLine(item, center, getGradientCoords(item, POINT_RG_R2, 0, Inkscape::FOR_STROKE), Inkscape::FOR_STROKE); } else if ( SP_IS_MESHGRADIENT(server) ) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool edit_stroke = abs(prefs->getBool("/tools/mesh/edit_stroke", true)); + + if (edit_stroke) { + // MESH FIXME: TURN ROUTINE INTO FUNCTION AND CALL FOR BOTH FILL AND STROKE. SPMeshGradient *mg = SP_MESHGRADIENT(server); @@ -2230,12 +2633,27 @@ void GrDrag::updateLines() SPMeshPatchI patch( &(mg->array.nodes), i, j ); + // clockwise around patch, used to find corner dragger + int corner0 = i * (columns + 1) + j; + int corner1 = corner0 + 1; + int corner2 = corner1 + columns + 1; + int corner3 = corner2 - 1; + // clockwise around patch, used to find handle dragger + int handle0 = 2*j + i*(2+4*columns); + int handle1 = handle0 + 1; + int handle2 = j + i*(2+4*columns) + 2*columns + 1; + int handle3 = j + i*(2+4*columns) + 3*columns + 2; + int handle4 = handle1 + (2+4*columns); + int handle5 = handle0 + (2+4*columns); + int handle6 = handle3 - 1; + int handle7 = handle2 - 1; + // Top line h = patch.getPointsForSide( 0 ); for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE ); + addCurve (item, h[0], h[1], h[2], h[3], corner0, corner1, handle0, handle1, Inkscape::FOR_STROKE); // Right line if( j == columns - 1 ) { @@ -2243,7 +2661,7 @@ void GrDrag::updateLines() for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE ); + addCurve (item, h[0], h[1], h[2], h[3], corner1, corner2, handle2, handle3, Inkscape::FOR_STROKE); } // Bottom line @@ -2252,7 +2670,7 @@ void GrDrag::updateLines() for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE ); + addCurve (item, h[0], h[1], h[2], h[3], corner2, corner3, handle4, handle5, Inkscape::FOR_STROKE); } // Left line @@ -2260,9 +2678,10 @@ void GrDrag::updateLines() for( guint p = 0; p < 4; ++p ) { h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine(); } - addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE ); + addCurve (item, h[0], h[1], h[2], h[3], corner3, corner0, handle6, handle7,Inkscape::FOR_STROKE); } - } + } + } } } } diff --git a/src/gradient-drag.h b/src/gradient-drag.h index cf0a35c89..b4a64c78d 100644 --- a/src/gradient-drag.h +++ b/src/gradient-drag.h @@ -107,6 +107,11 @@ struct GrDragger { /* Update handles/tensors when mesh corner moved */ void moveMeshHandles( Geom::Point pc_old, MeshNodeOperation op ); + /* Following are for highlighting mesh handles when corner node is selected. */ + GrDragger *getMgCorner(); + void highlightNode(SPMeshNode* node, bool highlight, Geom::Point corner_pos); + void highlightCorner(bool highlight); + bool mayMerge(GrDragger *other); bool mayMerge(GrDraggable *da2); @@ -116,6 +121,11 @@ struct GrDragger { void fireDraggables(bool write_repr, bool scale_radial = false, bool merging_focus = false); +protected: + void updateControlSizesOverload(SPKnot * knot); + void updateControlSizes(); + sigc::connection sizeUpdatedConn; + private: sigc::connection _moved_connection; sigc::connection _clicked_connection; @@ -168,6 +178,7 @@ public: // FIXME: make more of this private! bool keep_selection; + GrDragger *getDraggerFor(GrDraggable *d); GrDragger *getDraggerFor(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke); void grabKnot(GrDragger *dragger, gint x, gint y, guint32 etime); @@ -185,6 +196,7 @@ public: // FIXME: make more of this private! std::vector<SPCtrlLine *> lines; void updateDraggers(); + void refreshDraggers(); void updateLines(); void updateLevels(); @@ -203,13 +215,15 @@ private: void deselect_all(); void addLine( SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::PaintTarget fill_or_stroke); - void addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, Inkscape::PaintTarget fill_or_stroke); + void addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, + int corner0, int corner1, int handle0, int handle1, Inkscape::PaintTarget fill_or_stroke); - void addDragger(GrDraggable *draggable); + GrDragger *addDragger(GrDraggable *draggable); void addDraggersRadial(SPRadialGradient *rg, SPItem *item, Inkscape::PaintTarget fill_or_stroke); void addDraggersLinear(SPLinearGradient *lg, SPItem *item, Inkscape::PaintTarget fill_or_stroke); void addDraggersMesh( SPMeshGradient *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke); + void refreshDraggersMesh(SPMeshGradient *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke); bool styleSet( const SPCSSAttr *css ); diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp index 10641692d..50627fd0b 100644 --- a/src/helper/geom-pathstroke.cpp +++ b/src/helper/geom-pathstroke.cpp @@ -1,6 +1,7 @@ /* Authors: * Liam P. White * Tavmjong Bah + * Alexander Brock * * Copyright (C) 2014-2015 Authors * @@ -746,33 +747,36 @@ void get_cubic_data(Geom::CubicBezier const& bez, double time, double& len, doub len = l; } -void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, double tol, size_t levels) -{ +double _offset_cubic_stable_sub( + Geom::CubicBezier const& bez, + Geom::CubicBezier& c, + const Geom::Point& start_normal, + const Geom::Point& end_normal, + const Geom::Point& start_new, + const Geom::Point& end_new, + const double start_rad, + const double end_rad, + const double start_len, + const double end_len, + const double width, + const double width_correction) { using Geom::X; using Geom::Y; - Geom::Point start_pos = bez.initialPoint(); - Geom::Point end_pos = bez.finalPoint(); - - Geom::Point start_normal = Geom::rot90(bez.unitTangentAt(0)); - Geom::Point end_normal = -Geom::rot90(Geom::unitTangentAt(Geom::reverse(bez.toSBasis()), 0.)); - - // offset the start and end control points out by the width - Geom::Point start_new = start_pos + start_normal*width; - Geom::Point end_new = end_pos + end_normal*width; - - // -------- - double start_rad, end_rad; - double start_len, end_len; // tangent lengths - get_cubic_data(bez, 0, start_len, start_rad); - get_cubic_data(bez, 1, end_len, end_rad); - double start_off = 1, end_off = 1; // correction of the lengths of the tangent to the offset if (!Geom::are_near(start_rad, 0)) - start_off += width / start_rad; + start_off += (width + width_correction) / start_rad; if (!Geom::are_near(end_rad, 0)) - end_off += width / end_rad; + end_off += (width + width_correction) / end_rad; + + // We don't change the direction of the control points + if (start_off < 0) { + start_off = 0; + } + if (end_off < 0) { + end_off = 0; + } start_off *= start_len; end_off *= end_len; // -------- @@ -783,23 +787,137 @@ void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, dou mid2_new = Geom::Point(end_new[X] - mid2_new[X]/3., end_new[Y] - mid2_new[Y]/3.); // create the estimate curve - Geom::CubicBezier c = Geom::CubicBezier(start_new, mid1_new, mid2_new, end_new); + c = Geom::CubicBezier(start_new, mid1_new, mid2_new, end_new); + + // check the tolerance for our estimate to be a parallel curve + + double worst_residual = 0; + for (size_t ii = 3; ii <= 7; ii+=2) { + const double t = static_cast<double>(ii) / 10; + const Geom::Point req = bez.pointAt(t); + const Geom::Point chk = c.pointAt(c.nearestTime(req)); + const double current_residual = (chk-req).length() - std::abs(width); + if (std::abs(current_residual) > std::abs(worst_residual)) { + worst_residual = current_residual; + } + } + return worst_residual; +} + +void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, double tol, size_t levels) +{ + using Geom::X; + using Geom::Y; + + const Geom::Point start_pos = bez.initialPoint(); + const Geom::Point end_pos = bez.finalPoint(); + + const Geom::Point start_normal = Geom::rot90(bez.unitTangentAt(0)); + const Geom::Point end_normal = -Geom::rot90(Geom::unitTangentAt(Geom::reverse(bez.toSBasis()), 0.)); + + // offset the start and end control points out by the width + const Geom::Point start_new = start_pos + start_normal*width; + const Geom::Point end_new = end_pos + end_normal*width; + + // -------- + double start_rad, end_rad; + double start_len, end_len; // tangent lengths + get_cubic_data(bez, 0, start_len, start_rad); + get_cubic_data(bez, 1, end_len, end_rad); + + + Geom::CubicBezier c; + + double best_width_correction = 0; + double best_residual = _offset_cubic_stable_sub( + bez, c, + start_normal, end_normal, + start_new, end_new, + start_rad, end_rad, + start_len, end_len, + width, best_width_correction); + double stepsize = std::abs(width)/2; + bool seen_success = false; + double stepsize_threshold = 0; + // std::cout << "Residual from " << best_residual << " "; + size_t ii = 0; + for (; ii < 100 && stepsize > stepsize_threshold; ++ii) { + bool success = false; + const double width_correction = best_width_correction - (best_residual > 0 ? 1 : -1) * stepsize; + Geom::CubicBezier current_curve; + const double residual = _offset_cubic_stable_sub( + bez, current_curve, + start_normal, end_normal, + start_new, end_new, + start_rad, end_rad, + start_len, end_len, + width, width_correction); + if (std::abs(residual) < std::abs(best_residual)) { + best_residual = residual; + best_width_correction = width_correction; + c = current_curve; + success = true; + if (std::abs(best_residual) < tol/4) { + break; + } + } + + if (success) { + if (!seen_success) { + seen_success = true; + //std::cout << "Stepsize factor: " << std::abs(width) / stepsize << std::endl; + stepsize_threshold = stepsize / 1000; + } + } + else { + stepsize /= 2; + } + if (std::abs(best_width_correction) >= std::abs(width)/2) { + //break; // Seems to prevent some numerical instabilities, not clear if useful + } + } // reached maximum recursive depth // don't bother with any more correction if (levels == 0) { - p.append(c); + try { + p.append(c); + } + catch (...) { + if ((p.finalPoint() - c.initialPoint()).length() < 1e-6) { + c.setInitial(p.finalPoint()); + } + else { + auto line = Geom::LineSegment(p.finalPoint(), c.initialPoint()); + p.append(line); + } + p.append(c); + } + return; } - // check the tolerance for our estimate to be a parallel curve - Geom::Point chk = c.pointAt(.5); - Geom::Point req = bez.pointAt(.5) + Geom::rot90(bez.unitTangentAt(.5))*width; // required accuracy - - Geom::Point const diff = req - chk; - double const err = Geom::dot(diff, diff); + // We find the point on our new curve (c) for which the distance between + // (c) and (bez) differs the most from the desired distance (width). + double worst_err = std::abs(best_residual); + double worst_time = .5; + for (size_t ii = 1; ii <= 9; ++ii) { + const double t = static_cast<double>(ii) / 10; + const Geom::Point req = bez.pointAt(t); + // We use the exact solution with nearestTime because it is numerically + // much more stable than simply assuming that the point on (c) closest + // to bez.pointAt(t) is given by c.pointAt(t) + const Geom::Point chk = c.pointAt(c.nearestTime(req)); + + Geom::Point const diff = req - chk; + const double err = std::abs(diff.length() - std::abs(width)); + if (err > worst_err) { + worst_err = err; + worst_time = t; + } + } - if (err < tol) { + if (worst_err < tol) { if (Geom::are_near(start_new, p.finalPoint())) { p.setFinal(start_new); // if it isn't near, we throw } @@ -809,7 +927,7 @@ void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, dou return; } else { // split the curve in two - std::pair<Geom::CubicBezier, Geom::CubicBezier> s = bez.subdivide(.5); + std::pair<Geom::CubicBezier, Geom::CubicBezier> s = bez.subdivide(worst_time); offset_cubic(p, s.first, width, tol, levels - 1); offset_cubic(p, s.second, width, tol, levels - 1); } @@ -827,9 +945,8 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi offset_cubic(p, cub, width, tol, levels); } -void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) +void offset_curve(Geom::Path& res, Geom::Curve const* current, double width, double tolerance) { - double const tolerance = 0.0025; size_t levels = 8; if (current->isDegenerate()) return; // don't do anything @@ -855,14 +972,14 @@ void offset_curve(Geom::Path& res, Geom::Curve const* current, double width) default: { Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), tolerance); for (size_t i = 0; i < sbasis_path.size(); ++i) - offset_curve(res, &sbasis_path[i], width); + offset_curve(res, &sbasis_path[i], width, tolerance); break; } } } else { Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), 0.1); for (size_t i = 0; i < sbasis_path.size(); ++i) - offset_curve(res, &sbasis_path[i], width); + offset_curve(res, &sbasis_path[i], width, tolerance); } } @@ -902,13 +1019,19 @@ void peak_cap(Geom::PathBuilder& res, Geom::Path const& with_dir, Geom::Path con namespace Inkscape { -Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join, LineCapType butt) +Geom::PathVector outline( + Geom::Path const& input, + double width, + double miter, + LineJoinType join, + LineCapType butt, + double tolerance) { if (input.size() == 0) return Geom::PathVector(); // nope, don't even try Geom::PathBuilder res; - Geom::Path with_dir = half_outline(input, width/2., miter, join); - Geom::Path against_dir = half_outline(input.reversed(), width/2., miter, join); + Geom::Path with_dir = half_outline(input, width/2., miter, join, tolerance); + Geom::Path against_dir = half_outline(input.reversed(), width/2., miter, join, tolerance); res.moveTo(with_dir[0].initialPoint()); res.append(with_dir); @@ -947,8 +1070,21 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li return res.peek(); } -Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join) +Geom::Path half_outline( + Geom::Path const& input, + double width, + double miter, + LineJoinType join, + double tolerance) { + if (tolerance <= 0) { + if (std::abs(width) > 0) { + tolerance = 5.0 * (std::abs(width)/100); + } + else { + tolerance = 1e-4; + } + } Geom::Path res; if (input.size() == 0) return res; @@ -963,12 +1099,13 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin res.start(start); // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well - const size_t k = (input.back_closed().isDegenerate() && input.closed()) - ?input.size_default()-1:input.size_default(); + const Geom::Curve &closingline = input.back_closed(); + const size_t k = (are_near(closingline.initialPoint(), closingline.finalPoint()) && input.closed() ) + ?input.size_open():input.size_default(); for (size_t u = 0; u < k; u += 2) { temp.clear(); - offset_curve(temp, &input[u], width); + offset_curve(temp, &input[u], width, tolerance); // on the first run through, there isn't a join if (u == 0) { @@ -981,12 +1118,11 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin // odd number of paths if (u < k - 1) { temp.clear(); - offset_curve(temp, &input[u+1], width); + offset_curve(temp, &input[u+1], width, tolerance); tangents(tang, input[u], input[u+1]); outline_join(res, temp, tang[0], tang[1], width, miter, join); } } - if (input.closed()) { Geom::Curve const &c1 = res.back(); Geom::Curve const &c2 = res.front(); @@ -998,7 +1134,6 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin outline_join(temp, temp2, tang[0], tang[1], width, miter, join); res.erase(res.begin()); res.erase_last(); - // res.append(temp); res.close(); } @@ -1010,9 +1145,8 @@ void outline_join(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang, { if (res.size() == 0 || temp.size() == 0) return; - Geom::Curve const& outgoing = temp.front(); - if (Geom::are_near(res.finalPoint(), outgoing.initialPoint())) { + if (Geom::are_near(res.finalPoint(), outgoing.initialPoint(), 0.01)) { // if the points are /that/ close, just ignore this one res.setFinal(temp.initialPoint()); res.append(temp); diff --git a/src/helper/geom-pathstroke.h b/src/helper/geom-pathstroke.h index 6a753a6fd..4e4b8b91e 100644 --- a/src/helper/geom-pathstroke.h +++ b/src/helper/geom-pathstroke.h @@ -42,12 +42,19 @@ enum LineCapType { * @param[in] miter Miter limit. Only used when @a join is one of JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE. * @param[in] join Line join type used during offset. Member of LineJoinType enum. * @param[in] cap Line cap type used during stroking. Member of LineCapType enum. + * @param[in] tolerance Tolerance, values smaller than 0 lead to automatic tolerance depending on width. * * @return Stroked path. * If the input path is closed, the resultant vector will contain two paths. * Otherwise, there should be only one in the output. */ -Geom::PathVector outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL, LineCapType cap = BUTT_FLAT); +Geom::PathVector outline( + Geom::Path const& input, + double width, + double miter, + LineJoinType join = JOIN_BEVEL, + LineCapType cap = BUTT_FLAT, + double tolerance = -1); /** * Offset the input path by @a width. @@ -57,10 +64,16 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li * @param[in] width Amount to offset. * @param[in] miter Miter limit. Only used when @a join is one of JOIN_MITER, JOIN_MITER_CLIP, and JOIN_EXTRAPOLATE. * @param[in] join Line join type used during offset. Member of LineJoinType enum. + * @param[in] tolerance Tolerance, values smaller than 0 lead to automatic tolerance depending on width. * * @return Offsetted output. */ -Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join = JOIN_BEVEL); +Geom::Path half_outline( + Geom::Path const& input, + double width, + double miter, + LineJoinType join = JOIN_BEVEL, + double tolerance = -1); /** * Builds a join on the provided path. diff --git a/src/helper/png-write.cpp b/src/helper/png-write.cpp index 2433e2777..e06c539d3 100644 --- a/src/helper/png-write.cpp +++ b/src/helper/png-write.cpp @@ -123,8 +123,8 @@ void PngTextList::add(gchar const* key, gchar const* text) static bool sp_png_write_rgba_striped(SPDocument *doc, gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi, - int (* get_rows)(guchar const **rows, void **to_free, int row, int num_rows, void *data, int color_type, int bit_depth), - void *data, bool interlace, int color_type, int bit_depth, int zlib) + int (* get_rows)(guchar const **rows, void **to_free, int row, int num_rows, void *data, int color_type, int bit_depth, int antialias), + void *data, bool interlace, int color_type, int bit_depth, int zlib, int antialiasing) { g_return_val_if_fail(filename != NULL, false); g_return_val_if_fail(data != NULL, false); @@ -286,7 +286,7 @@ sp_png_write_rgba_striped(SPDocument *doc, r = 0; while (r < static_cast<png_uint_32>(height)) { void *to_free; - int n = get_rows((unsigned char const **) row_pointers, &to_free, r, height-r, data, color_type, bit_depth); + int n = get_rows((unsigned char const **) row_pointers, &to_free, r, height-r, data, color_type, bit_depth, antialiasing); if (!n) break; png_write_rows(png_ptr, row_pointers, n); g_free(to_free); @@ -320,7 +320,7 @@ sp_png_write_rgba_striped(SPDocument *doc, * */ static int -sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, void *data, int color_type, int bit_depth) +sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, void *data, int color_type, int bit_depth, int antialiasing) { struct SPEBP *ebp = (struct SPEBP *) data; @@ -352,7 +352,7 @@ sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, v dc.setOperator(CAIRO_OPERATOR_OVER); /* Render */ - ebp->drawing->render(dc, bbox); + ebp->drawing->render(dc, bbox, 0, antialiasing); cairo_surface_destroy(s); // PNG stores data as unpremultiplied big-endian RGBA, which means @@ -396,10 +396,10 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, unsigned long bgcolor, unsigned int (*status) (float, void *), void *data, bool force_overwrite, - const std::vector<SPItem*> &items_only, bool interlace, int color_type, int bit_depth, int zlib) + const std::vector<SPItem*> &items_only, bool interlace, int color_type, int bit_depth, int zlib, int antialiasing) { return sp_export_png_file(doc, filename, Geom::Rect(Geom::Point(x0,y0),Geom::Point(x1,y1)), - width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only, interlace, color_type, bit_depth, zlib); + width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only, interlace, color_type, bit_depth, zlib, antialiasing); } ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, @@ -408,7 +408,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, unsigned long bgcolor, unsigned (*status)(float, void *), void *data, bool force_overwrite, - const std::vector<SPItem*> &items_only, bool interlace, int color_type, int bit_depth, int zlib) + const std::vector<SPItem*> &items_only, bool interlace, int color_type, int bit_depth, int zlib, int antialiasing) { g_return_val_if_fail(doc != NULL, EXPORT_ERROR); g_return_val_if_fail(filename != NULL, EXPORT_ERROR); @@ -479,7 +479,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, ebp.px = g_try_new(guchar, 4 * ebp.sheight * width); if (ebp.px) { - write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp, interlace, color_type, bit_depth, zlib); + write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp, interlace, color_type, bit_depth, zlib, antialiasing); g_free(ebp.px); } diff --git a/src/helper/png-write.h b/src/helper/png-write.h index e47f01743..06498f46e 100644 --- a/src/helper/png-write.h +++ b/src/helper/png-write.h @@ -35,13 +35,13 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi, unsigned long bgcolor, unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>(), - bool interlace = false, int color_type = 6, int bit_depth = 8, int zlib = 6); + bool interlace = false, int color_type = 6, int bit_depth = 8, int zlib = 6, int antialiasing = 2); ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename, Geom::Rect const &area, unsigned long int width, unsigned long int height, double xdpi, double ydpi, unsigned long bgcolor, unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>(), - bool interlace = false, int color_type = 6, int bit_depth = 8, int zlib = 6); + bool interlace = false, int color_type = 6, int bit_depth = 8, int zlib = 6, int antialiasing = 2); #endif // SEEN_SP_PNG_WRITE_H diff --git a/src/inkscape.cpp b/src/inkscape.cpp index 2ba85026f..bf694ada7 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -274,7 +274,7 @@ int Application::autosave() if (doc->isModifiedSinceSave()) { gchar *oldest_autosave = 0; const gchar *filename = 0; - struct stat sb; + GStatBuf sb; time_t min_time = 0; gint count = 0; diff --git a/src/io/sys.cpp b/src/io/sys.cpp index 94175176a..61a6a96f4 100644 --- a/src/io/sys.cpp +++ b/src/io/sys.cpp @@ -226,7 +226,7 @@ bool Inkscape::IO::file_is_writable( char const *utf8name) filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL ); } if ( filename ) { - struct stat st; + GStatBuf st; if (g_file_test (filename, G_FILE_TEST_EXISTS)){ if (g_lstat (filename, &st) == 0) { success = ((st.st_mode & S_IWRITE) != 0); diff --git a/src/knot-enums.h b/src/knot-enums.h index 1045e0433..ea23a5a2c 100644 --- a/src/knot-enums.h +++ b/src/knot-enums.h @@ -19,6 +19,7 @@ typedef enum { SP_KNOT_SHAPE_SQUARE, SP_KNOT_SHAPE_DIAMOND, SP_KNOT_SHAPE_CIRCLE, + SP_KNOT_SHAPE_TRIANGLE, SP_KNOT_SHAPE_CROSS, SP_KNOT_SHAPE_BITMAP, SP_KNOT_SHAPE_IMAGE diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index 95b135be0..8add35d46 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -24,6 +24,7 @@ #include "sp-pattern.h" #include "snap.h" #include "desktop.h" +#include "inkscape.h" #include "sp-namedview.h" int KnotHolderEntity::counter = 0; @@ -32,6 +33,9 @@ void KnotHolderEntity::create(SPDesktop *desktop, SPItem *item, KnotHolder *pare const gchar *tip, SPKnotShapeType shape, SPKnotModeType mode, guint32 color) { + if (!desktop) { + desktop = SP_ACTIVE_DESKTOP; + } knot = new SPKnot(desktop, tip); this->parent_holder = parent; diff --git a/src/knot.cpp b/src/knot.cpp index b57c938c3..c04206dee 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -25,8 +25,6 @@ #include "document-undo.h" #include "message-stack.h" #include "message-context.h" -#include "ui/tools-switch.h" -#include "ui/tools/tool-base.h" #include "ui/tools/node-tool.h" #include <gtk/gtk.h> @@ -71,6 +69,7 @@ SPKnot::SPKnot(SPDesktop *desktop, gchar const *tip) this->flags = 0; this->size = 8; + this->angle = 0; this->pos = Geom::Point(0, 0); this->grabbed_rel_pos = Geom::Point(0, 0); this->anchor = SP_ANCHOR_CENTER; @@ -111,6 +110,7 @@ SPKnot::SPKnot(SPDesktop *desktop, gchar const *tip) SP_TYPE_CTRL, "anchor", SP_ANCHOR_CENTER, "size", 8.0, + "angle", 0.0, "filled", TRUE, "fill_color", 0xffffff00, "stroked", TRUE, @@ -214,7 +214,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot if ((event->button.button == 1) && knot->desktop && knot->desktop->event_context && !knot->desktop->event_context->space_panning) { Geom::Point const p = knot->desktop->w2d(Geom::Point(event->button.x, event->button.y)); knot->startDragging(p, (gint) event->button.x, (gint) event->button.y, event->button.time); - + knot->grabbed_signal.emit(knot, event->button.state); consumed = TRUE; } break; @@ -252,10 +252,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot consumed = TRUE; } } - if (tools_isactive(knot->desktop, TOOLS_NODES)) { - Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(knot->desktop->event_context); - nt->update_helperpath(); - } + Inkscape::UI::Tools::sp_update_helperpath(); break; case GDK_MOTION_NOTIFY: if (grabbed && knot->desktop && knot->desktop->event_context && !knot->desktop->event_context->space_panning) { @@ -279,8 +276,6 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot } if (!moved) { - knot->grabbed_signal.emit(knot, event->motion.state); - knot->setFlag(SP_KNOT_DRAGGING, TRUE); } @@ -288,10 +283,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot sp_knot_handler_request_position(event, knot); moved = TRUE; } - if (tools_isactive(knot->desktop, TOOLS_NODES)) { - Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(knot->desktop->event_context); - nt->update_helperpath(); - } + Inkscape::UI::Tools::sp_update_helperpath(); break; case GDK_ENTER_NOTIFY: knot->setFlag(SP_KNOT_MOUSEOVER, TRUE); @@ -449,6 +441,7 @@ void SPKnot::updateCtrl() { g_object_set(this->item, "shape", this->shape, NULL); g_object_set(this->item, "mode", this->mode, NULL); g_object_set(this->item, "size", (gdouble) this->size, NULL); + g_object_set(this->item, "angle", this->angle, NULL); g_object_set(this->item, "anchor", this->anchor, NULL); if (this->pixbuf) { @@ -492,6 +485,10 @@ void SPKnot::setPixbuf(gpointer p) { pixbuf = p; } +void SPKnot::setAngle(double i) { + angle = i; +} + void SPKnot::setFill(guint32 normal, guint32 mouseover, guint32 dragging) { fill[SP_KNOT_STATE_NORMAL] = normal; fill[SP_KNOT_STATE_MOUSEOVER] = mouseover; diff --git a/src/knot.h b/src/knot.h index e3ad98e66..954ddd8a6 100644 --- a/src/knot.h +++ b/src/knot.h @@ -51,6 +51,7 @@ public: unsigned int flags; unsigned int size; /**< Always square. */ + double angle; /**< Angle of mesh handle. */ Geom::Point pos; /**< Our desktop coordinates. */ Geom::Point grabbed_rel_pos; /**< Grabbed relative position. */ Geom::Point drag_origin; /**< Origin of drag. */ @@ -92,6 +93,7 @@ public: void setAnchor(unsigned int i); void setMode(unsigned int i); void setPixbuf(void* p); + void setAngle(double i); void setFill(guint32 normal, guint32 mouseover, guint32 dragging); void setStroke(guint32 normal, guint32 mouseover, guint32 dragging); diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 6b1aba53f..e6da9ba63 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -1182,10 +1182,34 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const for(input_index = para->first_input_index ; input_index < _flow._input_stream.size() ; input_index++) { if (_flow._input_stream[input_index]->Type() == CONTROL_CODE) { Layout::InputStreamControlCode const *control_code = static_cast<Layout::InputStreamControlCode const *>(_flow._input_stream[input_index]); + if ( control_code->code == SHAPE_BREAK - || control_code->code == PARAGRAPH_BREAK) + || control_code->code == PARAGRAPH_BREAK) { + + // Add span to be used to calculate line spacing of blank lines. + UnbrokenSpan new_span; + new_span.pango_item_index = -1; + new_span.input_index = input_index; + + // No pango object, so find font and line height ourselves. + SPObject * object = static_cast<SPObject *>(control_code->source_cookie); + if (object) { + SPStyle * style = object->style; + if (style) { + new_span.font_size = style->font_size.computed * _flow.getTextLengthMultiplierDue(); + font_factory * factory = font_factory::Default(); + font_instance * font = factory->FaceFromStyle( style ); + new_span.line_height_multiplier = _computeFontLineHeight( object->style ); + new_span.line_height.set( font ); + new_span.line_height *= new_span.font_size; + } + } + new_span.text_bytes = 0; + new_span.char_index_in_para = char_index_in_para; + para->unbroken_spans.push_back(new_span); + TRACE(("add empty span for break %lu\n", para->unbroken_spans.size() - 1)); break; // stop at the end of the paragraph - else if (control_code->code == ARBITRARY_GAP) { + } else if (control_code->code == ARBITRARY_GAP) { UnbrokenSpan new_span; new_span.pango_item_index = -1; new_span.input_index = input_index; @@ -1619,8 +1643,8 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, *line_height = *strut_height; for (std::vector<ChunkInfo>::const_iterator it_chunk = chunk_info->begin() ; it_chunk != chunk_info->end() ; it_chunk++) { for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) { - TRACE((" brokenspan line_height: %f\n", it_span->start.iter_span->line_height.emSize() )); FontMetrics span_height = it_span->start.iter_span->line_height; + TRACE((" brokenspan line_height: %f\n", span_height.emSize() )); span_height.computeEffective( it_span->start.iter_span->line_height_multiplier ); line_height->max( span_height ); } diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index df7adf5b4..a1a19f2b3 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -867,6 +867,23 @@ double Layout::getActualLength() const }//namespace Text }//namespace Inkscape +std::ostream &operator<<(std::ostream &out, const Inkscape::Text::Layout::FontMetrics &f) { + out << " emSize: " << f.emSize() + << " ascent: " << f.ascent + << " descent: " << f.descent + << " xheight: " << f.xheight; + return out; +} + +std::ostream &operator<<(std::ostream &out, const Inkscape::Text::Layout::FontMetrics *f) { + out << " emSize: " << f->emSize() + << " ascent: " << f->ascent + << " descent: " << f->descent + << " xheight: " << f->xheight; + return out; +} + + /* Local Variables: diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 8b46758f0..e06b8392f 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -629,7 +629,7 @@ public: void reset() { ascent = 0.8; - descent = -0.2; + descent = 0.2; xheight = 0.5; ascent_max = 0.8; descent_max = 0.2; @@ -664,7 +664,7 @@ public: // private: double ascent; // Typographic ascent. - double descent; // Typographic descent. + double descent; // Typographic descent. (Normally positive). double xheight; // Height of 'x' measured from alphabetic baseline. double ascent_max; // Maximum ascent of all glyphs in font. double descent_max; // Maximum descent of all glyphs in font. @@ -1189,6 +1189,10 @@ inline bool Layout::iterator::prevCharacter() }//namespace Text }//namespace Inkscape +std::ostream &operator<<(std::ostream &out, const Inkscape::Text::Layout::FontMetrics &f); +std::ostream &operator<<(std::ostream &out, const Inkscape::Text::Layout::FontMetrics *f); + + #endif diff --git a/src/libuemf/uemf.h b/src/libuemf/uemf.h index 82fd0990c..05566af06 100644 --- a/src/libuemf/uemf.h +++ b/src/libuemf/uemf.h @@ -168,12 +168,24 @@ extern "C" { #define U_ROUND(A) ( (A) > 0 ? floor((A)+0.5) : ( (A) < 0 ? -floor(-(A)+0.5) : (A) ) ) #define MAKE_MIN_PTR(A,B) ( A < B ? A : B) -/* This is tricky. The next one can be called with a size which is either an int or an unsigned int. - The former can be negative, which is obviously wrong, but testing for that means that the size cannot - be more than INT_MAX/2. Accept that limitation since no reasonable EMF record or file should ever be that large. - B must be an INT or size_t. - If a uint16_t is used gcc complains about the first test. Force B to be at least as big as int (at run time) */ -#define IS_MEM_UNSAFE(A,B,C) ( (sizeof(B) < sizeof(int) || (int)(B)) < 0 ? 1 : ((int8_t *)(A) > (int8_t *)(C) ? 1 : ((int8_t *)(C) - (int8_t *)(A) >= (int)(B) ? 0 : 1 ))) //!< Return 1 when a region of memory starting at A of B bytes extends beyond pointer C + +/* IS_MEM_UNSAFE takes 3 parameters: + A start address of a block of allocated memory + B offset into this block starting at A + C address of final byte of a block of allocated memory. + Returns + 1 if B cannot be an int or size_t + 1 if C > A + 1 if A+B is not in the range A to C, inclusive + 0 otherwise. + B may be an int, an unsigned int, or a size_t. An int can be negative, + which is obviously wrong, but testing for that means that the size + of B cannot be more than INT_MAX/2. Accept that limitation since + no reasonable EMF record or file should ever be that large. + If B is a uint16_t gcc complains about the first test. + This Macro must not be used where B needs more than 32 bits! +*/ +#define IS_MEM_UNSAFE(A,B,C) ( (sizeof(B) < sizeof(int) || (int)(B) < 0) ? 1 : ((int8_t *)(A) > (int8_t *)(C) ? 1 : ((int8_t *)(C) - (int8_t *)(A) >= (int)(B) ? 0 : 1 ))) /** @} */ diff --git a/src/livarot/LivarotDefs.h b/src/livarot/LivarotDefs.h index abeff3861..d020841b0 100644 --- a/src/livarot/LivarotDefs.h +++ b/src/livarot/LivarotDefs.h @@ -40,18 +40,6 @@ enum found_between = 4 }; -// boolean operation -enum bool_op -{ - bool_op_union, // A OR B - bool_op_inters, // A AND B - bool_op_diff, // A \ B - bool_op_symdiff, // A XOR B - bool_op_cut, // coupure (pleines) - bool_op_slice // coupure (contour) -}; -typedef enum bool_op BooleanOp; - // types of cap for stroking polylines enum butt_typ { diff --git a/src/livarot/Shape.h b/src/livarot/Shape.h index 98fc2d7bf..da1edc6b3 100644 --- a/src/livarot/Shape.h +++ b/src/livarot/Shape.h @@ -17,6 +17,7 @@ #include <2geom/point.h> #include "livarot/LivarotDefs.h" +#include "object-set.h" class Path; class FloatLigne; diff --git a/src/livarot/int-line.h b/src/livarot/int-line.h index 1d3bbd9d2..ed3ccfb96 100644 --- a/src/livarot/int-line.h +++ b/src/livarot/int-line.h @@ -2,6 +2,7 @@ #define INKSCAPE_LIVAROT_INT_LINE_H #include "livarot/LivarotDefs.h" +#include "object-set.h" /** \file * Coverage with integer boundaries. diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 792bb5d7a..5cc0d6f20 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -350,6 +350,7 @@ Effect::Effect(LivePathEffectObject *lpeobject) oncanvasedit_it(0), is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true), show_orig_path(false), + erase_extra_objects(true), lpeobj(lpeobject), concatenate_before_pwd2(false), sp_lpe_item(NULL), @@ -611,15 +612,15 @@ Effect::registerParameter(Parameter * param) * Add all registered LPE knotholder handles to the knotholder */ void -Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { +Effect::addHandles(KnotHolder *knotholder, SPItem *item) { using namespace Inkscape::LivePathEffect; // add handles provided by the effect itself - addKnotHolderEntities(knotholder, desktop, item); + addKnotHolderEntities(knotholder, item); // add handles provided by the effect's parameters (if any) for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) { - (*p)->addKnotHolderEntities(knotholder, desktop, item); + (*p)->addKnotHolderEntities(knotholder, item); } } @@ -659,14 +660,7 @@ Effect::addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathV */ void Effect::update_helperpath() { - using namespace Inkscape::UI; - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop) { - if (tools_isactive(desktop, TOOLS_NODES)) { - Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(desktop->event_context); - nt->update_helperpath(); - } - } + Inkscape::UI::Tools::sp_update_helperpath(); } /** diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 5ca53486c..1997ff0ca 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -100,7 +100,7 @@ public: // /TODO: in view of providesOwnFlashPaths() below, this is somewhat redundant // (but spiro lpe still needs it!) virtual LPEPathFlashType pathFlashType() const { return DEFAULT; } - void addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + void addHandles(KnotHolder *knotholder, SPItem *item); std::vector<Geom::PathVector> getCanvasIndicators(SPLPEItem const* lpeitem); void update_helperpath(); @@ -123,6 +123,7 @@ public: void editNextParamOncanvas(SPItem * item, SPDesktop * desktop); bool apply_to_clippath_and_mask; + bool erase_extra_objects; // set this to false allow retain extra generated objects, see measure line LPE bool upd_params; protected: @@ -141,7 +142,7 @@ protected: void registerParameter(Parameter * param); Parameter * getNextOncanvasEditableParam(); - virtual void addKnotHolderEntities(KnotHolder * /*knotholder*/, SPDesktop * /*desktop*/, SPItem * /*item*/) {}; + virtual void addKnotHolderEntities(KnotHolder * /*knotholder*/, SPItem * /*item*/) {}; virtual void addCanvasIndicators(SPLPEItem const* lpeitem, std::vector<Geom::PathVector> &hp_vec); diff --git a/src/live_effects/lpe-angle_bisector.cpp b/src/live_effects/lpe-angle_bisector.cpp index 9bfbf4ca8..56d33eb4b 100644 --- a/src/live_effects/lpe-angle_bisector.cpp +++ b/src/live_effects/lpe-angle_bisector.cpp @@ -8,15 +8,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-angle_bisector.h" - -#include <2geom/sbasis-to-bezier.h> - +#include "2geom/sbasis-to-bezier.h" #include "sp-lpe-item.h" #include "knot-holder-entity.h" #include "knotholder.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-attach-path.cpp b/src/live_effects/lpe-attach-path.cpp index d2b44dd4e..302165719 100644 --- a/src/live_effects/lpe-attach-path.cpp +++ b/src/live_effects/lpe-attach-path.cpp @@ -4,15 +4,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> #include <math.h> - #include "live_effects/lpe-attach-path.h" - #include "display/curve.h" #include "sp-shape.h" #include "sp-text.h" #include "2geom/path-sink.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-bendpath.cpp b/src/live_effects/lpe-bendpath.cpp index c24d38d7b..b1e133292 100644 --- a/src/live_effects/lpe-bendpath.cpp +++ b/src/live_effects/lpe-bendpath.cpp @@ -7,9 +7,10 @@ #include "live_effects/lpe-bendpath.h" #include "sp-item-group.h" - #include "knot-holder-entity.h" #include "knotholder.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using std::vector; diff --git a/src/live_effects/lpe-bounding-box.cpp b/src/live_effects/lpe-bounding-box.cpp index 2de768c3a..11fb34e04 100644 --- a/src/live_effects/lpe-bounding-box.cpp +++ b/src/live_effects/lpe-bounding-box.cpp @@ -3,14 +3,14 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - -#include <glibmm/i18n.h> - #include "live_effects/lpe-bounding-box.h" #include "display/curve.h" #include "sp-shape.h" #include "sp-text.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h index 7823f00f0..90cf82c19 100644 --- a/src/live_effects/lpe-bspline.h +++ b/src/live_effects/lpe-bspline.h @@ -6,8 +6,8 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "live_effects/effect.h" +#include "live_effects/effect.h" #include <vector> namespace Inkscape { diff --git a/src/live_effects/lpe-circle_3pts.cpp b/src/live_effects/lpe-circle_3pts.cpp index 18252f6a0..3410b13f2 100644 --- a/src/live_effects/lpe-circle_3pts.cpp +++ b/src/live_effects/lpe-circle_3pts.cpp @@ -17,6 +17,8 @@ // You might need to include other 2geom files. You can add them here: #include <2geom/circle.h> #include <2geom/path-sink.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-circle_with_radius.cpp b/src/live_effects/lpe-circle_with_radius.cpp index 6e03cb1ce..fcefc4ec6 100644 --- a/src/live_effects/lpe-circle_with_radius.cpp +++ b/src/live_effects/lpe-circle_with_radius.cpp @@ -17,6 +17,8 @@ // You might need to include other 2geom files. You can add them here: #include <2geom/circle.h> #include <2geom/path-sink.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using namespace Geom; diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp index 7541c0be2..10418a02d 100644 --- a/src/live_effects/lpe-clone-original.cpp +++ b/src/live_effects/lpe-clone-original.cpp @@ -4,11 +4,10 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-clone-original.h" - #include "display/curve.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-constructgrid.cpp b/src/live_effects/lpe-constructgrid.cpp index 4af8891e8..8d24f9f47 100644 --- a/src/live_effects/lpe-constructgrid.cpp +++ b/src/live_effects/lpe-constructgrid.cpp @@ -10,9 +10,9 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-constructgrid.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index 1133e083a..813f25d3d 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -11,36 +11,18 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <gtkmm.h> #include <gdk/gdk.h> #include <2geom/path-intersection.h> #include <2geom/sbasis-to-bezier.h> +#include <2geom/intersection-graph.h> #include "live_effects/lpe-copy_rotate.h" - -#include "knotholder.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { -namespace CR { - -class KnotHolderEntityStartingAngle : public LPEKnotHolderEntity { -public: - KnotHolderEntityStartingAngle(LPECopyRotate *effect) : LPEKnotHolderEntity(effect) {}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; -}; - -class KnotHolderEntityRotationAngle : public LPEKnotHolderEntity { -public: - KnotHolderEntityRotationAngle(LPECopyRotate *effect) : LPEKnotHolderEntity(effect) {}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; -}; - -} // namespace CR - bool pointInTriangle(Geom::Point const &p, Geom::Point const &p1, Geom::Point const &p2, Geom::Point const &p3) { @@ -58,28 +40,31 @@ pointInTriangle(Geom::Point const &p, Geom::Point const &p1, Geom::Point const & LPECopyRotate::LPECopyRotate(LivePathEffectObject *lpeobject) : Effect(lpeobject), - origin(_("Origin"), _("Origin of the rotation"), "origin", &wr, this), - starting_angle(_("Starting:"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0), - rotation_angle(_("Rotation angle:"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0), - num_copies(_("Number of copies:"), _("Number of copies of the original path"), "num_copies", &wr, this, 6), + origin(_("Origin"), _("Adjust origin of the rotation"), "origin", &wr, this, _("Adjust origin of the rotation")), + starting_point(_("Start point"), _("Starting point to define start angle"), "starting_point", &wr, this, _("Adjust starting point to define start angle")), + starting_angle(_("Starting angle"), _("Angle of the first copy"), "starting_angle", &wr, this, 0.0), + rotation_angle(_("Rotation angle"), _("Angle between two successive copies"), "rotation_angle", &wr, this, 60.0), + num_copies(_("Number of copies"), _("Number of copies of the original path"), "num_copies", &wr, this, 6), copies_to_360(_("360º Copies"), _("No rotation angle, fixed to 360º"), "copies_to_360", &wr, this, true), - fuse_paths(_("Fuse paths"), _("Fuse paths by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false), + fuse_paths(_("Kaleidoskope"), _("Kaleidoskope by helper line, use fill-rule: evenodd for best result"), "fuse_paths", &wr, this, false), + join_paths(_("Join paths"), _("Join paths, use fill-rule: evenodd for best result"), "join_paths", &wr, this, false), dist_angle_handle(100.0) { show_orig_path = true; _provides_knotholder_entities = true; - apply_to_clippath_and_mask = true; - // register all your parameters here, so Inkscape knows which parameters this effect has: registerParameter(&copies_to_360); registerParameter(&fuse_paths); + registerParameter(&join_paths); registerParameter(&starting_angle); + registerParameter(&starting_point); registerParameter(&rotation_angle); registerParameter(&num_copies); registerParameter(&origin); num_copies.param_make_integer(true); num_copies.param_set_range(0, 1000); + apply_to_clippath_and_mask = true; } LPECopyRotate::~LPECopyRotate() @@ -87,6 +72,41 @@ LPECopyRotate::~LPECopyRotate() } +Gtk::Widget * LPECopyRotate::newWidget() +{ + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter *param = *it; + Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); + Glib::ustring *tip = param->param_getTooltip(); + if (widg) { + if (param->param_key != "starting_point") { + vbox->pack_start(*widg, true, true, 2); + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + } + + ++it; + } + return dynamic_cast<Gtk::Widget *>(vbox); +} + + void LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) { @@ -95,7 +115,7 @@ LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) A = Point(boundingbox_X.min(), boundingbox_Y.middle()); B = Point(boundingbox_X.middle(), boundingbox_Y.middle()); - origin.param_setValue(A); + origin.param_setValue(A, true); origin.param_update_default(A); dist_angle_handle = L2(B - A); dir = unit_vector(B - A); @@ -104,11 +124,6 @@ LPECopyRotate::doOnApply(SPLPEItem const* lpeitem) void LPECopyRotate::transform_multiply(Geom::Affine const& postmul, bool set) { - if(fuse_paths) { - Geom::Coord angle = Geom::deg_from_rad(atan(-postmul[1]/postmul[0])); - angle += starting_angle; - starting_angle.param_set_value(angle); - } // cycle through all parameters. Most parameters will not need transformation, but path and point params do. for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) { @@ -146,14 +161,25 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) dir = unit_vector(B - A); // I first suspected the minus sign to be a bug in 2geom but it is // likely due to SVG's choice of coordinate system orientation (max) + bool near = Geom::are_near(previous_start_point, (Geom::Point)starting_point, 0.01); + if (!near) { + starting_angle.param_set_value(deg_from_rad(-angle_between(dir, starting_point - origin))); + if (GDK_SHIFT_MASK) { + dist_angle_handle = L2(B - A); + } else { + dist_angle_handle = L2(starting_point - origin); + } + } start_pos = origin + dir * Rotate(-rad_from_deg(starting_angle)) * dist_angle_handle; rot_pos = origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle; + near = Geom::are_near(start_pos, (Geom::Point)starting_point, 0.01); + if (!near) { + starting_point.param_setValue(start_pos, true); + } + previous_start_point = (Geom::Point)starting_point; if ( fuse_paths || copies_to_360 ) { rot_pos = origin; } - SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); - item->apply_to_clippath(item); - item->apply_to_mask(item); } void @@ -235,19 +261,8 @@ LPECopyRotate::setFusion(Geom::PathVector &path_on, Geom::Path divider, double s if (i%2 != 0) { Geom::Point A = (Geom::Point)origin; Geom::Point B = origin + dir * Geom::Rotate(-Geom::rad_from_deg((rotation_angle*i)+starting_angle)) * size_divider; - Geom::Translate m1(A[0], A[1]); - double hyp = Geom::distance(A, B); - double c = (B[0] - A[0]) / hyp; // cos(alpha) - double s = (B[1] - A[1]) / hyp; // sin(alpha) - - Geom::Affine m2(c, -s, s, c, 0.0, 0.0); - Geom::Scale sca(1.0, -1.0); - - Geom::Affine tmp_m = m1.inverse() * m2; - m = tmp_m; - m = m * sca; - m = m * m2.inverse(); - m = m * m1; + Geom::Line ls(A,B); + m = Geom::reflection (ls.vector(), A); } else { append_path = original; } @@ -388,10 +403,26 @@ LPECopyRotate::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p output = paths_to_pw(path_out); } } else { + Geom::PathVector output_pv = path_from_piecewise(output , 0.01); for (int i = 0; i < num_copies; ++i) { Rotate rot(-rad_from_deg(rotation_angle * i)); Affine t = pre * rot * Translate(origin); - output.concat(pwd2_in * t); + if (join_paths) { + Geom::PathVector join_pv = path_from_piecewise(pwd2_in * t , 0.01); + Geom::PathIntersectionGraph *pig = new Geom::PathIntersectionGraph(output_pv, join_pv); + if (pig) { + if (!output_pv.empty()) { + output_pv = pig->getUnion(); + } else { + output_pv = join_pv; + } + } + } else { + output.concat(pwd2_in * t); + } + } + if (join_paths) { + output = paths_to_pw(output_pv); } } return output; @@ -418,85 +449,6 @@ LPECopyRotate::resetDefaults(SPItem const* item) original_bbox(SP_LPE_ITEM(item)); } -void -LPECopyRotate::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) -{ - { - KnotHolderEntity *e = new CR::KnotHolderEntityStartingAngle(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("Adjust the starting angle")); - knotholder->add(e); - } - { - KnotHolderEntity *e = new CR::KnotHolderEntityRotationAngle(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("Adjust the rotation angle")); - knotholder->add(e); - } -}; - -namespace CR { - -using namespace Geom; - -void -KnotHolderEntityStartingAngle::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) -{ - LPECopyRotate* lpe = dynamic_cast<LPECopyRotate *>(_effect); - - Geom::Point const s = snap_knot_position(p, state); - - // I first suspected the minus sign to be a bug in 2geom but it is - // likely due to SVG's choice of coordinate system orientation (max) - lpe->starting_angle.param_set_value(deg_from_rad(-angle_between(lpe->dir, s - lpe->origin))); - if (state & GDK_SHIFT_MASK) { - lpe->dist_angle_handle = L2(lpe->B - lpe->A); - } else { - lpe->dist_angle_handle = L2(p - lpe->origin); - } - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); -} - -void -KnotHolderEntityRotationAngle::knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint state) -{ - LPECopyRotate* lpe = dynamic_cast<LPECopyRotate *>(_effect); - - Geom::Point const s = snap_knot_position(p, state); - - // I first suspected the minus sign to be a bug in 2geom but it is - // likely due to SVG's choice of coordinate system orientation (max) - lpe->rotation_angle.param_set_value(deg_from_rad(-angle_between(lpe->dir, s - lpe->origin)) - lpe->starting_angle); - if (state & GDK_SHIFT_MASK) { - lpe->dist_angle_handle = L2(lpe->B - lpe->A); - } else { - lpe->dist_angle_handle = L2(p - lpe->origin); - } - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); -} - -Geom::Point -KnotHolderEntityStartingAngle::knot_get() const -{ - LPECopyRotate const *lpe = dynamic_cast<LPECopyRotate const*>(_effect); - return lpe->start_pos; -} - -Geom::Point -KnotHolderEntityRotationAngle::knot_get() const -{ - LPECopyRotate const *lpe = dynamic_cast<LPECopyRotate const*>(_effect); - return lpe->rot_pos; -} - -} // namespace CR - -/* ######################## */ - } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-copy_rotate.h b/src/live_effects/lpe-copy_rotate.h index 87af867df..c2ae2daf1 100644 --- a/src/live_effects/lpe-copy_rotate.h +++ b/src/live_effects/lpe-copy_rotate.h @@ -21,12 +21,6 @@ namespace Inkscape { namespace LivePathEffect { -namespace CR { -// we need a separate namespace to avoid clashes with LPEPerpBisector -class KnotHolderEntityStartingAngle; -class KnotHolderEntityRotationAngle; -} - class LPECopyRotate : public Effect, GroupBBoxEffect { public: LPECopyRotate(LivePathEffectObject *lpeobject); @@ -38,26 +32,25 @@ public: virtual void split(Geom::PathVector &path_in, Geom::Path const ÷r); virtual void resetDefaults(SPItem const* item); virtual void transform_multiply(Geom::Affine const& postmul, bool set); - /* the knotholder entity classes must be declared friends */ - friend class CR::KnotHolderEntityStartingAngle; - friend class CR::KnotHolderEntityRotationAngle; - void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); - + virtual Gtk::Widget * newWidget(); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); private: PointParam origin; + PointParam starting_point; ScalarParam starting_angle; ScalarParam rotation_angle; ScalarParam num_copies; BoolParam copies_to_360; BoolParam fuse_paths; + BoolParam join_paths; Geom::Point A; Geom::Point B; Geom::Point dir; Geom::Point start_pos; Geom::Point rot_pos; + Geom::Point previous_start_point; double dist_angle_handle; LPECopyRotate(const LPECopyRotate&); LPECopyRotate& operator=(const LPECopyRotate&); diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp index 3beedaf57..38cbeaac0 100644 --- a/src/live_effects/lpe-curvestitch.cpp +++ b/src/live_effects/lpe-curvestitch.cpp @@ -13,8 +13,6 @@ */ #include "ui/widget/scalar.h" -#include <glibmm/i18n.h> - #include "live_effects/lpe-curvestitch.h" #include "sp-path.h" @@ -22,6 +20,9 @@ #include "xml/repr.h" #include <2geom/bezier-to-sbasis.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-dynastroke.cpp b/src/live_effects/lpe-dynastroke.cpp index 7e22f6e51..50bbe6451 100644 --- a/src/live_effects/lpe-dynastroke.cpp +++ b/src/live_effects/lpe-dynastroke.cpp @@ -16,6 +16,8 @@ #include <2geom/bezier-to-sbasis.h> #include <2geom/sbasis-math.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-ellipse_5pts.cpp b/src/live_effects/lpe-ellipse_5pts.cpp index 0371fc313..28e7058d7 100644 --- a/src/live_effects/lpe-ellipse_5pts.cpp +++ b/src/live_effects/lpe-ellipse_5pts.cpp @@ -12,15 +12,14 @@ */ #include "live_effects/lpe-ellipse_5pts.h" - -// You might need to include other 2geom files. You can add them here: -#include <glibmm/i18n.h> #include <2geom/circle.h> #include <2geom/ellipse.h> #include <2geom/path-sink.h> #include "inkscape.h" #include "desktop.h" #include "message-stack.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-envelope.cpp b/src/live_effects/lpe-envelope.cpp index c3b0a7c10..61a696435 100644 --- a/src/live_effects/lpe-envelope.cpp +++ b/src/live_effects/lpe-envelope.cpp @@ -6,6 +6,8 @@ #include "live_effects/lpe-envelope.h" #include "display/curve.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using std::vector; diff --git a/src/live_effects/lpe-extrude.cpp b/src/live_effects/lpe-extrude.cpp index d22007f76..daa30d45a 100644 --- a/src/live_effects/lpe-extrude.cpp +++ b/src/live_effects/lpe-extrude.cpp @@ -12,10 +12,10 @@ */ #include "live_effects/lpe-extrude.h" - +#include "sp-item.h" +// TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> -#include "sp-item.h" namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp index 2087925fa..1e2eadfdb 100644 --- a/src/live_effects/lpe-fill-between-many.cpp +++ b/src/live_effects/lpe-fill-between-many.cpp @@ -11,7 +11,7 @@ #include "display/curve.h" #include "sp-shape.h" #include "sp-text.h" - +// TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> namespace Inkscape { diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp index b1e328d18..0dbebdf26 100644 --- a/src/live_effects/lpe-fill-between-strokes.cpp +++ b/src/live_effects/lpe-fill-between-strokes.cpp @@ -3,14 +3,13 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - -#include <glibmm/i18n.h> - #include "live_effects/lpe-fill-between-strokes.h" #include "display/curve.h" #include "sp-shape.h" #include "sp-text.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp index 24ee2ccc3..1e2df7dc8 100644 --- a/src/live_effects/lpe-fillet-chamfer.cpp +++ b/src/live_effects/lpe-fillet-chamfer.cpp @@ -25,8 +25,8 @@ // for programmatically updating knots #include "ui/tools-switch.h" - // TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using namespace Geom; namespace Inkscape { diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp index 1d5398aa5..dad520041 100644 --- a/src/live_effects/lpe-gears.cpp +++ b/src/live_effects/lpe-gears.cpp @@ -7,10 +7,9 @@ */ #include "live_effects/lpe-gears.h" - -#include <glibmm/i18n.h> - #include <2geom/bezier-to-sbasis.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using std::vector; using namespace Geom; @@ -208,7 +207,7 @@ LPEGears::LPEGears(LivePathEffectObject *lpeobject) : Effect(lpeobject), teeth(_("_Teeth:"), _("The number of teeth"), "teeth", &wr, this, 10), phi(_("_Phi:"), _("Tooth pressure angle (typically 20-25 deg). The ratio of teeth not in contact."), "phi", &wr, this, 5), - min_radius(_("Min Radius:"), _("Minimun radius, low balues can slow"), "min_radius", &wr, this, 5.0) + min_radius(_("Min Radius:"), _("Minimum radius, low values can be slow"), "min_radius", &wr, this, 5.0) { /* Tooth pressure angle: The angle between the tooth profile and a perpendicular to the pitch * circle, usually at the point where the pitch circle meets the tooth profile. Standard angles diff --git a/src/live_effects/lpe-interpolate.cpp b/src/live_effects/lpe-interpolate.cpp index 43da4d105..e95dc5f38 100644 --- a/src/live_effects/lpe-interpolate.cpp +++ b/src/live_effects/lpe-interpolate.cpp @@ -9,15 +9,15 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - -#include <glibmm/i18n.h> - #include "live_effects/lpe-interpolate.h" #include <2geom/sbasis-to-bezier.h> #include "sp-path.h" #include "display/curve.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp index ab0576174..0a0bcea14 100644 --- a/src/live_effects/lpe-interpolate_points.cpp +++ b/src/live_effects/lpe-interpolate_points.cpp @@ -12,8 +12,9 @@ */ #include "live_effects/lpe-interpolate_points.h" - #include "live_effects/lpe-powerstroke-interpolators.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp index 3bfbd6288..dacb87dd9 100644 --- a/src/live_effects/lpe-jointype.cpp +++ b/src/live_effects/lpe-jointype.cpp @@ -19,6 +19,8 @@ #include <2geom/elliptical-arc.h> #include "lpe-jointype.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp index fbc32cf3e..2defecb77 100644 --- a/src/live_effects/lpe-knot.cpp +++ b/src/live_effects/lpe-knot.cpp @@ -20,18 +20,21 @@ #include "knot-holder-entity.h" #include "knotholder.h" -#include <glibmm/i18n.h> #include <gdk/gdk.h> #include <2geom/sbasis-to-bezier.h> #include <2geom/bezier-to-sbasis.h> #include <2geom/basic-intersection.h> +#include "helper/geom.h" // for change crossing undo #include "verbs.h" #include "document.h" #include "document-undo.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { @@ -392,14 +395,14 @@ LPEKnot::doEffect_path (Geom::PathVector const &path_in) if (gpaths.size()==0){ return path_in; } - - for (unsigned comp=0; comp<path_in.size(); comp++){ + Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in); + for (unsigned comp=0; comp<original_pathv.size(); comp++){ //find the relevant path component in gpaths (required to allow groups!) //Q: do we always receive the group members in the same order? can we rest on that? unsigned i0 = 0; for (i0=0; i0<gpaths.size(); i0++){ - if (path_in[comp]==gpaths[i0]) break; + if (original_pathv[comp]==gpaths[i0]) break; } if (i0 == gpaths.size() ) {THROW_EXCEPTION("lpe-knot error: group member not recognized");}// this should not happen... @@ -514,7 +517,7 @@ collectPathsAndWidths (SPLPEItem const *lpeitem, Geom::PathVector &paths, std::v c = SP_SHAPE(lpeitem)->getCurve(); } if (c) { - Geom::PathVector subpaths = c->get_pathvector(); + Geom::PathVector subpaths = pathv_to_linear_and_cubic_beziers(c->get_pathvector()); for (unsigned i=0; i<subpaths.size(); i++){ paths.push_back(subpaths[i]); //FIXME: do we have to be more carefull when trying to access stroke width? @@ -612,10 +615,10 @@ LPEKnot::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::Pat hp_vec.push_back(pathv); } -void LPEKnot::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +void LPEKnot::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { KnotHolderEntity *e = new KnotHolderEntityCrossingSwitcher(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + e->create( NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Drag to select a crossing, click to flip it") ); knotholder->add(e); }; diff --git a/src/live_effects/lpe-knot.h b/src/live_effects/lpe-knot.h index 95bfaf6e1..ac518b97c 100644 --- a/src/live_effects/lpe-knot.h +++ b/src/live_effects/lpe-knot.h @@ -61,7 +61,7 @@ public: /* the knotholder entity classes must be declared friends */ friend class KnotHolderEntityCrossingSwitcher; - void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); diff --git a/src/live_effects/lpe-lattice.cpp b/src/live_effects/lpe-lattice.cpp index 091b6ddca..acffed000 100644 --- a/src/live_effects/lpe-lattice.cpp +++ b/src/live_effects/lpe-lattice.cpp @@ -20,7 +20,8 @@ #include <2geom/sbasis-2d.h> #include <2geom/bezier-to-sbasis.h> - +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using namespace Geom; namespace Inkscape { diff --git a/src/live_effects/lpe-lattice2.cpp b/src/live_effects/lpe-lattice2.cpp index 9e9fc153a..e827491c0 100644 --- a/src/live_effects/lpe-lattice2.cpp +++ b/src/live_effects/lpe-lattice2.cpp @@ -16,12 +16,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <gtkmm.h> #include "live_effects/lpe-lattice2.h" #include "display/curve.h" #include "helper/geom.h" #include <2geom/sbasis-2d.h> #include <2geom/bezier-to-sbasis.h> + // TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using namespace Geom; diff --git a/src/live_effects/lpe-lattice2.h b/src/live_effects/lpe-lattice2.h index 4a025d182..59a0350d3 100644 --- a/src/live_effects/lpe-lattice2.h +++ b/src/live_effects/lpe-lattice2.h @@ -18,7 +18,7 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gtkmm.h> + #include "live_effects/effect.h" #include "live_effects/parameter/enum.h" #include "live_effects/parameter/point.h" diff --git a/src/live_effects/lpe-line_segment.cpp b/src/live_effects/lpe-line_segment.cpp index 4c9edabd4..cc024fb92 100644 --- a/src/live_effects/lpe-line_segment.cpp +++ b/src/live_effects/lpe-line_segment.cpp @@ -13,6 +13,8 @@ #include "live_effects/lpe-line_segment.h" #include "ui/tools/lpe-tool.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-measure-line.cpp b/src/live_effects/lpe-measure-line.cpp index e8ceb7e51..af2a8e919 100644 --- a/src/live_effects/lpe-measure-line.cpp +++ b/src/live_effects/lpe-measure-line.cpp @@ -14,8 +14,7 @@ #include <libnrtype/font-lister.h> #include "inkscape.h" #include "xml/node.h" -#include "uri.h" -#include "uri-references.h" +#include "xml/sp-css-attr.h" #include "preferences.h" #include "util/units.h" #include "svg/svg-length.h" @@ -29,7 +28,6 @@ #include "sp-item.h" #include "sp-shape.h" #include "sp-path.h" -#include "desktop.h" #include "document.h" #include <iomanip> @@ -54,16 +52,17 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : orientation(_("Orientation"), _("Orientation method"), "orientation", OMConverter, &wr, this, OM_PARALLEL, false), curve_linked(_("Curve on origin"), _("Curve on origin, set 0 to start/end"), "curve_linked", &wr, this, 1), precision(_("Precision*"), _("Precision"), "precision", &wr, this, 2), - position(_("Positon*"), _("Positon"), "position", &wr, this, 5), + position(_("Position*"), _("Position"), "position", &wr, this, 5), text_top_bottom(_("Text top/bottom*"), _("Text top/bottom"), "text_top_bottom", &wr, this, 0), text_right_left(_("Text right/left*"), _("Text right/left"), "text_right_left", &wr, this, 0), helpline_distance(_("Helpline distance*"), _("Helpline distance"), "helpline_distance", &wr, this, 0.0), helpline_overlap(_("Helpline overlap*"), _("Helpline overlap"), "helpline_overlap", &wr, this, 2.0), scale(_("Scale*"), _("Scaling factor"), "scale", &wr, this, 1.0), - format(_("Format*"), _("Format the number ex:{measure} {unit}, return to save"), "format", &wr, this,"measure unit"), + format(_("Format*"), _("Format the number ex:{measure} {unit}, return to save"), "format", &wr, this,"{measure}{unit}"), + id_origin("id_origin", "id_origin", "id_origin", &wr, this,""), arrows_outside(_("Arrows outside"), _("Arrows outside"), "arrows_outside", &wr, this, false), flip_side(_("Flip side*"), _("Flip side"), "flip_side", &wr, this, false), - scale_insensitive(_("Scale insensitive*"), _("Scale insensitive to transforms in element, parents..."), "scale_insensitive", &wr, this, true), + scale_sensitive(_("Scale sensitive*"), _("Costrained scale sensitive to transformed containers"), "scale_sensitive", &wr, this, true), local_locale(_("Local Number Format*"), _("Local number format"), "local_locale", &wr, this, true), line_group_05(_("Line Group 0.5*"), _("Line Group 0.5, from 0.7"), "line_group_05", &wr, this, true), rotate_anotation(_("Rotate Anotation*"), _("Rotate Anotation"), "rotate_anotation", &wr, this, true), @@ -88,7 +87,7 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : registerParameter(&format); registerParameter(&arrows_outside); registerParameter(&flip_side); - registerParameter(&scale_insensitive); + registerParameter(&scale_sensitive); registerParameter(&local_locale); registerParameter(&line_group_05); registerParameter(&rotate_anotation); @@ -97,6 +96,8 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : registerParameter(&helperlines_format); registerParameter(&anotation_format); registerParameter(&arrows_format); + registerParameter(&id_origin); + id_origin.param_hide_canvas_text(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::ustring fontbutton_value = prefs->getString("/live_effects/measure-line/fontbutton"); if(fontbutton_value.empty()){ @@ -124,7 +125,7 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : anotation_format.param_update_default(prefs->getString("/live_effects/measure-line/anotation_format")); arrows_format.param_update_default(prefs->getString("/live_effects/measure-line/arrows_format")); flip_side.param_update_default(prefs->getBool("/live_effects/measure-line/flip_side")); - scale_insensitive.param_update_default(prefs->getBool("/live_effects/measure-line/scale_insensitive")); + scale_sensitive.param_update_default(prefs->getBool("/live_effects/measure-line/scale_sensitive")); local_locale.param_update_default(prefs->getBool("/live_effects/measure-line/local_locale")); line_group_05.param_update_default(prefs->getBool("/live_effects/measure-line/line_group_05")); rotate_anotation.param_update_default(prefs->getBool("/live_effects/measure-line/rotate_anotation")); @@ -158,7 +159,8 @@ LPEMeasureLine::LPEMeasureLine(LivePathEffectObject *lpeobject) : helpline_overlap.param_set_range(-999999.0, 999999.0); helpline_overlap.param_set_increments(1, 1); helpline_overlap.param_set_digits(2); - erase = true; + start_stored = Geom::Point(0,0); + end_stored = Geom::Point(0,0); } LPEMeasureLine::~LPEMeasureLine() {} @@ -168,614 +170,568 @@ void swap(Geom::Point &A, Geom::Point &B){ A = B; B = tmp; } -void -LPEMeasureLine::doOnApply(SPLPEItem const* lpeitem) -{ - if (!SP_IS_SHAPE(lpeitem)) { - g_warning("LPE measure line can only be applied to shapes (not groups)."); - SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); - item->removeCurrentPathEffect(false); - } -} void -LPEMeasureLine::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) +LPEMeasureLine::createArrowMarker(const char * mode) { - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - Inkscape::URI SVGElem_uri(((Glib::ustring)"#" + (Glib::ustring)"text-on-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - Inkscape::URIReference* SVGElemRef = new Inkscape::URIReference(desktop->doc()); - SVGElemRef->attach(SVGElem_uri); - SPObject *elemref = NULL; - Inkscape::XML::Node *node = NULL; - if (elemref = SVGElemRef->getObject()) { - node = elemref->getRepr(); - if (!this->isVisible()) { - node->setAttribute("style", "display:none"); - } else { - node->setAttribute("style", NULL); - } - } - Inkscape::URI SVGElem_uri2(((Glib::ustring)"#" + (Glib::ustring)"infoline-on-start-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri2); - if (elemref = SVGElemRef->getObject()) { - node = elemref->getRepr(); - if (!this->isVisible()) { - node->setAttribute("style", "display:none"); - } else { - node->setAttribute("style", NULL); - } - } - Inkscape::URI SVGElem_uri3(((Glib::ustring)"#" + (Glib::ustring)"infoline-on-end-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri3); - if (elemref = SVGElemRef->getObject()) { - node = elemref->getRepr(); - if (!this->isVisible()) { - node->setAttribute("style", "display:none"); - } else { - node->setAttribute("style", NULL); - } - } - Inkscape::URI SVGElem_uri4(((Glib::ustring)"#" + (Glib::ustring)"infoline-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri4); - if (elemref = SVGElemRef->getObject()) { - node = elemref->getRepr(); - if (!this->isVisible()) { - node->setAttribute("style", "display:none"); - } else { - node->setAttribute("style", NULL); + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPObject *elemref = NULL; + Inkscape::XML::Node *arrow = NULL; + if (elemref = document->getObjectById(mode)) { + Inkscape::XML::Node *arrow= elemref->getRepr(); + if (arrow) { + arrow->setAttribute("sodipodi:insensitive", "true"); + arrow->setAttribute("transform", NULL); + Inkscape::XML::Node *arrow_data = arrow->firstChild(); + if (arrow_data) { + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property (css, "fill","#000000"); + sp_repr_css_set_property (css, "stroke","none"); + arrow_data->setAttribute("transform", NULL); + sp_repr_css_attr_add_from_string(css, arrows_format.param_getSVGValue()); + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + arrow_data->setAttribute("style", css_str.c_str()); } } - Inkscape::URI SVGElem_uri5(((Glib::ustring)"#" + (Glib::ustring)"downline-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri5); - if (elemref = SVGElemRef->getObject()) { - node = elemref->getRepr(); - if (!this->isVisible()) { - node->setAttribute("style", "display:none"); - } else { - node->setAttribute("style", NULL); - } + } else { + arrow = xml_doc->createElement("svg:marker"); + arrow->setAttribute("id", mode); + arrow->setAttribute("inkscape:stockid", mode); + arrow->setAttribute("orient", "auto"); + arrow->setAttribute("refX", "0.0"); + arrow->setAttribute("refY", "0.0"); + arrow->setAttribute("style", "overflow:visible"); + arrow->setAttribute("sodipodi:insensitive", "true"); + /* Create <path> */ + Inkscape::XML::Node *arrow_path = xml_doc->createElement("svg:path"); + if (std::strcmp(mode, "ArrowDIN-start") == 0) { + arrow_path->setAttribute("d", "M -8,0 8,-2.11 8,2.11 z"); + } else if (std::strcmp(mode, "ArrowDIN-end") == 0) { + arrow_path->setAttribute("d", "M 8,0 -8,2.11 -8,-2.11 z"); + } else if (std::strcmp(mode, "ArrowDINout-start") == 0) { + arrow_path->setAttribute("d", "M 0,0 -16,2.11 -16,0.5 -26,0.5 -26,-0.5 -16,-0.5 -16,-2.11 z"); + } else { + arrow_path->setAttribute("d", "M 0,0 16,2.11 16,0.5 26,0.5 26,-0.5 16,-0.5 16,-2.11 z"); } + + arrow_path->setAttribute("id", Glib::ustring(mode).append("_path").c_str()); + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property (css, "fill","#000000"); + sp_repr_css_set_property (css, "stroke","none"); + sp_repr_css_attr_add_from_string(css, arrows_format.param_getSVGValue()); + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + arrow_path->setAttribute("style", css_str.c_str()); + arrow->addChild(arrow_path, NULL); + Inkscape::GC::release(arrow_path); + elemref = SP_OBJECT(document->getDefs()->appendChildRepr(arrow)); + Inkscape::GC::release(arrow); } + elements.push_back(mode); } void -LPEMeasureLine::createArrowMarker(Glib::ustring mode) +LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid) { - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); - Inkscape::URI SVGElem_uri(((Glib::ustring)"#" + mode).c_str()); - Inkscape::URIReference* SVGElemRef = new Inkscape::URIReference(desktop->doc()); - SVGElemRef->attach(SVGElem_uri); - SPObject *elemref = NULL; - Inkscape::XML::Node *arrow = NULL; - if (!(elemref = SVGElemRef->getObject())) { - arrow = xml_doc->createElement("svg:marker"); - arrow->setAttribute("id", mode.c_str()); - arrow->setAttribute("inkscape:stockid", mode.c_str()); - arrow->setAttribute("orient", "auto"); - arrow->setAttribute("refX", "0.0"); - arrow->setAttribute("refY", "0.0"); - arrow->setAttribute("style", "overflow:visible"); - arrow->setAttribute("sodipodi:insensitive", "true"); - /* Create <path> */ - Inkscape::XML::Node *arrow_path = xml_doc->createElement("svg:path"); - if (mode == (Glib::ustring)"ArrowDIN-start") { - arrow_path->setAttribute("d", "M -8,0 8,-2.11 8,2.11 z"); - } else if (mode == (Glib::ustring)"ArrowDIN-end") { - arrow_path->setAttribute("d", "M 8,0 -8,2.11 -8,-2.11 z"); - } else if (mode == (Glib::ustring)"ArrowDINout-start") { - arrow_path->setAttribute("d", "M 0,0 -16,2.11 -16,0.5 -26,0.5 -26,-0.5 -16,-0.5 -16,-2.11 z"); - } else { - arrow_path->setAttribute("d", "M 0,0 16,2.11 16,0.5 26,0.5 26,-0.5 16,-0.5 16,-2.11 z"); - } - - arrow_path->setAttribute("id", (mode + (Glib::ustring)"_path").c_str()); - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_set_property (css, "fill","#000000"); - sp_repr_css_set_property (css, "stroke","none" ); - sp_repr_css_attr_add_from_string(css, arrows_format.param_getSVGValue()); - Glib::ustring css_str; - sp_repr_css_write_string(css,css_str); - arrow_path->setAttribute("style", css_str.c_str()); - arrow->addChild(arrow_path, NULL); - Inkscape::GC::release(arrow_path); - elemref = SP_OBJECT(desktop->getDocument()->getDefs()->appendChildRepr(arrow)); - Inkscape::GC::release(arrow); - } else { - Inkscape::XML::Node *arrow= elemref->getRepr(); - if (arrow) { - arrow->setAttribute("sodipodi:insensitive", "true"); - Inkscape::XML::Node *arrow_data = arrow->firstChild(); - if (arrow_data) { - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_set_property (css, "fill","#000000"); - sp_repr_css_set_property (css, "stroke","none" ); - sp_repr_css_attr_add_from_string(css, arrows_format.param_getSVGValue()); - Glib::ustring css_str; - sp_repr_css_write_string(css,css_str); - arrow_data->setAttribute("style", css_str.c_str()); - } - } - } + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Inkscape::XML::Node *rtext = NULL; + double doc_w = document->getRoot()->width.value; + Geom::Scale scale = document->getDocumentScale(); + SPNamedView *nv = sp_document_namedview(document, NULL); + Glib::ustring display_unit = nv->display_units->abbr; + if (display_unit.empty()) { + display_unit = "px"; + } + //only check constrain viewbox on X + doc_scale = Inkscape::Util::Quantity::convert( scale[Geom::X], "px", nv->display_units ); + if( doc_scale > 0 ) { + doc_scale= 1.0/doc_scale; + } else { + doc_scale = 1.0; + } + const char * id = g_strdup(Glib::ustring("text-on-").append(this->getRepr()->attribute("id")).c_str()); + SPObject *elemref = NULL; + Inkscape::XML::Node *rtspan = NULL; + if (elemref = document->getObjectById(id)) { + if (remove) { + elemref->deleteObject(); + return; + } + pos = pos - Point::polar(angle, text_right_left); + rtext = elemref->getRepr(); + sp_repr_set_svg_double(rtext, "x", pos[Geom::X]); + sp_repr_set_svg_double(rtext, "y", pos[Geom::Y]); + rtext->setAttribute("sodipodi:insensitive", "true"); + rtext->setAttribute("transform", NULL); + } else { + if (remove) { + return; + } + rtext = xml_doc->createElement("svg:text"); + rtext->setAttribute("xml:space", "preserve"); + rtext->setAttribute("id", id); + rtext->setAttribute("sodipodi:insensitive", "true"); + pos = pos - Point::polar(angle, text_right_left); + sp_repr_set_svg_double(rtext, "x", pos[Geom::X]); + sp_repr_set_svg_double(rtext, "y", pos[Geom::Y]); + rtspan = xml_doc->createElement("svg:tspan"); + rtspan->setAttribute("sodipodi:role", "line"); + } + const char * transform; + Geom::Affine affine = Geom::Affine(Geom::Translate(pos).inverse()); + angle = std::fmod(angle, 2*M_PI); + if (angle < 0) angle += 2*M_PI; + if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) { + angle = std::fmod(angle + rad_from_deg(180), 2*M_PI); + if (angle < 0) angle += 2*M_PI; + } + affine *= Geom::Rotate(angle); + affine *= Geom::Translate(pos); + if (rotate_anotation) { + transform = sp_svg_transform_write(affine); + } else { + transform = NULL; + } + rtext->setAttribute("transform", transform); + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, anotation_format.param_getSVGValue()); + Inkscape::FontLister *fontlister = Inkscape::FontLister::get_instance(); + fontlister->fill_css(css, Glib::ustring(fontbutton.param_getSVGValue())); + std::stringstream font_size; + font_size.imbue(std::locale::classic()); + font_size << fontsize << "pt"; + sp_repr_css_set_property (css, "font-size",font_size.str().c_str()); + sp_repr_css_set_property (css, "line-height","125%"); + sp_repr_css_set_property (css, "letter-spacing","0"); + sp_repr_css_set_property (css, "word-spacing", "0"); + sp_repr_css_set_property (css, "text-align", "center"); + sp_repr_css_set_property (css, "text-anchor", "middle"); + sp_repr_css_set_property (css, "fill", "#000000"); + sp_repr_css_set_property (css, "fill-opacity", "1"); + sp_repr_css_set_property (css, "stroke", "none"); + sp_repr_css_attr_add_from_string(css, anotation_format.param_getSVGValue()); + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + if (!rtspan) { + rtspan = rtext->firstChild(); + } + rtext->setAttribute("style", css_str.c_str()); + rtspan->setAttribute("style", NULL); + rtspan->setAttribute("transform", NULL); + sp_repr_css_attr_unref (css); + if (!elemref) { + rtext->addChild(rtspan, NULL); + Inkscape::GC::release(rtspan); + } + length = Inkscape::Util::Quantity::convert(length / doc_scale, display_unit.c_str(), unit.get_abbreviation()); + std::stringstream length_str; + length_str.precision(precision); + length_str.setf(std::ios::fixed, std::ios::floatfield); + if (local_locale) { + length_str.imbue(std::locale("")); + } else { + length_str.imbue(std::locale::classic()); + } + length_str << std::fixed << length; + Glib::ustring label_value = Glib::ustring(format.param_getSVGValue()); + size_t s = label_value.find(Glib::ustring("{measure}"),0); + if(s < label_value.length()) { + label_value.replace(s,s+9,length_str.str()); + } + s = label_value.find(Glib::ustring("{unit}"),0); + if(s < label_value.length()) { + label_value.replace(s,s+6,unit.get_abbreviation()); + } + if ( !valid ) { + label_value = Glib::ustring(_("Non Uniform Scale")); + } + Inkscape::XML::Node *rstring = NULL; + if (!elemref) { + rstring = xml_doc->createTextNode(label_value.c_str()); + rtspan->addChild(rstring, NULL); + Inkscape::GC::release(rstring); + } else { + rstring = rtspan->firstChild(); + rstring->setContent(label_value.c_str()); + } + if (!elemref) { + elemref = sp_lpe_item->parent->appendChildRepr(rtext); + Inkscape::GC::release(rtext); + } else if (elemref->parent != sp_lpe_item->parent) { + Inkscape::XML::Node *old_repr = elemref->getRepr(); + Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); + SPObject * elemref_copy = sp_lpe_item->parent->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + copy->setAttribute("id", id); + elemref = elemref_copy; + } + elements.push_back(id); + Geom::OptRect bounds = SP_ITEM(elemref)->bounds(SPItem::GEOMETRIC_BBOX); + if (bounds) { + anotation_width = bounds->width() * 1.4; } } void -LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove) +LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows) { - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); - Inkscape::XML::Node *rtext = NULL; - double doc_w = desktop->getDocument()->getRoot()->width.value; - Geom::Scale scale = desktop->getDocument()->getDocumentScale(); - SPNamedView *nv = desktop->getNamedView(); - Glib::ustring display_unit = nv->display_units->abbr; - if (display_unit.empty()) { - display_unit = "px"; - } - //only check constrain viewbox on X - doc_scale = Inkscape::Util::Quantity::convert( scale[Geom::X], "px", nv->display_units ); - if( doc_scale > 0 ) { - doc_scale= 1.0/doc_scale; - } else { - doc_scale = 1.0; - } - Inkscape::URI SVGElem_uri(((Glib::ustring)"#" + (Glib::ustring)"text-on-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - Inkscape::URIReference* SVGElemRef = new Inkscape::URIReference(desktop->doc()); - SVGElemRef->attach(SVGElem_uri); - SPObject *elemref = NULL; - Inkscape::XML::Node *rtspan = NULL; - - if (elemref = SVGElemRef->getObject()) { - if (remove) { - elemref->deleteObject(); - return; - } - pos = pos - Point::polar(angle, text_right_left); - rtext = elemref->getRepr(); - sp_repr_set_svg_double(rtext, "x", pos[Geom::X]); - sp_repr_set_svg_double(rtext, "y", pos[Geom::Y]); - rtext->setAttribute("sodipodi:insensitive", "true"); - } else { - if (remove) { - return; - } - rtext = xml_doc->createElement("svg:text"); - rtext->setAttribute("xml:space", "preserve"); - rtext->setAttribute("id", ( (Glib::ustring)"text-on-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - rtext->setAttribute("sodipodi:insensitive", "true"); - pos = pos - Point::polar(angle, text_right_left); - sp_repr_set_svg_double(rtext, "x", pos[Geom::X]); - sp_repr_set_svg_double(rtext, "y", pos[Geom::Y]); - rtspan = xml_doc->createElement("svg:tspan"); - rtspan->setAttribute("sodipodi:role", "line"); - } - gchar * transform; - Geom::Affine affine = Geom::Affine(Geom::Translate(pos).inverse()); - angle = std::fmod(angle, 2*M_PI); - if (angle < 0) angle += 2*M_PI; - if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) { - angle = std::fmod(angle + rad_from_deg(180), 2*M_PI); - if (angle < 0) angle += 2*M_PI; - } - affine *= Geom::Rotate(angle); - affine *= Geom::Translate(pos); - if (rotate_anotation) { - transform = sp_svg_transform_write(affine); - } else { - transform = NULL; - } - rtext->setAttribute("transform", transform); - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_attr_add_from_string(css, anotation_format.param_getSVGValue()); - Inkscape::FontLister *fontlister = Inkscape::FontLister::get_instance(); - fontlister->fill_css( css, (Glib::ustring)fontbutton.param_getSVGValue() ); - std::stringstream font_size; - font_size.imbue(std::locale::classic()); - font_size << fontsize << "pt"; - - sp_repr_css_set_property (css, "font-size",font_size.str().c_str()); - sp_repr_css_set_property (css, "line-height","125%"); - sp_repr_css_set_property (css, "letter-spacing","0"); - sp_repr_css_set_property (css, "word-spacing", "0"); - sp_repr_css_set_property (css, "text-align", "center"); - sp_repr_css_set_property (css, "text-anchor", "middle"); - sp_repr_css_set_property (css, "fill", "#000000"); - sp_repr_css_set_property (css, "fill-opacity", "1"); - sp_repr_css_set_property (css, "stroke", "none"); - sp_repr_css_attr_add_from_string(css, anotation_format.param_getSVGValue()); - Glib::ustring css_str; - sp_repr_css_write_string(css,css_str); - if (!rtspan) { - rtspan = rtext->firstChild(); - } - rtext->setAttribute("style", css_str.c_str()); - rtspan->setAttribute("style", NULL); - sp_repr_css_attr_unref (css); - if (!elemref) { - rtext->addChild(rtspan, NULL); - Inkscape::GC::release(rtspan); - } - length = Inkscape::Util::Quantity::convert(length / doc_scale, display_unit.c_str(), unit.get_abbreviation()); - std::stringstream length_str; - length_str.precision(precision); - length_str.setf(std::ios::fixed, std::ios::floatfield); - if (local_locale) { - length_str.imbue(std::locale("")); + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPObject *elemref = NULL; + Inkscape::XML::Node *line = NULL; + if (!main) { + Geom::Ray ray(start, end); + Geom::Coord angle = ray.angle(); + start = start + Point::polar(angle, helpline_distance ); + end = end + Point::polar(angle, helpline_overlap ); + } + Geom::PathVector line_pathv; + if (main && std::abs(text_top_bottom) < fontsize/1.5 && hide_back && !overflow){ + Geom::Path line_path; + double k = 0; + if (flip_side) { + k = (Geom::distance(start,end)/2.0) + arrow_gap - (anotation_width/2.0); } else { - length_str.imbue(std::locale::classic()); - } - length_str << std::fixed << length; - Glib::ustring label_value = Glib::ustring(format.param_getSVGValue()); - size_t s = label_value.find((Glib::ustring)"{measure}",0); - if(s < label_value.length()) { - label_value.replace(s,s+9,length_str.str()); + k = (Geom::distance(start,end)/2.0) - arrow_gap - (anotation_width/2.0); + } + if (Geom::distance(start,end) < anotation_width){ + return; + } + Geom::Ray ray(end, start); + Geom::Coord angle = ray.angle(); + line_path.start(start); + line_path.appendNew<Geom::LineSegment>(start - Point::polar(angle, k)); + line_pathv.push_back(line_path); + line_path.clear(); + line_path.start(end + Point::polar(angle, k)); + line_path.appendNew<Geom::LineSegment>(end); + line_pathv.push_back(line_path); + } else { + Geom::Path line_path; + line_path.start(start); + line_path.appendNew<Geom::LineSegment>(end); + line_pathv.push_back(line_path); + } + if (elemref = document->getObjectById(id)) { + if (remove) { + elemref->deleteObject(); + return; } - s = label_value.find((Glib::ustring)"{unit}",0); - if(s < label_value.length()) { - label_value.replace(s,s+6,unit.get_abbreviation()); + line = elemref->getRepr(); + + const char * line_str = sp_svg_write_path( line_pathv ); + line->setAttribute("d" , line_str); + line->setAttribute("transform", NULL); + } else { + if (remove) { + return; } - Inkscape::XML::Node *rstring = NULL; - if (!elemref) { - rstring = xml_doc->createTextNode(label_value.c_str()); - rtspan->addChild(rstring, NULL); - Inkscape::GC::release(rstring); + line = xml_doc->createElement("svg:path"); + line->setAttribute("id", id); + const char * line_str = sp_svg_write_path( line_pathv ); + line->setAttribute("d" , line_str); + } + line->setAttribute("sodipodi:insensitive", "true"); + line_pathv.clear(); + + Glib::ustring style = Glib::ustring("stroke:#000000;fill:none;"); + if (overflow && !arrows) { + line->setAttribute("inkscape:label", "downline"); + } else if (main) { + line->setAttribute("inkscape:label", "dinline"); + if (arrows_outside) { + style = style + Glib::ustring("marker-start:url(#ArrowDINout-start);marker-end:url(#ArrowDINout-end);"); } else { - rstring = rtspan->firstChild(); - rstring->setContent(label_value.c_str()); + style = style + Glib::ustring("marker-start:url(#ArrowDIN-start);marker-end:url(#ArrowDIN-end);"); } - if (!elemref) { - elemref = SP_OBJECT(desktop->currentLayer()->appendChildRepr(rtext)); - Inkscape::GC::release(rtext); - } - Inkscape::XML::Node *tmp_node = rtext->duplicate(xml_doc); - affine = Geom::Affine(Geom::Scale(1.4)); - tmp_node->setAttribute("transform", sp_svg_transform_write(affine)); - SPObject * tmp_obj = SP_OBJECT(desktop->currentLayer()->appendChildRepr(tmp_node)); - Inkscape::GC::release(tmp_node); - tmp_obj->updateRepr(); - Geom::OptRect bounds = SP_ITEM(tmp_obj)->bounds(SPItem::GEOMETRIC_BBOX); - if (bounds) { - anotation_width = bounds->width(); - } - tmp_obj->deleteObject(); + } else { + line->setAttribute("inkscape:label", "dinhelpline"); + } + std::stringstream stroke_w; + stroke_w.imbue(std::locale::classic()); + if (line_group_05) { + double stroke_width = Inkscape::Util::Quantity::convert(0.25 / doc_scale, "mm", display_unit.c_str()); + stroke_w << stroke_width; + style = style + Glib::ustring("stroke-width:" + stroke_w.str()); + } else { + double stroke_width = Inkscape::Util::Quantity::convert(0.35 / doc_scale, "mm", display_unit.c_str()); + stroke_w << stroke_width; + style = style + Glib::ustring("stroke-width:" + stroke_w.str()); + } + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, style.c_str()); + if (main) { + sp_repr_css_attr_add_from_string(css, dimline_format.param_getSVGValue()); + } else { + sp_repr_css_attr_add_from_string(css, helperlines_format.param_getSVGValue()); + } + Glib::ustring css_str; + sp_repr_css_write_string(css,css_str); + line->setAttribute("style", css_str.c_str()); + if (!elemref) { + elemref = sp_lpe_item->parent->appendChildRepr(line); + Inkscape::GC::release(line); + } else if (elemref->parent != sp_lpe_item->parent) { + Inkscape::XML::Node *old_repr = elemref->getRepr(); + Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc); + SPObject * elemref_copy = sp_lpe_item->parent->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); + copy->setAttribute("id", id); } + elements.push_back(id); } void -LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, Glib::ustring id, bool main, bool overflow, bool remove, bool arrows) +LPEMeasureLine::doOnApply(SPLPEItem const* lpeitem) { - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); - Inkscape::URI SVGElem_uri(((Glib::ustring)"#" + id).c_str()); - Inkscape::URIReference* SVGElemRef = new Inkscape::URIReference(desktop->doc()); - SVGElemRef->attach(SVGElem_uri); - SPObject *elemref = NULL; - Inkscape::XML::Node *line = NULL; - if (!main) { - Geom::Ray ray(start, end); - Geom::Coord angle = ray.angle(); - start = start + Point::polar(angle, helpline_distance ); - end = end + Point::polar(angle, helpline_overlap ); - } - Geom::PathVector line_pathv; - if (main && std::abs(text_top_bottom) < fontsize/1.5 && hide_back && !overflow){ - Geom::Path line_path; - double k = 0; - if (flip_side) { - k = (Geom::distance(start,end)/2.0) + arrow_gap - (anotation_width/2.0); - } else { - k = (Geom::distance(start,end)/2.0) - arrow_gap - (anotation_width/2.0); - } - if (Geom::distance(start,end) < anotation_width){ - return; - } - Geom::Ray ray(end, start); - Geom::Coord angle = ray.angle(); - line_path.start(start); - line_path.appendNew<Geom::LineSegment>(start - Point::polar(angle, k)); - line_pathv.push_back(line_path); - line_path.clear(); - line_path.start(end + Point::polar(angle, k)); - line_path.appendNew<Geom::LineSegment>(end); - line_pathv.push_back(line_path); - } else { - Geom::Path line_path; - line_path.start(start); - line_path.appendNew<Geom::LineSegment>(end); - line_pathv.push_back(line_path); - } - - if (elemref = SVGElemRef->getObject()) { - if (remove) { - elemref->deleteObject(); - return; - } - line = elemref->getRepr(); - - gchar * line_str = sp_svg_write_path( line_pathv ); - line->setAttribute("d" , line_str); - } else { - if (remove) { - return; - } - line = xml_doc->createElement("svg:path"); - line->setAttribute("id", id.c_str()); - gchar * line_str = sp_svg_write_path( line_pathv ); - line->setAttribute("d" , line_str); - } - line->setAttribute("sodipodi:insensitive", "true"); - line_pathv.clear(); - - Glib::ustring style = (Glib::ustring)"stroke:#000000;fill:none;"; - if (overflow && !arrows) { - line->setAttribute("inkscape:label", "downline"); - } else if (main) { - line->setAttribute("inkscape:label", "dinline"); - if (arrows_outside) { - style = style + (Glib::ustring)"marker-start:url(#ArrowDINout-start);marker-end:url(#ArrowDINout-end);"; - } else { - style = style + (Glib::ustring)"marker-start:url(#ArrowDIN-start);marker-end:url(#ArrowDIN-end);"; - } - } else { - line->setAttribute("inkscape:label", "dinhelpline"); - } - std::stringstream stroke_w; - stroke_w.imbue(std::locale::classic()); - if (line_group_05) { - double stroke_width = Inkscape::Util::Quantity::convert(0.25 / doc_scale, "mm", display_unit.c_str()); - stroke_w << stroke_width; - style = style + (Glib::ustring)"stroke-width:" + (Glib::ustring)stroke_w.str(); - } else { - double stroke_width = Inkscape::Util::Quantity::convert(0.35 / doc_scale, "mm", display_unit.c_str()); - stroke_w << stroke_width; - style = style + (Glib::ustring)"stroke-width:" + (Glib::ustring)stroke_w.str(); - } - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_attr_add_from_string(css, style.c_str()); - if (main) { - sp_repr_css_attr_add_from_string(css, dimline_format.param_getSVGValue()); - } else { - sp_repr_css_attr_add_from_string(css, helperlines_format.param_getSVGValue()); - } - Glib::ustring css_str; - sp_repr_css_write_string(css,css_str); - line->setAttribute("style", css_str.c_str()); - if (!elemref) { - elemref = SP_OBJECT(desktop->currentLayer()->appendChildRepr(line)); - Inkscape::GC::release(line); - } + if (!SP_IS_SHAPE(lpeitem)) { + g_warning("LPE measure line can only be applied to shapes (not groups)."); + SPLPEItem * item = const_cast<SPLPEItem*>(lpeitem); + item->removeCurrentPathEffect(false); } + id_origin.param_setValue(Glib::ustring(lpeitem->getId())); + id_origin.write_to_SVG(); } void LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem) { SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem); + sp_lpe_item->parent = dynamic_cast<SPObject *>(splpeitem->parent); + SPDocument * document = SP_ACTIVE_DOCUMENT; + Inkscape::XML::Node *root = splpeitem->document->getReprRoot(); + Inkscape::XML::Node *root_origin = document->getReprRoot(); + if (root_origin != root) { + return; + } SPPath *sp_path = dynamic_cast<SPPath *>(splpeitem); if (sp_path) { - SPDocument * doc = SP_ACTIVE_DOCUMENT; - Geom::Affine affinetransform = i2anc_affine(SP_OBJECT(lpeitem)->parent, SP_OBJECT(doc->getRoot())); - double parents_scale = (affinetransform.inverse().expansionX() + affinetransform.inverse().expansionY()) / 2.0; - + Geom::Affine affinetransform = i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(document->getRoot())); Geom::PathVector pathvector = sp_path->get_original_curve()->get_pathvector(); - pathvector *= affinetransform; - if (arrows_outside) { - createArrowMarker((Glib::ustring)"ArrowDINout-start"); - createArrowMarker((Glib::ustring)"ArrowDINout-end"); - } else { - createArrowMarker((Glib::ustring)"ArrowDIN-start"); - createArrowMarker((Glib::ustring)"ArrowDIN-end"); + Geom::Affine writed_transform = Geom::identity(); + sp_svg_transform_read(splpeitem->getAttribute("transform"), &writed_transform ); + pathvector *= writed_transform; + if ((Glib::ustring(format.param_getSVGValue()).empty())) { + format.param_setValue(Glib::ustring("{measure}{unit}")); + this->upd_params = true; + } + size_t ncurves = pathvector.curveCount(); + if (ncurves != (size_t)curve_linked.param_get_max()) { + curve_linked.param_set_range(0, ncurves); } - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - if (((Glib::ustring)format.param_getSVGValue()).empty()) { - format.param_setValue((Glib::ustring)"{measure}{unit}"); - this->upd_params = true; + Geom::Point start = pathvector.initialPoint(); + Geom::Point end = pathvector.finalPoint(); + if (curve_linked) { //!0 + start = pathvector.pointAt(curve_linked -1); + end = pathvector.pointAt(curve_linked); + } + if (Geom::are_near(start, start_stored, 0.01) && + Geom::are_near(end, end_stored, 0.01) && + sp_lpe_item->getCurrentLPE() != this){ + return; + } + elements.clear(); + start_stored = start; + end_stored = end; + Geom::Point hstart = start; + Geom::Point hend = end; + bool remove = false; + if (Geom::are_near(hstart, hend)) { + remove = true; + } + if (orientation == OM_VERTICAL) { + Coord xpos = std::max(hstart[Geom::X],hend[Geom::X]); + if (flip_side) { + xpos = std::min(hstart[Geom::X],hend[Geom::X]); } - size_t ncurves = pathvector.curveCount(); - curve_linked.param_set_range(0, ncurves); - Geom::Point start = pathvector.initialPoint(); - Geom::Point end = pathvector.finalPoint(); - if (curve_linked) { //!0 - start = pathvector.pointAt(curve_linked -1); - end = pathvector.pointAt(curve_linked); + hstart[Geom::X] = xpos; + hend[Geom::X] = xpos; + if (hstart[Geom::Y] > hend[Geom::Y]) { + swap(hstart,hend); + swap(start,end); } - Geom::Point hstart = start; - Geom::Point hend = end; - bool remove = false; - if (Geom::are_near(hstart, hend)) { + if (Geom::are_near(hstart[Geom::Y], hend[Geom::Y])) { remove = true; } - if (orientation == OM_VERTICAL) { - Coord xpos = std::max(hstart[Geom::X],hend[Geom::X]); - if (flip_side) { - xpos = std::min(hstart[Geom::X],hend[Geom::X]); - } - hstart[Geom::X] = xpos; - hend[Geom::X] = xpos; - if (hstart[Geom::Y] > hend[Geom::Y]) { - swap(hstart,hend); - swap(start,end); - } - if (Geom::are_near(hstart[Geom::Y], hend[Geom::Y])) { - remove = true; - } + } + if (orientation == OM_HORIZONTAL) { + Coord ypos = std::max(hstart[Geom::Y],hend[Geom::Y]); + if (flip_side) { + ypos = std::min(hstart[Geom::Y],hend[Geom::Y]); } - if (orientation == OM_HORIZONTAL) { - Coord ypos = std::max(hstart[Geom::Y],hend[Geom::Y]); - if (flip_side) { - ypos = std::min(hstart[Geom::Y],hend[Geom::Y]); - } - hstart[Geom::Y] = ypos; - hend[Geom::Y] = ypos; - if (hstart[Geom::X] < hend[Geom::X]) { - swap(hstart,hend); - swap(start,end); - } - if (Geom::are_near(hstart[Geom::X], hend[Geom::X])) { - remove = true; - } + hstart[Geom::Y] = ypos; + hend[Geom::Y] = ypos; + if (hstart[Geom::X] < hend[Geom::X]) { + swap(hstart,hend); + swap(start,end); } - double length = Geom::distance(hstart,hend) * scale; - Geom::Point pos = Geom::middle_point(hstart,hend); - Geom::Ray ray(hstart,hend); - Geom::Coord angle = ray.angle(); - if (flip_side) { - angle = std::fmod(angle + rad_from_deg(180), 2*M_PI); - if (angle < 0) angle += 2*M_PI; + if (Geom::are_near(hstart[Geom::X], hend[Geom::X])) { + remove = true; } - //We get the font size to offset the text to the middle - Pango::FontDescription fontdesc((Glib::ustring)fontbutton.param_getSVGValue()); - fontsize = fontdesc.get_size()/(double)Pango::SCALE; - fontsize *= desktop->doc()->getRoot()->c2p.inverse().expansionX(); - Geom::Coord angle_cross = std::fmod(angle + rad_from_deg(90), 2*M_PI); - if (angle_cross < 0) angle_cross += 2*M_PI; - angle = std::fmod(angle, 2*M_PI); + } + double length = Geom::distance(hstart,hend) * scale; + Geom::Point pos = Geom::middle_point(hstart,hend); + Geom::Ray ray(hstart,hend); + Geom::Coord angle = ray.angle(); + if (flip_side) { + angle = std::fmod(angle + rad_from_deg(180), 2*M_PI); if (angle < 0) angle += 2*M_PI; - if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) { - pos = pos - Point::polar(angle_cross, (position - text_top_bottom) + fontsize/2.5); - } else { - pos = pos - Point::polar(angle_cross, (position + text_top_bottom) - fontsize/2.5); - } - if (scale_insensitive) { - length *= parents_scale; + } + if (arrows_outside) { + createArrowMarker("ArrowDINout-start"); + createArrowMarker("ArrowDINout-end"); + } else { + createArrowMarker("ArrowDIN-start"); + createArrowMarker("ArrowDIN-end"); + } + //We get the font size to offset the text to the middle + Pango::FontDescription fontdesc(Glib::ustring(fontbutton.param_getSVGValue())); + fontsize = fontdesc.get_size()/(double)Pango::SCALE; + fontsize *= document->getRoot()->c2p.inverse().expansionX(); + Geom::Coord angle_cross = std::fmod(angle + rad_from_deg(90), 2*M_PI); + if (angle_cross < 0) angle_cross += 2*M_PI; + angle = std::fmod(angle, 2*M_PI); + if (angle < 0) angle += 2*M_PI; + if (angle >= rad_from_deg(90) && angle < rad_from_deg(270)) { + pos = pos - Point::polar(angle_cross, (position - text_top_bottom) + fontsize/2.5); + } else { + pos = pos - Point::polar(angle_cross, (position + text_top_bottom) - fontsize/2.5); + } + double parents_scale = (affinetransform.expansionX() + affinetransform.expansionY()) / 2.0; + if (scale_sensitive) { + length *= parents_scale; + } + if (scale_sensitive && !affinetransform.preservesAngles()) { + createTextLabel(pos, length, angle, remove, false); + } else { + createTextLabel(pos, length, angle, remove, true); + } + bool overflow = false; + const char * downline = g_strdup(Glib::ustring("downline-").append(this->getRepr()->attribute("id")).c_str()); + if ((anotation_width/2) + std::abs(text_right_left) > Geom::distance(start,end)/2.0) { + Geom::Point sstart = end - Point::polar(angle_cross, position); + Geom::Point send = end - Point::polar(angle_cross, position); + if (text_right_left < 0 && flip_side || text_right_left > 0 && !flip_side) { + sstart = start - Point::polar(angle_cross, position); + send = start - Point::polar(angle_cross, position); } - createTextLabel(pos, length, angle, remove); - bool overflow = false; - if ((anotation_width/2) + std::abs(text_right_left) > Geom::distance(start,end)/2.0) { - Geom::Point sstart = end - Point::polar(angle_cross, position); - Geom::Point send = end - Point::polar(angle_cross, position); - if (text_right_left < 0 && flip_side || text_right_left > 0 && !flip_side) { - sstart = start - Point::polar(angle_cross, position); - send = start - Point::polar(angle_cross, position); - } - Geom::Point prog_end = Geom::Point(); - if (std::abs(text_top_bottom) < fontsize/1.5 && hide_back) { - if (text_right_left > 0 ) { - prog_end = sstart - Point::polar(angle, std::abs(text_right_left) - (anotation_width/1.9) - (Geom::distance(start,end)/2.0)); - } else { - prog_end = sstart + Point::polar(angle, std::abs(text_right_left) - (anotation_width/1.9) - (Geom::distance(start,end)/2.0)); - } + Geom::Point prog_end = Geom::Point(); + if (std::abs(text_top_bottom) < fontsize/1.5 && hide_back) { + if (text_right_left > 0 ) { + prog_end = sstart - Point::polar(angle, std::abs(text_right_left) - (anotation_width/1.9) - (Geom::distance(start,end)/2.0)); } else { - if (text_right_left > 0 ) { - prog_end = sstart - Point::polar(angle,(anotation_width/2) + std::abs(text_right_left) - (Geom::distance(start,end)/2.0)); - } else { - prog_end = sstart + Point::polar(angle,(anotation_width/2) + std::abs(text_right_left) - (Geom::distance(start,end)/2.0)); - } + prog_end = sstart + Point::polar(angle, std::abs(text_right_left) - (anotation_width/1.9) - (Geom::distance(start,end)/2.0)); } - overflow = true; - createLine(sstart, prog_end, (Glib::ustring)"downline-" + (Glib::ustring)this->getRepr()->attribute("id"), true, overflow, false, false); } else { - //erase it - createLine(Geom::Point(),Geom::Point(), (Glib::ustring)"downline-" + (Glib::ustring)this->getRepr()->attribute("id"), true, overflow, true, false); - } - //LINE - arrow_gap = 8 * Inkscape::Util::Quantity::convert(0.35 / doc_scale, "mm", display_unit.c_str()); - if (line_group_05) { - arrow_gap = 8 * Inkscape::Util::Quantity::convert(0.25 / doc_scale, "mm", display_unit.c_str()); - } - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_attr_add_from_string(css, dimline_format.param_getSVGValue()); - setlocale(LC_NUMERIC, std::locale::classic().name().c_str()); - double width_line = atof(sp_repr_css_property(css,"stroke-width","-1")); - setlocale(LC_NUMERIC, std::locale("").name().c_str()); - if (width_line > -0.0001) { - arrow_gap = 8 * Inkscape::Util::Quantity::convert(width_line/ doc_scale, "mm", display_unit.c_str()); - } - if (flip_side) { - arrow_gap *= -1; - } - hstart = hstart - Point::polar(angle_cross, position); - Glib::ustring id = (Glib::ustring)"infoline-on-start-" + (Glib::ustring)this->getRepr()->attribute("id"); - createLine(start, hstart, id, false, false, remove); - hend = hend - Point::polar(angle_cross, position); - id = (Glib::ustring)"infoline-on-end-" + (Glib::ustring)this->getRepr()->attribute("id"); - createLine(end, hend, id, false, false, remove); - if (!arrows_outside) { - hstart = hstart + Point::polar(angle, arrow_gap); - hend = hend - Point::polar(angle, arrow_gap ); + if (text_right_left > 0 ) { + prog_end = sstart - Point::polar(angle,(anotation_width/2) + std::abs(text_right_left) - (Geom::distance(start,end)/2.0)); + } else { + prog_end = sstart + Point::polar(angle,(anotation_width/2) + std::abs(text_right_left) - (Geom::distance(start,end)/2.0)); + } } - id = (Glib::ustring)"infoline-" + (Glib::ustring)this->getRepr()->attribute("id"); - createLine(hstart, hend, id, true, overflow, remove, true); + overflow = true; + createLine(sstart, prog_end, downline, true, overflow, false, false); + } else { + //erase it + createLine(Geom::Point(),Geom::Point(), downline, true, overflow, true, false); } + //LINE + arrow_gap = 8 * Inkscape::Util::Quantity::convert(0.35 / doc_scale, "mm", display_unit.c_str()); + if (line_group_05) { + arrow_gap = 8 * Inkscape::Util::Quantity::convert(0.25 / doc_scale, "mm", display_unit.c_str()); + } + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, dimline_format.param_getSVGValue()); + setlocale(LC_NUMERIC, std::locale::classic().name().c_str()); + double width_line = atof(sp_repr_css_property(css,"stroke-width","-1")); + setlocale(LC_NUMERIC, std::locale("").name().c_str()); + if (width_line > -0.0001) { + arrow_gap = 8 * Inkscape::Util::Quantity::convert(width_line/ doc_scale, "mm", display_unit.c_str()); + } + if (flip_side) { + arrow_gap *= -1; + } + hstart = hstart - Point::polar(angle_cross, position); + createLine(start, hstart, g_strdup(Glib::ustring("infoline-on-start-").append(this->getRepr()->attribute("id")).c_str()), false, false, remove); + hend = hend - Point::polar(angle_cross, position); + createLine(end, hend, g_strdup(Glib::ustring("infoline-on-end-").append(this->getRepr()->attribute("id")).c_str()), false, false, remove); + if (!arrows_outside) { + hstart = hstart + Point::polar(angle, arrow_gap); + hend = hend - Point::polar(angle, arrow_gap ); + } + createLine(hstart, hend, g_strdup(Glib::ustring("infoline-").append(this->getRepr()->attribute("id")).c_str()), true, overflow, remove, true); } } -void LPEMeasureLine::doOnRemove (SPLPEItem const* lpeitem) +void +LPEMeasureLine::doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/) { - if (!erase) return; - - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - Inkscape::URI SVGElem_uri(((Glib::ustring)"#" + (Glib::ustring)"text-on-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - Inkscape::URIReference* SVGElemRef = new Inkscape::URIReference(desktop->doc()); - SVGElemRef->attach(SVGElem_uri); - SPObject *elemref = NULL; - if (elemref = SVGElemRef->getObject()) { - elemref->deleteObject(); - } - Inkscape::URI SVGElem_uri2(((Glib::ustring)"#" + (Glib::ustring)"infoline-on-end-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri2); - if (elemref = SVGElemRef->getObject()) { - elemref->deleteObject(); - } - Inkscape::URI SVGElem_uri3(((Glib::ustring)"#" + (Glib::ustring)"infoline-on-start-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri3); - if (elemref = SVGElemRef->getObject()) { - elemref->deleteObject(); - } - Inkscape::URI SVGElem_uri4(((Glib::ustring)"#" + (Glib::ustring)"infoline-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri4); - if (elemref = SVGElemRef->getObject()) { - elemref->deleteObject(); - } - Inkscape::URI SVGElem_uri5(((Glib::ustring)"#" + (Glib::ustring)"downline-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri5); - if (elemref = SVGElemRef->getObject()) { - elemref->deleteObject(); - } + processObjects(LPE_VISIBILITY); +} + +void +LPEMeasureLine::doOnRemove (SPLPEItem const* /*lpeitem*/) +{ + //unset "erase_extra_objects" hook on sp-lpe-item.cpp + if (!erase_extra_objects) { + processObjects(LPE_TO_OBJECTS); + elements.clear(); + return; } + processObjects(LPE_ERASE); } -void LPEMeasureLine::toObjects() +void +LPEMeasureLine::processObjects(LpeAction lpe_action) { - if (SPDesktop *desktop = SP_ACTIVE_DESKTOP) { - - Inkscape::URI SVGElem_uri(((Glib::ustring)"#" + (Glib::ustring)"text-on-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - Inkscape::URIReference* SVGElemRef = new Inkscape::URIReference(desktop->doc()); - SVGElemRef->attach(SVGElem_uri); - SPObject *elemref = NULL; - if (elemref = SVGElemRef->getObject()) { - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); - } - Inkscape::URI SVGElem_uri2(((Glib::ustring)"#" + (Glib::ustring)"infoline-on-end-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri2); - if (elemref = SVGElemRef->getObject()) { - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); - } - Inkscape::URI SVGElem_uri3(((Glib::ustring)"#" + (Glib::ustring)"infoline-on-start-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri3); - if (elemref = SVGElemRef->getObject()) { - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); + SPDocument * document = SP_ACTIVE_DOCUMENT; + for (std::vector<const char *>::iterator el_it = elements.begin(); + el_it != elements.end(); ++el_it) { + const char * id = *el_it; + if (!id || strlen(id) == 0) { + return; } - Inkscape::URI SVGElem_uri4(((Glib::ustring)"#" + (Glib::ustring)"infoline-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri4); - if (elemref = SVGElemRef->getObject()) { - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); - } - Inkscape::URI SVGElem_uri5(((Glib::ustring)"#" + (Glib::ustring)"downline-" + (Glib::ustring)this->getRepr()->attribute("id")).c_str()); - SVGElemRef->attach(SVGElem_uri5); - if (elemref = SVGElemRef->getObject()) { - elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); - } - erase = false; - sp_lpe_item->removeCurrentPathEffect(true); - //TODO: find better way to refresh effect list - if (SP_IS_OBJECT(sp_lpe_item)){ - Inkscape::Selection *selection = SP_ACTIVE_DESKTOP->getSelection(); - selection->remove(SP_OBJECT(sp_lpe_item)); - selection->add(SP_OBJECT(sp_lpe_item)); + SPObject *elemref = NULL; + if (elemref = document->getObjectById(id)) { + SPCSSAttr *css; + Glib::ustring css_str; + switch (lpe_action){ + case LPE_TO_OBJECTS: + elemref->getRepr()->setAttribute("inkscape:path-effect", NULL); + elemref->getRepr()->setAttribute("sodipodi:insensitive", NULL); + break; + + case LPE_ERASE: + if (std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) { + elemref->deleteObject(); + } + break; + + case LPE_VISIBILITY: + css = sp_repr_css_attr_new(); + sp_repr_css_attr_add_from_string(css, elemref->getRepr()->attribute("style")); + if (!this->isVisible() && std::strcmp(elemref->getId(),id_origin.param_getSVGValue()) != 0) { + css->setAttribute("display", "none"); + } else { + css->setAttribute("display", NULL); + } + sp_repr_css_write_string(css,css_str); + elemref->getRepr()->setAttribute("style", css_str.c_str()); + break; + + default: + break; + } } } + if (lpe_action == LPE_ERASE) { + elements.clear(); + } } Gtk::Widget *LPEMeasureLine::newWidget() @@ -790,7 +746,6 @@ Gtk::Widget *LPEMeasureLine::newWidget() std::vector<Parameter *>::iterator it = param_vector.begin(); Gtk::HBox * button1 = Gtk::manage(new Gtk::HBox(true,0)); - Gtk::HBox * button2 = Gtk::manage(new Gtk::HBox(true,0)); Gtk::VBox * vbox_expander = Gtk::manage( new Gtk::VBox(Effect::newWidget()) ); vbox_expander->set_border_width(0); vbox_expander->set_spacing(2); @@ -822,16 +777,12 @@ Gtk::Widget *LPEMeasureLine::newWidget() Gtk::Button *save_default = Gtk::manage(new Gtk::Button(Glib::ustring(_("Save '*' as default")))); save_default->signal_clicked().connect(sigc::mem_fun(*this, &LPEMeasureLine::saveDefault)); button1->pack_start(*save_default, true, true, 2); - Gtk::Button *remove = Gtk::manage(new Gtk::Button(Glib::ustring(_("Convert to objects")))); - remove->signal_clicked().connect(sigc::mem_fun(*this, &LPEMeasureLine::toObjects)); - button2->pack_start(*remove, true, true, 2); expander = Gtk::manage(new Gtk::Expander(Glib::ustring(_("Show DIM CSS style override")))); expander->add(*vbox_expander); expander->set_expanded(expanded); expander->property_expanded().signal_changed().connect(sigc::mem_fun(*this, &LPEMeasureLine::onExpanderChanged) ); vbox->pack_start(*expander, true, true, 2); vbox->pack_start(*button1, true, true, 2); - vbox->pack_start(*button2, true, true, 2); return dynamic_cast<Gtk::Widget *>(vbox); } @@ -856,21 +807,21 @@ void LPEMeasureLine::saveDefault() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setString("/live_effects/measure-line/fontbutton", (Glib::ustring)fontbutton.param_getSVGValue()); + prefs->setString("/live_effects/measure-line/fontbutton", Glib::ustring(fontbutton.param_getSVGValue())); prefs->setDouble("/live_effects/measure-line/scale", scale); prefs->setInt("/live_effects/measure-line/precision", precision); prefs->setDouble("/live_effects/measure-line/position", position); prefs->setDouble("/live_effects/measure-line/text_top_bottom", text_top_bottom); prefs->setDouble("/live_effects/measure-line/helpline_distance", helpline_distance); prefs->setDouble("/live_effects/measure-line/helpline_overlap", helpline_overlap); - prefs->setString("/live_effects/measure-line/unit", (Glib::ustring)unit.get_abbreviation()); - prefs->setString("/live_effects/measure-line/format", (Glib::ustring)format.param_getSVGValue()); - prefs->setString("/live_effects/measure-line/dimline_format", (Glib::ustring)dimline_format.param_getSVGValue()); - prefs->setString("/live_effects/measure-line/helperlines_format", (Glib::ustring)helperlines_format.param_getSVGValue()); - prefs->setString("/live_effects/measure-line/anotation_format", (Glib::ustring)anotation_format.param_getSVGValue()); - prefs->setString("/live_effects/measure-line/arrows_format", (Glib::ustring)arrows_format.param_getSVGValue()); + prefs->setString("/live_effects/measure-line/unit", Glib::ustring(unit.get_abbreviation())); + prefs->setString("/live_effects/measure-line/format", Glib::ustring(format.param_getSVGValue())); + prefs->setString("/live_effects/measure-line/dimline_format", Glib::ustring(dimline_format.param_getSVGValue())); + prefs->setString("/live_effects/measure-line/helperlines_format", Glib::ustring(helperlines_format.param_getSVGValue())); + prefs->setString("/live_effects/measure-line/anotation_format", Glib::ustring(anotation_format.param_getSVGValue())); + prefs->setString("/live_effects/measure-line/arrows_format", Glib::ustring(arrows_format.param_getSVGValue())); prefs->setBool("/live_effects/measure-line/flip_side", flip_side); - prefs->setBool("/live_effects/measure-line/scale_insensitive", scale_insensitive); + prefs->setBool("/live_effects/measure-line/scale_sensitive", scale_sensitive); prefs->setBool("/live_effects/measure-line/local_locale", local_locale); prefs->setBool("/live_effects/measure-line/line_group_05", line_group_05); prefs->setBool("/live_effects/measure-line/rotate_anotation", rotate_anotation); diff --git a/src/live_effects/lpe-measure-line.h b/src/live_effects/lpe-measure-line.h index 2fab9dbb9..c69921a4d 100644 --- a/src/live_effects/lpe-measure-line.h +++ b/src/live_effects/lpe-measure-line.h @@ -11,6 +11,9 @@ */ #include "live_effects/effect.h" + +#include <gtkmm/expander.h> + #include "live_effects/parameter/enum.h" #include "live_effects/parameter/fontbutton.h" #include "live_effects/parameter/text.h" @@ -32,20 +35,27 @@ enum OrientationMethod { OM_END }; +enum LpeAction { + LPE_ERASE = 0, + LPE_TO_OBJECTS, + LPE_VISIBILITY +}; + class LPEMeasureLine : public Effect { public: LPEMeasureLine(LivePathEffectObject *lpeobject); virtual ~LPEMeasureLine(); virtual void doBeforeEffect (SPLPEItem const* lpeitem); virtual void doOnApply(SPLPEItem const* lpeitem); - virtual void doOnRemove (SPLPEItem const* lpeitem); + virtual void doOnRemove (SPLPEItem const* /*lpeitem*/); + virtual void doEffect (SPCurve * curve){}; //stop the chain virtual void doOnVisibilityToggled(SPLPEItem const* /*lpeitem*/); virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in); - void createLine(Geom::Point start,Geom::Point end,Glib::ustring id, bool main, bool overflow, bool remove, bool arrows = false); - void createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove); + void processObjects(LpeAction lpe_action); + void createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows = false); + void createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid); void onExpanderChanged(); - void toObjects(); - void createArrowMarker(Glib::ustring mode); + void createArrowMarker(const char * mode); void saveDefault(); virtual Gtk::Widget *newWidget(); private: @@ -61,9 +71,10 @@ private: ScalarParam helpline_overlap; ScalarParam scale; TextParam format; + TextParam id_origin; BoolParam arrows_outside; BoolParam flip_side; - BoolParam scale_insensitive; + BoolParam scale_sensitive; BoolParam local_locale; BoolParam line_group_05; BoolParam rotate_anotation; @@ -79,7 +90,9 @@ private: double fontsize; double anotation_width; double arrow_gap; - bool erase; + Geom::Point start_stored; + Geom::Point end_stored; + std::vector<const char *> elements; /* Geom::Affine affine_over;*/ LPEMeasureLine(const LPEMeasureLine &); LPEMeasureLine &operator=(const LPEMeasureLine &); diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 4deb29d8f..61b2b8b5c 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -13,13 +13,16 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ + +#include <gtkmm.h> #include "live_effects/lpe-mirror_symmetry.h" #include <display/curve.h> #include <svg/path-string.h> #include "helper/geom.h" #include <2geom/path-intersection.h> -#include "knotholder.h" + // TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { @@ -34,39 +37,15 @@ static const Util::EnumData<ModeType> ModeTypeData[MT_END] = { static const Util::EnumDataConverter<ModeType> MTConverter(ModeTypeData, MT_END); -namespace MS { - -class KnotHolderEntityCenterMirrorSymmetry : public LPEKnotHolderEntity { -public: - KnotHolderEntityCenterMirrorSymmetry(LPEMirrorSymmetry *effect) : LPEKnotHolderEntity(effect){}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; -}; - -class KnotHolderEntityStartMirrorSymmetry : public LPEKnotHolderEntity { -public: - KnotHolderEntityStartMirrorSymmetry(LPEMirrorSymmetry *effect) : LPEKnotHolderEntity(effect){}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; -}; - -class KnotHolderEntityEndMirrorSymmetry : public LPEKnotHolderEntity { -public: - KnotHolderEntityEndMirrorSymmetry(LPEMirrorSymmetry *effect) : LPEKnotHolderEntity(effect){}; - virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state); - virtual Geom::Point knot_get() const; -}; - -} // namespace MS - LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) : Effect(lpeobject), mode(_("Mode"), _("Symmetry move mode"), "mode", MTConverter, &wr, this, MT_FREE), - discard_orig_path(_("Discard original path?"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false), + discard_orig_path(_("Discard original path"), _("Check this to only keep the mirrored part of the path"), "discard_orig_path", &wr, this, false), fuse_paths(_("Fuse paths"), _("Fuse original and the reflection into a single path"), "fuse_paths", &wr, this, false), oposite_fuse(_("Opposite fuse"), _("Picks the other side of the mirror as the original"), "oposite_fuse", &wr, this, false), - start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, "Adjust the start of mirroring"), - end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, "Adjust end of mirroring") + start_point(_("Start mirror line"), _("Start mirror line"), "start_point", &wr, this, _("Adjust start of mirroring")), + end_point(_("End mirror line"), _("End mirror line"), "end_point", &wr, this, _("Adjust end of mirroring")), + center_point(_("Center mirror line"), _("Center mirror line"), "center_point", &wr, this, _("Adjust center of mirroring")) { show_orig_path = true; registerParameter(&mode); @@ -75,6 +54,8 @@ LPEMirrorSymmetry::LPEMirrorSymmetry(LivePathEffectObject *lpeobject) : registerParameter( &oposite_fuse); registerParameter( &start_point); registerParameter( &end_point); + registerParameter( ¢er_point); + previous_center = Geom::Point(0,0); apply_to_clippath_and_mask = true; } @@ -82,12 +63,47 @@ LPEMirrorSymmetry::~LPEMirrorSymmetry() { } -void +Gtk::Widget * LPEMirrorSymmetry::newWidget() +{ + // use manage here, because after deletion of Effect object, others might + // still be pointing to this widget. + Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget())); + + vbox->set_border_width(5); + vbox->set_homogeneous(false); + vbox->set_spacing(2); + + std::vector<Parameter *>::iterator it = param_vector.begin(); + while (it != param_vector.end()) { + if ((*it)->widget_is_visible) { + Parameter *param = *it; + Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget()); + Glib::ustring *tip = param->param_getTooltip(); + if (widg) { + if (param->param_key != "center_point") { + vbox->pack_start(*widg, true, true, 2); + if (tip) { + widg->set_tooltip_text(*tip); + } else { + widg->set_tooltip_text(""); + widg->set_has_tooltip(false); + } + } + } + } + + ++it; + } + return dynamic_cast<Gtk::Widget *>(vbox); +} + +void LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) { using namespace Geom; original_bbox(lpeitem); + //center_point->param_set_liveupdate(false); Point point_a(boundingbox_X.max(), boundingbox_Y.min()); Point point_b(boundingbox_X.max(), boundingbox_Y.max()); Point point_c(boundingbox_X.max(), boundingbox_Y.middle()); @@ -99,47 +115,59 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) point_a = Geom::Point(center_point[X],boundingbox_Y.min()); point_b = Geom::Point(center_point[X],boundingbox_Y.max()); } - line_separation.setPoints(point_a, point_b); + if ((Geom::Point)start_point == (Geom::Point)end_point) { + start_point.param_setValue(point_a, true); + end_point.param_setValue(point_b, true); + previous_center = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); + center_point.param_setValue(previous_center, true); + return; + } if ( mode == MT_X || mode == MT_Y ) { - start_point.param_setValue(point_a); - end_point.param_setValue(point_b); - center_point = Geom::middle_point(point_a, point_b); + if (!are_near(previous_center, (Geom::Point)center_point, 0.01)) { + center_point.param_setValue(Geom::middle_point(point_a, point_b), true); + end_point.param_setValue(point_b, true); + start_point.param_setValue(point_a, true); + } else { + if ( mode == MT_X ) { + if (!are_near(start_point[X], point_a[X], 0.01)) { + start_point.param_setValue(point_a, true); + } + if (!are_near(end_point[X], point_b[X], 0.01)) { + end_point.param_setValue(point_b, true); + } + } else { //MT_Y + if (!are_near(start_point[Y], point_a[Y], 0.01)) { + start_point.param_setValue(point_a, true); + } + if (!are_near(end_point[Y], point_b[Y], 0.01)) { + end_point.param_setValue(point_b, true); + } + } + } } else if ( mode == MT_FREE) { - if(!are_near(previous_center,center_point, 0.01)) { - Geom::Point trans = center_point - previous_center; - start_point.param_setValue(start_point * trans); - end_point.param_setValue(end_point * trans); - line_separation.setPoints(start_point, end_point); + if (are_near(previous_center, (Geom::Point)center_point, 0.01)) { + center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); } else { - center_point = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); - line_separation.setPoints(start_point, end_point); + Geom::Point trans = center_point - Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); + start_point.param_setValue(start_point * trans, true); + end_point.param_setValue(end_point * trans, true); } } else if ( mode == MT_V){ - if(SP_ACTIVE_DESKTOP){ - SPDocument * doc = SP_ACTIVE_DESKTOP->getDocument(); - Geom::Rect view_box_rect = doc->getViewBox(); - Geom::Point sp = Geom::Point(view_box_rect.width()/2.0, 0); - sp *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); - start_point.param_setValue(sp); - Geom::Point ep = Geom::Point(view_box_rect.width()/2.0, view_box_rect.height()); - ep *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); - end_point.param_setValue(ep); - center_point = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); - line_separation.setPoints(start_point, end_point); - } + SPDocument * document = SP_ACTIVE_DOCUMENT; + Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse(); + Geom::Point sp = Geom::Point(document->getWidth().value("px")/2.0, 0) * transform; + start_point.param_setValue(sp, true); + Geom::Point ep = Geom::Point(document->getWidth().value("px")/2.0, document->getHeight().value("px")) * transform; + end_point.param_setValue(ep, true); + center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); } else { //horizontal page - if(SP_ACTIVE_DESKTOP){ - SPDocument * doc = SP_ACTIVE_DESKTOP->getDocument(); - Geom::Rect view_box_rect = doc->getViewBox(); - Geom::Point sp = Geom::Point(0, view_box_rect.height()/2.0); - sp *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); - start_point.param_setValue(sp); - Geom::Point ep = Geom::Point(view_box_rect.width(), view_box_rect.height()/2.0); - ep *= i2anc_affine(SP_OBJECT(lpeitem), SP_OBJECT(SP_ACTIVE_DESKTOP->currentLayer()->parent)) .inverse(); - end_point.param_setValue(ep); - center_point = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); - line_separation.setPoints(start_point, end_point); - } + SPDocument * document = SP_ACTIVE_DOCUMENT; + Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse(); + Geom::Point sp = Geom::Point(0, document->getHeight().value("px")/2.0) * transform; + start_point.param_setValue(sp, true); + Geom::Point ep = Geom::Point(document->getWidth().value("px"), document->getHeight().value("px")/2.0) * transform; + end_point.param_setValue(ep, true); + center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); } previous_center = center_point; } @@ -147,13 +175,12 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) void LPEMirrorSymmetry::transform_multiply(Geom::Affine const& postmul, bool set) { - center_point *= postmul; - previous_center = center_point; // cycle through all parameters. Most parameters will not need transformation, but path and point params do. for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); ++it) { Parameter * param = *it; param->param_transform_multiply(postmul, set); } + previous_center = Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point); } void @@ -166,11 +193,11 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem) Point point_a(boundingbox_X.max(), boundingbox_Y.min()); Point point_b(boundingbox_X.max(), boundingbox_Y.max()); Point point_c(boundingbox_X.max(), boundingbox_Y.middle()); - start_point.param_setValue(point_a); + start_point.param_setValue(point_a, true); start_point.param_update_default(point_a); - end_point.param_setValue(point_b); + end_point.param_setValue(point_b, true); end_point.param_update_default(point_b); - center_point = point_c; + center_point.param_setValue(point_c, true); previous_center = center_point; } @@ -185,24 +212,8 @@ LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in) path_out = pathv_to_linear_and_cubic_beziers(path_in); } - Geom::Point point_a(line_separation.initialPoint()); - Geom::Point point_b(line_separation.finalPoint()); - - Geom::Translate m1(point_a[0], point_a[1]); - double hyp = Geom::distance(point_a, point_b); - double cos = 0; - double sin = 0; - if (hyp > 0) { - cos = (point_b[0] - point_a[0]) / hyp; - sin = (point_b[1] - point_a[1]) / hyp; - } - Geom::Affine m2(cos, -sin, sin, cos, 0.0, 0.0); - Geom::Scale sca(1.0, -1.0); - - Geom::Affine m = m1.inverse() * m2; - m = m * sca; - m = m * m2.inverse(); - m = m * m1; + Geom::Line line_separation((Geom::Point)start_point, (Geom::Point)end_point); + Geom::Affine m = Geom::reflection (line_separation.vector(), (Geom::Point)start_point); if (fuse_paths && !discard_orig_path) { for (Geom::PathVector::const_iterator path_it = original_pathv.begin(); @@ -330,44 +341,6 @@ LPEMirrorSymmetry::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector hp_vec.push_back(helper); } -void -LPEMirrorSymmetry::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) -{ - SPKnotShapeType knot_shape = SP_KNOT_SHAPE_CIRCLE; - SPKnotModeType knot_mode = SP_KNOT_MODE_XOR; - guint32 knot_color = 0x0000ff00; - { - KnotHolderEntity *c = new MS::KnotHolderEntityCenterMirrorSymmetry(this); - c->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, - _("Adjust the center"), knot_shape, knot_mode, knot_color ); - knotholder->add(c); - } -}; - -namespace MS { - -using namespace Geom; - -void -KnotHolderEntityCenterMirrorSymmetry::knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) -{ - LPEMirrorSymmetry* lpe = dynamic_cast<LPEMirrorSymmetry *>(_effect); - Geom::Point const s = snap_knot_position(p, state); - lpe->center_point = s; - - // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true); -} - -Geom::Point -KnotHolderEntityCenterMirrorSymmetry::knot_get() const -{ - LPEMirrorSymmetry const *lpe = dynamic_cast<LPEMirrorSymmetry const*>(_effect); - return lpe->center_point; -} - -} // namespace CR - } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h index 7ec4029e0..b4967173a 100644 --- a/src/live_effects/lpe-mirror_symmetry.h +++ b/src/live_effects/lpe-mirror_symmetry.h @@ -15,7 +15,7 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gtkmm.h> + #include "live_effects/effect.h" #include "live_effects/parameter/parameter.h" #include "live_effects/parameter/point.h" @@ -26,11 +26,6 @@ namespace Inkscape { namespace LivePathEffect { -namespace MS { -// we need a separate namespace to avoid clashes with LPEPerpBisector -class KnotHolderEntityCenterMirrorSymmetry; -} - enum ModeType { MT_V, MT_H, @@ -48,9 +43,7 @@ public: virtual void doBeforeEffect (SPLPEItem const* lpeitem); virtual void transform_multiply(Geom::Affine const& postmul, bool set); virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in); - /* the knotholder entity classes must be declared friends */ - friend class MS::KnotHolderEntityCenterMirrorSymmetry; - void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + virtual Gtk::Widget * newWidget(); protected: virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec); @@ -62,9 +55,8 @@ private: BoolParam oposite_fuse; PointParam start_point; PointParam end_point; - Geom::Line line_separation; + PointParam center_point; Geom::Point previous_center; - Geom::Point center_point; LPEMirrorSymmetry(const LPEMirrorSymmetry&); LPEMirrorSymmetry& operator=(const LPEMirrorSymmetry&); diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp index a0fa46c3f..057f404e0 100644 --- a/src/live_effects/lpe-offset.cpp +++ b/src/live_effects/lpe-offset.cpp @@ -11,13 +11,12 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-offset.h" #include "sp-shape.h" #include "display/curve.h" - #include <2geom/elliptical-arc.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-parallel.cpp b/src/live_effects/lpe-parallel.cpp index 9cd8ecf46..276749c43 100644 --- a/src/live_effects/lpe-parallel.cpp +++ b/src/live_effects/lpe-parallel.cpp @@ -11,14 +11,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-parallel.h" #include "sp-shape.h" #include "display/curve.h" #include "knotholder.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-path_length.cpp b/src/live_effects/lpe-path_length.cpp index 8fbf9d420..a06dbde98 100644 --- a/src/live_effects/lpe-path_length.cpp +++ b/src/live_effects/lpe-path_length.cpp @@ -11,10 +11,10 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-path_length.h" #include "util/units.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { @@ -37,12 +37,6 @@ LPEPathLength::~LPEPathLength() } -void -LPEPathLength::hideCanvasText() { - // this is only used in sp-lpe-item.cpp to hide the canvas text when the effect is invisible - info_text.param_setValue(""); -} - Geom::Piecewise<Geom::D2<Geom::SBasis> > LPEPathLength::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) { @@ -69,7 +63,9 @@ LPEPathLength::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & p //g_print ("Area is zero\n"); } //g_print ("Area: %f\n", area); - + if (!this->isVisible()) { + info_text.param_setValue(""); + } return pwd2_in; } diff --git a/src/live_effects/lpe-path_length.h b/src/live_effects/lpe-path_length.h index e108e770a..14d093c09 100644 --- a/src/live_effects/lpe-path_length.h +++ b/src/live_effects/lpe-path_length.h @@ -29,8 +29,6 @@ public: virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in); - void hideCanvasText(); - private: LPEPathLength(const LPEPathLength&); LPEPathLength& operator=(const LPEPathLength&); diff --git a/src/live_effects/lpe-patternalongpath.cpp b/src/live_effects/lpe-patternalongpath.cpp index 0785da235..966e9020e 100644 --- a/src/live_effects/lpe-patternalongpath.cpp +++ b/src/live_effects/lpe-patternalongpath.cpp @@ -11,7 +11,10 @@ #include <2geom/bezier-to-sbasis.h> #include "knotholder.h" +#include <cmath> #include <algorithm> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> using std::vector; @@ -61,7 +64,7 @@ static const Util::EnumDataConverter<PAPCopyType> PAPCopyTypeConverter(PAPCopyTy LPEPatternAlongPath::LPEPatternAlongPath(LivePathEffectObject *lpeobject) : Effect(lpeobject), pattern(_("Pattern source:"), _("Path to put along the skeleton path"), "pattern", &wr, this, "M0,0 L1,0"), - original_height(0), + original_height(0.0), prop_scale(_("_Width:"), _("Width of the pattern"), "prop_scale", &wr, this, 1.0), copytype(_("Pattern copies:"), _("How many pattern copies to place along the skeleton path"), "copytype", PAPCopyTypeConverter, &wr, this, PAPCT_SINGLE_STRETCHED), @@ -142,15 +145,15 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con double xspace = spacing; double noffset = normal_offset; double toffset = tang_offset; - if (prop_units.get_value() && pattBndsY){ + if (prop_units.get_value()){ xspace *= pattBndsX->extent(); noffset *= pattBndsY->extent(); toffset *= pattBndsX->extent(); } //Prevent more than 90% overlap... - if (xspace < -pattBndsX->extent()*.9) { - xspace = -pattBndsX->extent()*.9; + if (xspace < -pattBndsX->extent() * 0.9) { + xspace = -pattBndsX->extent() * 0.9; } //TODO: dynamical update of parameter ranges? //if (prop_units.get_value()){ @@ -159,7 +162,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con // spacing.param_set_range(-pattBndsX.extent()*.9, Geom::infinity()); // } - y0+=noffset; + y0 += noffset; std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > paths_in; paths_in = split_at_discontinuities(pwd2_in); @@ -168,11 +171,14 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con Geom::Piecewise<Geom::D2<Geom::SBasis> > path_i = paths_in[idx]; Piecewise<SBasis> x = x0; Piecewise<SBasis> y = y0; - Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1); - uskeleton = remove_short_cuts(uskeleton,.01); + Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2, 0.1); + uskeleton = remove_short_cuts(uskeleton, 0.01); Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton)); - n = force_continuity(remove_short_cuts(n,.1)); - + if (Geom::are_near(pwd2_in[0].at0(),pwd2_in[pwd2_in.size()-1].at1(), 0.01)) { + n = force_continuity(remove_short_cuts(n, 0.1), 0.01); + } else { + n = force_continuity(remove_short_cuts(n, 0.1)); + } int nbCopies = 0; double scaling = 1; switch(type) { @@ -192,7 +198,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con case PAPCT_REPEATED_STRETCHED: // if uskeleton is closed: - if(path_i.segs.front().at0() == path_i.segs.back().at1()){ + if (are_near(path_i.segs.front().at0(), path_i.segs.back().at1())){ nbCopies = std::max(1, static_cast<int>(std::floor((uskeleton.domain().extent() - toffset)/(pattBndsX->extent()+xspace)))); pattBndsX = Interval(pattBndsX->min(),pattBndsX->max()+xspace); scaling = (uskeleton.domain().extent() - toffset)/(((double)nbCopies)*pattBndsX->extent()); @@ -208,18 +214,18 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con return pwd2_in; }; + //Ceil to 6 decimals + scaling = ceil(scaling * 1000000) / 1000000; double pattWidth = pattBndsX->extent() * scaling; - if (scaling != 1.0) { - x*=scaling; - } + x *= scaling; if ( scale_y_rel.get_value() ) { - y*=(scaling*prop_scale); + y *= prop_scale * scaling; } else { - if (prop_scale != 1.0) y *= prop_scale; + y *= prop_scale; } x += toffset; - + double offs = 0; for (int i=0; i<nbCopies; i++){ if (fuse_tolerance > 0){ @@ -232,7 +238,7 @@ LPEPatternAlongPath::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > con offs+=pattWidth; } } - if (fuse_tolerance > 0){ + if (fuse_tolerance > 0){ pre_output = fuse_nearby_ends(pre_output, fuse_tolerance); for (unsigned i=0; i<pre_output.size(); i++){ output.concat(pre_output[i]); @@ -261,7 +267,6 @@ LPEPatternAlongPath::transform_multiply(Geom::Affine const& postmul, bool set) pattern.param_transform_multiply(postmul, set); pattern.write_to_SVG(); } - sp_lpe_item_update_patheffect (sp_lpe_item, false, true); } void @@ -272,10 +277,10 @@ LPEPatternAlongPath::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vect void -LPEPatternAlongPath::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +LPEPatternAlongPath::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { KnotHolderEntity *e = new WPAP::KnotHolderEntityWidthPatternAlongPath(this); - e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Change the width"), SP_KNOT_SHAPE_CIRCLE); + e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Change the width"), SP_KNOT_SHAPE_CIRCLE); knotholder->add(e); } diff --git a/src/live_effects/lpe-patternalongpath.h b/src/live_effects/lpe-patternalongpath.h index 3d7fc02bc..c34a9a15d 100644 --- a/src/live_effects/lpe-patternalongpath.h +++ b/src/live_effects/lpe-patternalongpath.h @@ -43,7 +43,7 @@ public: void addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec); - virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item); + virtual void addKnotHolderEntities(KnotHolder * knotholder, SPItem * item); PathParam pattern; diff --git a/src/live_effects/lpe-perp_bisector.cpp b/src/live_effects/lpe-perp_bisector.cpp index f69dae6a1..bce22250a 100644 --- a/src/live_effects/lpe-perp_bisector.cpp +++ b/src/live_effects/lpe-perp_bisector.cpp @@ -11,9 +11,6 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ - -#include <glibmm/i18n.h> - #include "live_effects/lpe-perp_bisector.h" #include "display/curve.h" #include "sp-path.h" @@ -21,6 +18,9 @@ #include "knotholder.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { namespace PB { diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp index 6a6b59519..18b5b724d 100644 --- a/src/live_effects/lpe-perspective-envelope.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -20,6 +20,9 @@ #include "display/curve.h" #include <gsl/gsl_linalg.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + using namespace Geom; namespace Inkscape { @@ -376,7 +379,7 @@ LPEPerspectiveEnvelope::newWidget() hbox_down_handles->pack_start(*widg, true, true, 2); } if (tip) { - widg->set_tooltip_text(*tip); + widg->set_tooltip_markup(*tip); } else { widg->set_tooltip_text(""); widg->set_has_tooltip(false); @@ -405,7 +408,8 @@ LPEPerspectiveEnvelope::newWidget() vbox->pack_start(*hbox_middle, false, true, 2); vbox->pack_start(*hbox_down_handles, true, true, 2); Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); - Gtk::Button* reset_button = Gtk::manage(new Gtk::Button(Gtk::Stock::CLEAR)); + Gtk::Button* reset_button = Gtk::manage(new Gtk::Button(_("_Clear"), true)); + reset_button->set_image_from_icon_name("edit-clear"); reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPEPerspectiveEnvelope::resetGrid)); reset_button->set_size_request(140,30); vbox->pack_start(*hbox, true,true,2); @@ -537,4 +541,4 @@ LPEPerspectiveEnvelope::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::v fill-column:99 End: */ -// vim: file_type=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/live_effects/lpe-perspective_path.cpp b/src/live_effects/lpe-perspective_path.cpp index cb4e43d87..979b6dea5 100644 --- a/src/live_effects/lpe-perspective_path.cpp +++ b/src/live_effects/lpe-perspective_path.cpp @@ -11,8 +11,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ #include <gtkmm.h> -#include <glibmm/i18n.h> - #include "persp3d.h" //#include "transf_mat_3x4.h" #include "document-private.h" @@ -20,9 +18,11 @@ #include "live_effects/lpeobject.h" #include "knot-holder-entity.h" #include "knotholder.h" -#include "desktop.h" #include <util/units.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { @@ -242,9 +242,9 @@ LPEPerspectivePath::newWidget() return dynamic_cast<Gtk::Widget *>(vbox); } -void LPEPerspectivePath::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { +void LPEPerspectivePath::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { KnotHolderEntity *e = new PP::KnotHolderEntityOffset(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + e->create( NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Adjust the origin") ); knotholder->add(e); }; diff --git a/src/live_effects/lpe-perspective_path.h b/src/live_effects/lpe-perspective_path.h index c4ddf1853..87ee453ff 100644 --- a/src/live_effects/lpe-perspective_path.h +++ b/src/live_effects/lpe-perspective_path.h @@ -41,7 +41,7 @@ public: virtual Gtk::Widget * newWidget(); /* the knotholder entity classes must be declared friends */ friend class PP::KnotHolderEntityOffset; - void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); private: // add the parameters for your effect here: diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp index 329a00756..e9f3975c7 100644 --- a/src/live_effects/lpe-powerstroke.cpp +++ b/src/live_effects/lpe-powerstroke.cpp @@ -25,6 +25,9 @@ #include <2geom/circle.h> #include "helper/geom.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Geom { // should all be moved to 2geom at some point diff --git a/src/live_effects/lpe-recursiveskeleton.cpp b/src/live_effects/lpe-recursiveskeleton.cpp index ed0c915ce..aa0db920b 100644 --- a/src/live_effects/lpe-recursiveskeleton.cpp +++ b/src/live_effects/lpe-recursiveskeleton.cpp @@ -10,12 +10,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-recursiveskeleton.h" #include <2geom/bezier-to-sbasis.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-rough-hatches.cpp b/src/live_effects/lpe-rough-hatches.cpp index 2fb65b349..3cc8658ea 100644 --- a/src/live_effects/lpe-rough-hatches.cpp +++ b/src/live_effects/lpe-rough-hatches.cpp @@ -13,7 +13,6 @@ */ #include "ui/widget/scalar.h" -#include <glibmm/i18n.h> #include "live_effects/lpe-rough-hatches.h" #include "sp-item.h" @@ -23,6 +22,8 @@ #include <2geom/sbasis-math.h> #include <2geom/bezier-to-sbasis.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp index 3a486ff10..c6edffd9b 100644 --- a/src/live_effects/lpe-roughen.cpp +++ b/src/live_effects/lpe-roughen.cpp @@ -13,11 +13,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <gtkmm.h> #include "live_effects/lpe-roughen.h" #include "display/curve.h" #include <boost/functional/hash.hpp> #include "helper/geom.h" + // TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-roughen.h b/src/live_effects/lpe-roughen.h index dbdb91e62..bab06022f 100644 --- a/src/live_effects/lpe-roughen.h +++ b/src/live_effects/lpe-roughen.h @@ -12,7 +12,6 @@ #ifndef INKSCAPE_LPE_ROUGHEN_H #define INKSCAPE_LPE_ROUGHEN_H -#include <gtkmm.h> #include "live_effects/effect.h" #include "live_effects/parameter/enum.h" #include "live_effects/parameter/parameter.h" diff --git a/src/live_effects/lpe-ruler.cpp b/src/live_effects/lpe-ruler.cpp index 3a2d78b2c..60c2a3e1c 100644 --- a/src/live_effects/lpe-ruler.cpp +++ b/src/live_effects/lpe-ruler.cpp @@ -12,7 +12,8 @@ */ #include "live_effects/lpe-ruler.h" - +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-show_handles.cpp b/src/live_effects/lpe-show_handles.cpp index 388ea176f..7c298d0e7 100644 --- a/src/live_effects/lpe-show_handles.cpp +++ b/src/live_effects/lpe-show_handles.cpp @@ -6,6 +6,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <gtkmm.h> #include "live_effects/lpe-show_handles.h" #include <2geom/sbasis-to-bezier.h> #include <2geom/svg-path-parser.h> @@ -13,6 +14,7 @@ #include "desktop-style.h" #include "style.h" #include "svg/svg.h" + // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -24,20 +26,17 @@ LPEShowHandles::LPEShowHandles(LivePathEffectObject *lpeobject) nodes(_("Show nodes"), _("Show nodes"), "nodes", &wr, this, true), handles(_("Show handles"), _("Show handles"), "handles", &wr, this, true), original_path(_("Show path"), _("Show path"), "original_path", &wr, this, true), - scale_nodes_and_handles(_("Scale nodes and handles"), _("Scale nodes and handles"), "scale_nodes_and_handles", &wr, this, 10), - rotate_nodes(_("Rotate nodes"), _("Rotate nodes"), "rotate_nodes", &wr, this, 0) + show_center_node(_("Show center of node"), _("Show center of node"), "show_center_node", &wr, this, false), + scale_nodes_and_handles(_("Scale nodes and handles"), _("Scale nodes and handles"), "scale_nodes_and_handles", &wr, this, 10) { registerParameter(&nodes); registerParameter(&handles); registerParameter(&original_path); + registerParameter(&show_center_node); registerParameter(&scale_nodes_and_handles); - registerParameter(&rotate_nodes); scale_nodes_and_handles.param_set_range(0, 500.); scale_nodes_and_handles.param_set_increments(1, 1); scale_nodes_and_handles.param_set_digits(2); - rotate_nodes.param_set_range(0, 365); - rotate_nodes.param_set_increments(1, 1); - rotate_nodes.param_set_digits(0); stroke_width = 1.0; } @@ -129,7 +128,11 @@ LPEShowHandles::generateHelperPath(Geom::PathVector result) } } if(nodes) { - drawNode(curve_it1->initialPoint()); + Geom::NodeType nodetype = Geom::NODE_CUSP; + if(path_it->closed()) { + nodetype = Geom::get_nodetype(path_it->finalCurve(), *curve_it1); + } + drawNode(curve_it1->initialPoint(), nodetype); } while (curve_it1 != curve_endit) { cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1); @@ -145,8 +148,9 @@ LPEShowHandles::generateHelperPath(Geom::PathVector result) } } } - if(nodes) { - drawNode(curve_it1->finalPoint()); + if(nodes && (curve_it2 != curve_endit || !path_it->closed())) { + Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2); + drawNode(curve_it1->finalPoint(), nodetype); } ++curve_it1; if(curve_it2 != curve_endit) { @@ -157,16 +161,26 @@ LPEShowHandles::generateHelperPath(Geom::PathVector result) } void -LPEShowHandles::drawNode(Geom::Point p) +LPEShowHandles::drawNode(Geom::Point p, Geom::NodeType nodetype) { if(stroke_width * scale_nodes_and_handles > 0.0) { + Geom::Rotate rotate = Geom::Rotate(0); + if ( nodetype == Geom::NODE_CUSP) { + rotate = Geom::Rotate::from_degrees(45); + } double diameter = stroke_width * scale_nodes_and_handles; char const * svgd; - svgd = "M 0.05,0 A 0.05,0.05 0 0 1 0,0.05 0.05,0.05 0 0 1 -0.05,0 0.05,0.05 0 0 1 0,-0.05 0.05,0.05 0 0 1 0.05,0 Z M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z"; + if (show_center_node) { + svgd = "M 0.05,0 A 0.05,0.05 0 0 1 0,0.05 0.05,0.05 0 0 1 -0.05,0 0.05,0.05 0 0 1 0,-0.05 0.05,0.05 0 0 1 0.05,0 Z M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z"; + } else { + svgd = "M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z"; + } Geom::PathVector pathv = sp_svg_read_pathv(svgd); - pathv *= Geom::Rotate::from_degrees(rotate_nodes) * Geom::Scale(diameter) * Geom::Translate(p); + pathv *= rotate * Geom::Scale(diameter) * Geom::Translate(p); outline_path.push_back(pathv[0]); - outline_path.push_back(pathv[1]); + if (show_center_node) { + outline_path.push_back(pathv[1]); + } } } diff --git a/src/live_effects/lpe-show_handles.h b/src/live_effects/lpe-show_handles.h index 8bff3c1a8..c46abd2c2 100644 --- a/src/live_effects/lpe-show_handles.h +++ b/src/live_effects/lpe-show_handles.h @@ -8,7 +8,7 @@ * Copyright (C) Jabier Arraiza Cenoz 2014 <jabier.arraiza@marker.es> * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gtkmm.h> +#include "helper/geom-nodetype.h" #include "live_effects/effect.h" #include "live_effects/lpegroupbbox.h" #include "live_effects/parameter/bool.h" @@ -28,7 +28,7 @@ public: virtual void generateHelperPath(Geom::PathVector result); - virtual void drawNode(Geom::Point p); + virtual void drawNode(Geom::Point p, Geom::NodeType nodetype); virtual void drawHandle(Geom::Point p); @@ -43,8 +43,8 @@ private: BoolParam nodes; BoolParam handles; BoolParam original_path; + BoolParam show_center_node; ScalarParam scale_nodes_and_handles; - ScalarParam rotate_nodes; double stroke_width; static bool alerts_off; diff --git a/src/live_effects/lpe-simplify.cpp b/src/live_effects/lpe-simplify.cpp index ec21e10d2..5de9816bb 100644 --- a/src/live_effects/lpe-simplify.cpp +++ b/src/live_effects/lpe-simplify.cpp @@ -2,6 +2,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include <gtkmm.h> #include "live_effects/lpe-simplify.h" #include "display/curve.h" #include "helper/geom.h" @@ -9,6 +10,7 @@ #include "svg/svg.h" #include "ui/tools/node-tool.h" #include "ui/icon-names.h" + // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -138,11 +140,7 @@ LPESimplify::doEffect(SPCurve *curve) Geom::PathVector result = Geom::parse_svg_path(pathliv->svg_dump_path()); generateHelperPathAndSmooth(result); curve->set_pathvector(result); - SPDesktop* desktop = SP_ACTIVE_DESKTOP; - if(desktop && INK_IS_NODE_TOOL(desktop->event_context)) { - Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(desktop->event_context); - nt->update_helperpath(); - } + Inkscape::UI::Tools::sp_update_helperpath(); } void diff --git a/src/live_effects/lpe-simplify.h b/src/live_effects/lpe-simplify.h index 8135561af..6c407f572 100644 --- a/src/live_effects/lpe-simplify.h +++ b/src/live_effects/lpe-simplify.h @@ -6,7 +6,6 @@ * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gtkmm.h> #include "live_effects/effect.h" #include "live_effects/parameter/togglebutton.h" #include "live_effects/lpegroupbbox.h" diff --git a/src/live_effects/lpe-skeleton.cpp b/src/live_effects/lpe-skeleton.cpp index 7d34db699..4fc18cee2 100644 --- a/src/live_effects/lpe-skeleton.cpp +++ b/src/live_effects/lpe-skeleton.cpp @@ -20,8 +20,7 @@ #include "live_effects/lpe-skeleton.h" -// You might need to include other 2geom files. You can add them here: - +// TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> namespace Inkscape { @@ -98,10 +97,10 @@ public: } // namespace Skeleton void -LPESkeleton::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { +LPESkeleton::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { { KnotHolderEntityMyHandle *e = new KnotHolderEntityMyHandle(this); - e->create( desktop, item, knotholder, + e->create( NULL, item, knotholder, _("Text describing what this handle does"), //optional: knot_shape, knot_mode, knot_color); knotholder->add(e); diff --git a/src/live_effects/lpe-skeleton.h b/src/live_effects/lpe-skeleton.h index 3b45b6978..e633ba5ab 100644 --- a/src/live_effects/lpe-skeleton.h +++ b/src/live_effects/lpe-skeleton.h @@ -41,7 +41,7 @@ public: /* the knotholder entity classes (if any) can be declared friends */ //friend class Skeleton::KnotHolderEntityMyHandle; - //virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + //virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); private: // add the parameters for your effect here: diff --git a/src/live_effects/lpe-sketch.cpp b/src/live_effects/lpe-sketch.cpp index 95e2f6f0d..e01516f2e 100644 --- a/src/live_effects/lpe-sketch.cpp +++ b/src/live_effects/lpe-sketch.cpp @@ -13,13 +13,14 @@ #include "live_effects/lpe-sketch.h" -#include <glibmm/i18n.h> - // You might need to include other 2geom files. You can add them here: #include <2geom/sbasis-math.h> #include <2geom/bezier-to-sbasis.h> #include <2geom/path-intersection.h> +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp index 4a41dc5a0..8ea57bee4 100644 --- a/src/live_effects/lpe-spiro.cpp +++ b/src/live_effects/lpe-spiro.cpp @@ -16,8 +16,6 @@ // For handling un-continuous paths: #include "message-stack.h" #include "inkscape.h" -#include "desktop.h" - namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-tangent_to_curve.cpp b/src/live_effects/lpe-tangent_to_curve.cpp index b308ef8d7..5f63e1ee9 100644 --- a/src/live_effects/lpe-tangent_to_curve.cpp +++ b/src/live_effects/lpe-tangent_to_curve.cpp @@ -13,13 +13,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-tangent_to_curve.h" #include "sp-path.h" #include "display/curve.h" #include "knotholder.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> namespace Inkscape { namespace LivePathEffect { @@ -92,22 +92,22 @@ LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const } void -LPETangentToCurve::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) { +LPETangentToCurve::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { { KnotHolderEntity *e = new TtC::KnotHolderEntityAttachPt(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + e->create( NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Adjust the point of attachment of the tangent") ); knotholder->add(e); } { KnotHolderEntity *e = new TtC::KnotHolderEntityLeftEnd(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + e->create( NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Adjust the <b>left</b> end of the tangent") ); knotholder->add(e); } { KnotHolderEntity *e = new TtC::KnotHolderEntityRightEnd(this); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + e->create( NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Adjust the <b>right</b> end of the tangent") ); knotholder->add(e); } diff --git a/src/live_effects/lpe-tangent_to_curve.h b/src/live_effects/lpe-tangent_to_curve.h index 8e44c01d1..a6a3c17ca 100644 --- a/src/live_effects/lpe-tangent_to_curve.h +++ b/src/live_effects/lpe-tangent_to_curve.h @@ -41,7 +41,7 @@ public: friend class TtC::KnotHolderEntityLeftEnd; friend class TtC::KnotHolderEntityRightEnd; friend class TtC::KnotHolderEntityAttachPt; - virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item); + virtual void addKnotHolderEntities(KnotHolder * knotholder, SPItem * item); private: ScalarParam angle; diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp index f6f6b33dc..f4a81aa90 100644 --- a/src/live_effects/lpe-taperstroke.cpp +++ b/src/live_effects/lpe-taperstroke.cpp @@ -26,6 +26,8 @@ #include "svg/svg.h" #include "knotholder.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> template<typename T> inline bool withinRange(T value, T low, T high) { @@ -433,14 +435,14 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path } } -void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this); - e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE); + e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE); knotholder->add(e); KnotHolderEntity *f = new TpS::KnotHolderEntityAttachEnd(this); - f->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE); + f->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE); knotholder->add(f); } diff --git a/src/live_effects/lpe-taperstroke.h b/src/live_effects/lpe-taperstroke.h index 88604486e..e3ecbb56c 100644 --- a/src/live_effects/lpe-taperstroke.h +++ b/src/live_effects/lpe-taperstroke.h @@ -36,7 +36,7 @@ public: virtual Geom::PathVector doEffect_path (Geom::PathVector const& path_in); Geom::PathVector doEffect_simplePath(Geom::PathVector const& path_in); - virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item); + virtual void addKnotHolderEntities(KnotHolder * knotholder, SPItem * item); friend class TpS::KnotHolderEntityAttachBegin; friend class TpS::KnotHolderEntityAttachEnd; diff --git a/src/live_effects/lpe-test-doEffect-stack.cpp b/src/live_effects/lpe-test-doEffect-stack.cpp index c7ecf6481..324893706 100644 --- a/src/live_effects/lpe-test-doEffect-stack.cpp +++ b/src/live_effects/lpe-test-doEffect-stack.cpp @@ -4,10 +4,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-test-doEffect-stack.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + using std::memcpy; namespace Inkscape { diff --git a/src/live_effects/lpe-text_label.cpp b/src/live_effects/lpe-text_label.cpp index 602a6897c..709d05e18 100644 --- a/src/live_effects/lpe-text_label.cpp +++ b/src/live_effects/lpe-text_label.cpp @@ -11,10 +11,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glibmm/i18n.h> - #include "live_effects/lpe-text_label.h" +// TODO due to internal breakage in glibmm headers, this must be last: +#include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { diff --git a/src/live_effects/lpe-transform_2pts.cpp b/src/live_effects/lpe-transform_2pts.cpp index 78db622f2..ef2900775 100644 --- a/src/live_effects/lpe-transform_2pts.cpp +++ b/src/live_effects/lpe-transform_2pts.cpp @@ -20,6 +20,7 @@ // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> + namespace Inkscape { namespace LivePathEffect { @@ -33,7 +34,7 @@ LPETransform2Pts::LPETransform2Pts(LivePathEffectObject *lpeobject) : flip_vertical(_("Flip vertical"), _("Flip vertical"), "flip_vertical", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")), start(_("Start"), _("Start point"), "start", &wr, this, "Start point"), end(_("End"), _("End point"), "end", &wr, this, "End point"), - strech(_("Stretch"), _("Stretch the result"), "strech", &wr, this, 1), + stretch(_("Stretch"), _("Stretch the result"), "stretch", &wr, this, 1), offset(_("Offset"), _("Offset from knots"), "offset", &wr, this, 0), first_knot(_("First Knot"), _("First Knot"), "first_knot", &wr, this, 1), last_knot(_("Last Knot"), _("Last Knot"), "last_knot", &wr, this, 1), @@ -51,7 +52,7 @@ LPETransform2Pts::LPETransform2Pts(LivePathEffectObject *lpeobject) : registerParameter(&first_knot); registerParameter(&last_knot); registerParameter(&helper_size); - registerParameter(&strech); + registerParameter(&stretch); registerParameter(&offset); registerParameter(&start); registerParameter(&end); @@ -72,9 +73,9 @@ LPETransform2Pts::LPETransform2Pts(LivePathEffectObject *lpeobject) : offset.param_set_range(-999999.0, 999999.0); offset.param_set_increments(1, 1); offset.param_set_digits(2); - strech.param_set_range(0, 999.0); - strech.param_set_increments(0.01, 0.01); - strech.param_set_digits(4); + stretch.param_set_range(0, 999.0); + stretch.param_set_increments(0.01, 0.01); + stretch.param_set_digits(4); apply_to_clippath_and_mask = true; } @@ -378,9 +379,9 @@ LPETransform2Pts::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const m *= Geom::Scale(-1,1); m *= Geom::Rotate(original_angle); } - if(strech != 1){ + if(stretch != 1){ m *= Geom::Rotate(-original_angle); - m *= Geom::Scale(1,strech); + m *= Geom::Scale(1,stretch); m *= Geom::Rotate(original_angle); } if(elastic) { diff --git a/src/live_effects/lpe-transform_2pts.h b/src/live_effects/lpe-transform_2pts.h index c20d56206..0f88e6b00 100644 --- a/src/live_effects/lpe-transform_2pts.h +++ b/src/live_effects/lpe-transform_2pts.h @@ -57,7 +57,7 @@ private: ToggleButtonParam flip_vertical; PointParam start; PointParam end; - ScalarParam strech; + ScalarParam stretch; ScalarParam offset; ScalarParam first_knot; ScalarParam last_knot; diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 2486f3366..47e2a1cec 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -5,7 +5,7 @@ */ #include "live_effects/lpe-vonkoch.h" - +// TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> //using std::vector; diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp index b321a5831..7696288b0 100644 --- a/src/live_effects/parameter/filletchamferpointarray.cpp +++ b/src/live_effects/parameter/filletchamferpointarray.cpp @@ -24,7 +24,6 @@ #include "selection.h" // needed for on-canvas editting: -#include "desktop.h" #include "live_effects/lpeobject.h" #include "helper/geom-nodetype.h" #include "helper/geom-curves.h" @@ -825,10 +824,7 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_set_offset( this->parent_holder->knot_ungrabbed_handler(this->knot, 0); } -void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, - SPDesktop *desktop, - SPItem *item) -{ +void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { recalculate_knots(get_pwd2()); for (unsigned int i = 0; i < _vector.size(); ++i) { if (_vector[i][Y] <= 0) { @@ -854,7 +850,7 @@ void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, } FilletChamferPointArrayParamKnotHolderEntity *e = new FilletChamferPointArrayParamKnotHolderEntity(this, i); - e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip), + e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip), knot_shape, knot_mode, knot_color); knotholder->add(e); } diff --git a/src/live_effects/parameter/filletchamferpointarray.h b/src/live_effects/parameter/filletchamferpointarray.h index 48cd26d2d..b81339a69 100644 --- a/src/live_effects/parameter/filletchamferpointarray.h +++ b/src/live_effects/parameter/filletchamferpointarray.h @@ -59,8 +59,7 @@ public: return true; } virtual void updateCanvasIndicators(); - virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, - SPItem *item); + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); void set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_normal_in); diff --git a/src/live_effects/parameter/fontbutton.cpp b/src/live_effects/parameter/fontbutton.cpp index ff8ab76a0..64c203093 100644 --- a/src/live_effects/parameter/fontbutton.cpp +++ b/src/live_effects/parameter/fontbutton.cpp @@ -4,7 +4,7 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ - +#include <gtkmm.h> #include "ui/widget/registered-widget.h" #include "live_effects/parameter/fontbutton.h" #include "live_effects/effect.h" diff --git a/src/live_effects/parameter/fontbutton.h b/src/live_effects/parameter/fontbutton.h index 387ad130b..df47251a2 100644 --- a/src/live_effects/parameter/fontbutton.h +++ b/src/live_effects/parameter/fontbutton.h @@ -8,7 +8,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ #include <glib.h> -#include <gtkmm.h> #include "live_effects/parameter/parameter.h" namespace Inkscape { diff --git a/src/live_effects/parameter/originalpath.cpp b/src/live_effects/parameter/originalpath.cpp index 2741461be..f7eb48b7a 100644 --- a/src/live_effects/parameter/originalpath.cpp +++ b/src/live_effects/parameter/originalpath.cpp @@ -56,7 +56,7 @@ OriginalPathParam::param_newWidget() } { // Paste path to link button - Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-paste"), Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( INKSCAPE_ICON("edit-clone"), Inkscape::ICON_SIZE_BUTTON) ); Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); pButton->set_relief(Gtk::RELIEF_NONE); pIcon->show(); diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp index 4ee068ebf..083abc94c 100644 --- a/src/live_effects/parameter/originalpatharray.cpp +++ b/src/live_effects/parameter/originalpatharray.cpp @@ -144,7 +144,7 @@ Gtk::Widget* OriginalPathArrayParam::param_newWidget() { // Paste path to link button - Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon("gtk-stock", Inkscape::ICON_SIZE_BUTTON) ); + Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon("edit-clone", Inkscape::ICON_SIZE_BUTTON) ); Gtk::Button *pButton = Gtk::manage(new Gtk::Button()); pButton->set_relief(Gtk::RELIEF_NONE); pIcon->show(); diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h index 3658bded8..6cf10710c 100644 --- a/src/live_effects/parameter/parameter.h +++ b/src/live_effects/parameter/parameter.h @@ -68,7 +68,7 @@ public: // overload these for your particular parameter to make it provide knotholder handles or canvas helperpaths virtual bool providesKnotHolderEntities() const { return false; } - virtual void addKnotHolderEntities(KnotHolder */*knotholder*/, SPDesktop */*desktop*/, SPItem */*item*/) {}; + virtual void addKnotHolderEntities(KnotHolder */*knotholder*/, SPItem */*item*/) {}; virtual void addCanvasIndicators(SPLPEItem const*/*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/) {}; virtual void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) {}; @@ -118,6 +118,8 @@ public: void param_set_digits(unsigned digits); void param_set_increments(double step, double page); void addSlider(bool add_slider_widget) { add_slider = add_slider_widget; }; + double param_get_max() { return max; }; + double param_get_min() { return min; }; void param_overwrite_widget(bool overwrite_widget); virtual Gtk::Widget * param_newWidget(); diff --git a/src/live_effects/parameter/point.cpp b/src/live_effects/parameter/point.cpp index 3442fd851..c87b1e299 100644 --- a/src/live_effects/parameter/point.cpp +++ b/src/live_effects/parameter/point.cpp @@ -15,9 +15,6 @@ #include "knotholder.h" #include <glibmm/i18n.h> -// needed for on-canvas editting: -#include "desktop.h" - namespace Inkscape { namespace LivePathEffect { @@ -126,9 +123,8 @@ PointParam::param_newWidget() *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::Affine transf = desktop->doc2dt(); + Geom::Affine transf = Geom::Scale(1, -1); + transf[5] = SP_ACTIVE_DOCUMENT->getHeight().value("px"); _pointwdg->setTransform(transf); _pointwdg->setValue( *this ); _pointwdg->clearProgrammatically(); @@ -205,12 +201,12 @@ PointParamKnotHolderEntity::knot_click(guint state) } void -PointParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +PointParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { knoth = knotholder; PointParamKnotHolderEntity *e = new PointParamKnotHolderEntity(this); // TODO: can we ditch handleTip() etc. because we have access to handle_tip etc. itself??? - e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), knot_shape, knot_mode, knot_color); + e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), knot_shape, knot_mode, knot_color); knotholder->add(e); } diff --git a/src/live_effects/parameter/point.h b/src/live_effects/parameter/point.h index 62c6fb83d..e8cb66225 100644 --- a/src/live_effects/parameter/point.h +++ b/src/live_effects/parameter/point.h @@ -49,7 +49,7 @@ public: void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color); virtual bool providesKnotHolderEntities() const { return true; } - virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); friend class PointParamKnotHolderEntity; private: diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp index c61e8f9cb..7753d819d 100644 --- a/src/live_effects/parameter/powerstrokepointarray.cpp +++ b/src/live_effects/parameter/powerstrokepointarray.cpp @@ -248,11 +248,11 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state) } } -void PowerStrokePointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +void PowerStrokePointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { for (unsigned int i = 0; i < _vector.size(); ++i) { PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i); - e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, + e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."), knot_shape, knot_mode, knot_color); knotholder->add(e); diff --git a/src/live_effects/parameter/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h index 70b22e27e..56a609fa8 100644 --- a/src/live_effects/parameter/powerstrokepointarray.h +++ b/src/live_effects/parameter/powerstrokepointarray.h @@ -38,7 +38,7 @@ public: float median_width(); virtual bool providesKnotHolderEntities() const { return true; } - virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); void set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in); Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2() const { return last_pwd2; } diff --git a/src/live_effects/parameter/text.cpp b/src/live_effects/parameter/text.cpp index 0650b7e66..8cab68ad0 100644 --- a/src/live_effects/parameter/text.cpp +++ b/src/live_effects/parameter/text.cpp @@ -110,7 +110,10 @@ TextParam::param_readSVGValue(const gchar * strvalue) gchar * TextParam::param_getSVGValue() const { - return g_strdup(value.c_str()); + Inkscape::SVGOStringStream os; + os << value; + gchar * str = g_strdup(os.str().c_str()); + return str; } Gtk::Widget * diff --git a/src/live_effects/parameter/vector.cpp b/src/live_effects/parameter/vector.cpp index cfaa9e7e7..55b4d4b32 100644 --- a/src/live_effects/parameter/vector.cpp +++ b/src/live_effects/parameter/vector.cpp @@ -14,7 +14,6 @@ #include "svg/stringstream.h" #include "live_effects/effect.h" -#include "desktop.h" #include "verbs.h" namespace Inkscape { @@ -117,7 +116,7 @@ VectorParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point void VectorParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/) { - set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() ); + set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() ); } @@ -188,14 +187,14 @@ private: }; void -VectorParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) +VectorParam::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) { VectorParamKnotHolderEntity_Origin *origin_e = new VectorParamKnotHolderEntity_Origin(this); - origin_e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), ori_knot_shape, ori_knot_mode, ori_knot_color); + origin_e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), ori_knot_shape, ori_knot_mode, ori_knot_color); knotholder->add(origin_e); VectorParamKnotHolderEntity_Vector *vector_e = new VectorParamKnotHolderEntity_Vector(this); - vector_e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), vec_knot_shape, vec_knot_mode, vec_knot_color); + vector_e->create(NULL, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), vec_knot_shape, vec_knot_mode, vec_knot_color); knotholder->add(vector_e); } diff --git a/src/live_effects/parameter/vector.h b/src/live_effects/parameter/vector.h index 35ca04437..edee4ff4d 100644 --- a/src/live_effects/parameter/vector.h +++ b/src/live_effects/parameter/vector.h @@ -53,7 +53,7 @@ public: void set_oncanvas_color(guint32 color); virtual bool providesKnotHolderEntities() const { return true; } - virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item); + virtual void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item); private: VectorParam(const VectorParam&); diff --git a/src/main.cpp b/src/main.cpp index 95764aea9..605c1207e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -420,7 +420,7 @@ struct poptOption options[] = { {"export-pdf-version", 0, POPT_ARG_STRING, &sp_export_pdf_version, SP_ARG_EXPORT_PDF_VERSION, // TRANSLATORS: "--export-pdf-version" is an Inkscape command line option; see "inkscape --help" - N_("Export PDF to given version. (hint: make sure to input the exact string found in the PDF export dialog, e.g. \"PDF 1.4\" which is PDF-a conformant)"), + N_("Export PDF to given version. (hint: make sure to input a version found in the PDF export dialog, e.g. \"1.4\" which is PDF-a conformant)"), N_("PDF_VERSION")}, {"export-latex", 0, @@ -2159,7 +2159,7 @@ sp_process_args(poptContext ctx) } #endif // WITH_YAML case SP_ARG_VERSION: { - printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__); + printf("Inkscape %s\n", Inkscape::version_string); exit(0); break; } diff --git a/src/object-set.h b/src/object-set.h index 12f2cb101..7c224f640 100644 --- a/src/object-set.h +++ b/src/object-set.h @@ -33,6 +33,29 @@ #include "sp-item-group.h" #include "desktop.h" #include "document.h" +#include "verbs.h" + +enum BoolOpErrors { + DONE, + DONE_NO_PATH, + DONE_NO_ACTION, + ERR_TOO_LESS_PATHS_1, + ERR_TOO_LESS_PATHS_2, + ERR_NO_PATHS, + ERR_Z_ORDER +}; + +// boolean operation +enum bool_op +{ + bool_op_union, // A OR B + bool_op_inters, // A AND B + bool_op_diff, // A \ B + bool_op_symdiff, // A XOR B + bool_op_cut, // coupure (pleines) + bool_op_slice // coupure (contour) +}; +typedef enum bool_op BooleanOp; class SPBox3D; class Persp3D; @@ -376,7 +399,16 @@ public: void toCurves(bool skip_undo = false); void toLPEItems(); void pathReverse(); - + + // Boolean operations + // in splivarot.cpp + bool pathUnion(const bool skip_undo = false); + bool pathIntersect(const bool skip_undo = false); + bool pathDiff(const bool skip_undo = false); + bool pathSymDiff(const bool skip_undo = false); + bool pathCut(const bool skip_undo = false); + bool pathSlice(const bool skip_undo = false); + //Other path operations //in selection-chemistry.cpp void toMarker(bool apply = true); @@ -415,7 +447,7 @@ public: // various void getExportHints(Glib::ustring &filename, float *xdpi, float *ydpi); bool fitCanvas(bool with_margins, bool skip_undo = false); - + void swapFillStroke(); protected: virtual void _connectSignals(SPObject* object) {}; @@ -438,6 +470,9 @@ protected: std::list<SPBox3D *> _3dboxes; std::unordered_map<SPObject*, sigc::connection> _releaseConnections; +private: + BoolOpErrors pathBoolOp(bool_op bop, const bool skip_undo, const unsigned int verb = SP_VERB_NONE, const Glib::ustring description = ""); + }; typedef ObjectSet::SPItemRange SPItemRange; diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index a19ca9524..741f433f2 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -552,7 +552,8 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) return g_repr; } - + + SP_LPE_ITEM(item)->removeAllPathEffects(true); SPCurve *curve = NULL; { SPShape *shape = dynamic_cast<SPShape *>(item); diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 38196a58e..ed00092b2 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -74,8 +74,7 @@ static char const preferences_skeleton[] = " </group>\n" "\n" " <group id=\"tools\"\n" -" bounding_box=\"0\"\n" -" style=\"fill:none;stroke:black;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;\">\n" +" bounding_box=\"0\">\n" " <group id=\"shapes\" style=\"fill-rule:evenodd;\" selcue=\"1\" gradientdrag=\"1\">\n" " <eventcontext id=\"rect\" style=\"fill:blue;\" usecurrent=\"1\"/>\n" " <eventcontext id=\"3dbox\" style=\"stroke:none;stroke-linejoin:round;\" usecurrent=\"1\">\n" @@ -88,7 +87,7 @@ static char const preferences_skeleton[] = " </eventcontext>\n" " <eventcontext id=\"arc\" style=\"fill:red;\" end=\"0\" start=\"0\" usecurrent=\"1\"/>\n" " <eventcontext id=\"star\" magnitude=\"5\" style=\"fill:yellow;\" usecurrent=\"1\"/>\n" -" <eventcontext id=\"spiral\" style=\"fill:none;\" usecurrent=\"0\"/>\n" +" <eventcontext id=\"spiral\" style=\"fill:none;stroke:black\" expansion=\"1\" usecurrent=\"0\"/>\n" " </group>\n" " <group id=\"freehand\"\n" " style=\"fill:none;stroke:black;stroke-opacity:1;stroke-linejoin:miter;stroke-linecap:butt;\">\n" @@ -140,6 +139,7 @@ static char const preferences_skeleton[] = " <dash id=\"dash-1-12\" style=\"stroke-dasharray:1,12\"/>\n" " <dash id=\"dash-1-24\" style=\"stroke-dasharray:1,24\"/>\n" " <dash id=\"dash-1-48\" style=\"stroke-dasharray:1,48\"/>\n" +" <dash id=\"dash-empty\" style=\"stroke-dasharray:0 11\"/>\n" " <dash id=\"dash-2-1\" style=\"stroke-dasharray:2,1\"/>\n" " <dash id=\"dash-3-1\" style=\"stroke-dasharray:3,1\"/>\n" " <dash id=\"dash-4-1\" style=\"stroke-dasharray:4,1\"/>\n" diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index f8ab33ac4..67972cabb 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -97,6 +97,7 @@ SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; #include "live_effects/parameter/originalpath.h" #include "layer-manager.h" #include "object-set.h" +#include "svg/svg-color.h" // For clippath editing #include "ui/tools/node-tool.h" @@ -1242,7 +1243,6 @@ void ObjectSet::pasteStyle() } } - void ObjectSet::pastePathEffect() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); @@ -4169,6 +4169,75 @@ bool ObjectSet::fitCanvas(bool with_margins, bool skip_undo) } } +void ObjectSet::swapFillStroke() +{ + if (desktop() == NULL) { + return; + } + + SPIPaint *paint; + SPPaintServer *server; + Glib::ustring _paintserver_id; + + auto list= items(); + for (auto itemlist=list.begin();itemlist!=list.end();++itemlist) { + SPItem *item = *itemlist; + + SPCSSAttr *css = sp_repr_css_attr_new (); + + _paintserver_id.clear(); + paint = &(item->style->fill); + if (paint->set && paint->isNone()) + sp_repr_css_set_property (css, "stroke", "none"); + else if (paint->set && paint->isColor()) { + guint32 color = paint->value.color.toRGBA32(SP_SCALE24_TO_FLOAT (item->style->fill_opacity.value)); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), color); + sp_repr_css_set_property (css, "stroke", c); + } + else if (!paint->set) + sp_repr_css_unset_property (css, "stroke"); + else if (paint->set && paint->isPaintserver()) { + server = SP_STYLE_FILL_SERVER(item->style); + if (server) { + Inkscape::XML::Node *srepr = server->getRepr(); + _paintserver_id += "url(#"; + _paintserver_id += srepr->attribute("id"); + _paintserver_id += ")"; + sp_repr_css_set_property (css, "stroke", _paintserver_id.c_str()); + } + } + + _paintserver_id.clear(); + paint = &(item->style->stroke); + if (paint->set && paint->isNone()) + sp_repr_css_set_property (css, "fill", "none"); + else if (paint->set && paint->isColor()) { + guint32 color = paint->value.color.toRGBA32(SP_SCALE24_TO_FLOAT (item->style->stroke_opacity.value)); + gchar c[64]; + sp_svg_write_color (c, sizeof(c), color); + sp_repr_css_set_property (css, "fill", c); + } + else if (!paint->set) + sp_repr_css_unset_property (css, "fill"); + else if (paint->set && paint->isPaintserver()) { + server = SP_STYLE_STROKE_SERVER(item->style); + if (server) { + Inkscape::XML::Node *srepr = server->getRepr(); + _paintserver_id += "url(#"; + _paintserver_id += srepr->attribute("id"); + _paintserver_id += ")"; + sp_repr_css_set_property (css, "fill", _paintserver_id.c_str()); + } + } + + sp_desktop_apply_css_recursive(item, css, true); + sp_repr_css_attr_unref (css); + } + + DocumentUndo::done(document(), SP_VERB_EDIT_SWAP_FILL_STROKE, + _("Swap fill and stroke of an object")); +} /** * \param with_margins margins defined in the xml under <sodipodi:namedview> diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index 0d60aa5d8..ed1e2b504 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -32,11 +32,12 @@ #include "svg/svg.h" #include "svg/path-string.h" +#define SP_2PI (2 * M_PI) SPGenericEllipse::SPGenericEllipse() : SPShape() , start(0) - , end(M_2_PI) + , end(SP_2PI) , type(SP_GENERIC_ELLIPSE_UNDEFINED) , _closed(true) { @@ -527,7 +528,7 @@ void SPGenericEllipse::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, // Snap to the 4 quadrant points of the ellipse, but only if the arc // spans far enough to include them if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)) { - for (double angle = 0; angle < M_2_PI; angle += M_PI_2) { + for (double angle = 0; angle < SP_2PI; angle += M_PI_2) { if (Geom::AngleInterval(this->start, this->end, true).contains(angle)) { Geom::Point pt = this->getPointAtAngle(angle) * i2dt; p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)); @@ -662,7 +663,7 @@ bool SPGenericEllipse::_isSlice() const { Geom::AngleInterval a(this->start, this->end, true); - return !(Geom::are_near(a.extent(), 0) || Geom::are_near(a.extent(), M_2_PI)); + return !(Geom::are_near(a.extent(), 0) || Geom::are_near(a.extent(), SP_2PI)); } /* diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 746c7fa41..f02149cbb 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -820,6 +820,9 @@ has_units_set(SPGradient const *gr) SPGradient *SPGradient::getVector(bool force_vector) { SPGradient * src = chase_hrefs(this, has_stopsFN); + if (src == NULL) { + src = this; + } if (force_vector) { src = sp_gradient_ensure_vector_normalized(src); @@ -830,10 +833,9 @@ SPGradient *SPGradient::getVector(bool force_vector) SPGradient *SPGradient::getArray(bool force_vector) { SPGradient * src = chase_hrefs(this, has_patchesFN); - - // if (force_vector) { - // src = sp_gradient_ensure_vector_normalized(src); - // } + if (src == NULL) { + src = this; + } return src; } diff --git a/src/sp-image.cpp b/src/sp-image.cpp index aa1dbfe20..1961971cb 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -793,7 +793,7 @@ void sp_image_refresh_if_outdated( SPImage* image ) if ( image->href && image->pixbuf && image->pixbuf->modificationTime()) { // It *might* change - struct stat st; + GStatBuf st; memset(&st, 0, sizeof(st)); int val = 0; if (g_file_test (image->pixbuf->originalPath().c_str(), G_FILE_TEST_EXISTS)){ diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 96dcdbe30..7b2507b5e 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -584,12 +584,13 @@ sp_item_group_ungroup (SPGroup *group, std::vector<SPItem*> &children, bool do_d SPText * text = dynamic_cast<SPText *>(citem); if (text) { //this causes a change in text-on-path appearance when there is a non-conformal transform, see bug #1594565 - double scale = (ctrans.expansionX() + ctrans.expansionY()) / 2.0; SPTextPath * text_path = dynamic_cast<SPTextPath *>(text->firstChild()); if (!text_path) { nrepr->setAttribute("transform", affinestr); } else { - sp_recursive_scale_text_size(nrepr, scale); + // The following breaks roundtripping group -> ungroup + // double scale = (ctrans.expansionX() + ctrans.expansionY()) / 2.0; + // sp_recursive_scale_text_size(nrepr, scale); Geom::Affine ttrans = ctrans.inverse() * SP_ITEM(text)->transform * ctrans; gchar *affinestr = sp_svg_transform_write(ttrans); nrepr->setAttribute("transform", affinestr); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 36a9c3c9d..5d02020c6 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -121,6 +121,7 @@ void SPItem::setLocked(bool locked) { setAttribute("sodipodi:insensitive", ( locked ? "1" : NULL )); updateRepr(); + document->_emitModified(); } bool SPItem::isHidden() const { @@ -667,7 +668,7 @@ void SPItem::update(SPCtx* ctx, guint flags) { if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { for (SPItemView *v = display; v != NULL; v = v->next) { v->arenaitem->setOpacity(SP_SCALE24_TO_FLOAT(style->opacity.value)); - v->arenaitem->setAntialiasing(style->shape_rendering.computed != SP_CSS_SHAPE_RENDERING_CRISPEDGES); + v->arenaitem->setAntialiasing(style->shape_rendering.computed == SP_CSS_SHAPE_RENDERING_CRISPEDGES ? 0 : 2); v->arenaitem->setIsolation( style->isolation.value ); v->arenaitem->setBlendMode( style->mix_blend_mode.value ); v->arenaitem->setVisible(!isHidden()); diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 8f0713652..e2f61bfb5 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -125,7 +125,8 @@ void SPLPEItem::set(unsigned int key, gchar const* value) { { if (!value) { LivePathEffectObject *lpeobj = (*it)->lpeobject; - if (Inkscape::LivePathEffect::LPEMeasureLine * lpe = dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpeobj->get_lpe())) { + Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe(); + if (dynamic_cast<Inkscape::LivePathEffect::LPEMeasureLine *>(lpe)){ lpe->doOnRemove(this); } } @@ -289,22 +290,6 @@ sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write) if (!lpeitem->pathEffectsEnabled()) return; - // TODO: hack! this will be removed when path length measuring is reimplemented in a better way - PathEffectList lpelist = lpeitem->getEffectList(); - std::list<Inkscape::LivePathEffect::LPEObjectReference *>::iterator i; - for (i = lpelist.begin(); i != lpelist.end(); ++i) { - if ((*i)->lpeobject) { - Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe(); - if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) { - if (!lpe->isVisible()) { - // we manually disable text for LPEPathLength - // use static_cast, because we already checked for the right type above - static_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText(); - } - } - } - } - SPLPEItem *top = NULL; if (wholetree) { @@ -514,6 +499,21 @@ void SPLPEItem::removeCurrentPathEffect(bool keep_paths) */ void SPLPEItem::removeAllPathEffects(bool keep_paths) { + if (keep_paths) { + if (path_effect_list->empty()) { + return; + } + + for (PathEffectList::const_iterator it = path_effect_list->begin(); it != path_effect_list->end(); ++it) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + if (lpeobj) { + Inkscape::LivePathEffect::Effect * lpe = lpeobj->get_lpe(); + lpe->erase_extra_objects = false; + } + } + } + this->getRepr()->setAttribute("inkscape:path-effect", NULL); if (!keep_paths) { diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp index 208dac2bc..f192d0e44 100644 --- a/src/sp-mesh-array.cpp +++ b/src/sp-mesh-array.cpp @@ -38,6 +38,7 @@ */ #include <glibmm.h> +#include <set> // For color picking #include "display/drawing.h" @@ -1057,10 +1058,10 @@ void SPMeshNodeArray::write( SPMeshGradient *mg ) { break; case 'z': case 'Z': - std::cout << "SPMeshNodeArray::write(): bad path type" << path_type << std::endl; + std::cerr << "SPMeshNodeArray::write(): bad path type" << path_type << std::endl; break; default: - std::cout << "SPMeshNodeArray::write(): unhandled path type" << path_type << std::endl; + std::cerr << "SPMeshNodeArray::write(): unhandled path type" << path_type << std::endl; } stop->setAttribute("path", is.str().c_str()); // std::cout << "SPMeshNodeArray::write: path: " << is.str().c_str() << std::endl; @@ -1120,11 +1121,11 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb if( !bbox ) { // Set default size to bounding box if size not given. - std::cout << "SPMeshNodeArray::create(): bbox empty" << std::endl; + std::cerr << "SPMeshNodeArray::create(): bbox empty" << std::endl; bbox = item->geometricBounds(); if( !bbox ) { - std::cout << "SPMeshNodeArray::create: ERROR: No bounding box!" << std::endl; + std::cerr << "SPMeshNodeArray::create: ERROR: No bounding box!" << std::endl; return; } } @@ -1151,7 +1152,14 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb // Get default color SPColor color = default_color( item ); - + + // Set some corners to white so we can see the mesh. + SPColor white( 1.0, 1.0, 1.0 ); + if (color == white) { + // If default color is white, set other color to black. + white = SPColor( 0.0, 0.0, 0.0 ); + } + // Get preferences Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint prows = prefs->getInt("/tools/mesh/mesh_rows", 1); @@ -1190,6 +1198,9 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb ry = arc->ry.computed; start = arc->start; end = arc->end; + if( end <= start ) { + end += 2.0 * M_PI; + } } // std::cout << " start: " << start << " end: " << end << std::endl; @@ -1236,7 +1247,7 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb for( guint k = 0; k < 4; ++k ) { patch.setPathType( k, 'l' ); - patch.setColor( k, color ); + patch.setColor( k, (i+k)%2 ? color : white ); patch.setOpacity( k, 1.0 ); } patch.setPathType( 0, 'c' ); @@ -1293,7 +1304,7 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb patch.setPathType( i, 'c' ); - patch.setColor( i, color ); + patch.setColor( i, i%2 ? color : white ); patch.setOpacity( i, 1.0 ); } @@ -1334,7 +1345,7 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb for( guint s = 0; s < 4; ++s ) { patch.setPathType( s, 'l' ); - patch.setColor( s, color ); + patch.setColor( s, (i+s)%2 ? color : white ); patch.setOpacity( s, 1.0 ); } @@ -1362,10 +1373,10 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb for( guint s = 0; s < 4; ++s ) { patch0.setPathType( s, 'l' ); - patch0.setColor( s, color ); + patch0.setColor( s, s%2 ? color : white ); patch0.setOpacity( s, 1.0 ); patch1.setPathType( s, 'l' ); - patch1.setColor( s, color ); + patch1.setColor( s, s%2 ? white : color ); patch1.setOpacity( s, 1.0 ); } @@ -1415,7 +1426,7 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb // Corner node->node_type = MG_NODE_TYPE_CORNER; node->set = true; - node->color = color; + node->color = (i+j)%2 ? color : white; node->opacity = 1.0; } else { @@ -1467,11 +1478,8 @@ void SPMeshNodeArray::clear() { delete nodes[i][j]; } } - for( guint i = 0; i < nodes.size(); ++i ) { - nodes[i].clear(); - } - nodes.clear(); } + nodes.clear(); }; @@ -2018,7 +2026,7 @@ guint SPMeshNodeArray::side_arc( std::vector<guint> corners ) { { case 'L': case 'l': - std::cout << "SPMeshNodeArray::arc_sides: Can't convert straight lines to arcs."; + std::cerr << "SPMeshNodeArray::side_arc: Can't convert straight lines to arcs." << std::endl; break; case 'C': @@ -2044,15 +2052,15 @@ guint SPMeshNodeArray::side_arc( std::vector<guint> corners ) { ++arced; } else { - std::cout << "SPMeshNodeArray::arc_sides: No crossing, can't turn into arc." << std::endl; + std::cerr << "SPMeshNodeArray::side_arc: No crossing, can't turn into arc." << std::endl; } } else { - std::cout << "SPMeshNodeArray::arc_sides: Handles parallel, can't turn into arc." << std::endl; + std::cerr << "SPMeshNodeArray::side_arc: Handles parallel, can't turn into arc." << std::endl; } break; } default: - std::cout << "SPMeshNodeArray::arc_sides: Invalid path type: " << n[1]->path_type << std::endl; + std::cerr << "SPMeshNodeArray::side_arc: Invalid path type: " << n[1]->path_type << std::endl; } } } @@ -2415,11 +2423,74 @@ guint SPMeshNodeArray::color_pick( std::vector<guint> icorners, SPItem* item ) { } /** + Splits selected rows and/or columns in half (according to the path 't' parameter). + Input is a list of selected corner draggable indices. +*/ +guint SPMeshNodeArray::insert( std::vector<guint> corners ) { + + guint inserted = 0; + + if( corners.size() < 2 ) return 0; + + std::set<guint> columns; + std::set<guint> rows; + + for( guint i = 0; i < corners.size()-1; ++i ) { + for( guint j = i+1; j < corners.size(); ++j ) { + + // This works as all corners have indices and they + // are numbered in order by row and column (and + // the node array is rectangular). + + guint c1 = corners[i]; + guint c2 = corners[j]; + if (c2 < c1) { + c1 = corners[j]; + c2 = corners[i]; + } + + // Number of corners in a row of patches. + guint ncorners = patch_columns() + 1; + + guint crow1 = c1 / ncorners; + guint crow2 = c2 / ncorners; + guint ccol1 = c1 % ncorners; + guint ccol2 = c2 % ncorners; + + // Check for horizontal neighbors + if ( crow1 == crow2 && (ccol2 - ccol1) == 1 ) { + columns.insert( ccol1 ); + } + + // Check for vertical neighbors + if ( ccol1 == ccol2 && (crow2 - crow1) == 1 ) { + rows.insert( crow1 ); + } + } + } + + // Iterate backwards so column/row numbers are not invalidated. + std::set<guint>::reverse_iterator rit; + for (rit=columns.rbegin(); rit != columns.rend(); ++rit) { + split_column( *rit, 0.5); + ++inserted; + } + for (rit=rows.rbegin(); rit != rows.rend(); ++rit) { + split_row( *rit, 0.5); + ++inserted; + } + + if( inserted > 0 ) built = false; + return inserted; +} + +/** Moves handles in response to a corner node move. p_old: orignal position of moved corner node. corner: the corner node moved (draggable index, i.e. point_i). selected: list of all corners selected (draggable indices). op: how other corners should be moved. + Corner node must already have been moved! */ void SPMeshNodeArray::update_handles( guint corner, std::vector< guint > /*selected*/, Geom::Point p_old, MeshNodeOperation /*op*/ ) { @@ -2704,6 +2775,11 @@ SPCurve * SPMeshNodeArray::outline_path() { SPCurve *outline = new SPCurve(); + if (nodes.empty() ) { + std::cerr << "SPMeshNodeArray::outline_path: empty array!" << std::endl; + return outline; + } + outline->moveto( nodes[0][0]->p ); int ncol = nodes[0].size(); @@ -2720,7 +2796,7 @@ SPCurve * SPMeshNodeArray::outline_path() { } // Bottom (right to left) - for (int i = 1; i < nrow; i += 3 ) { + for (int i = 1; i < ncol; i += 3 ) { outline->curveto( nodes[nrow-1][ncol-i-1]->p, nodes[nrow-1][ncol-i-2]->p, nodes[nrow-1][ncol-i-3]->p); } @@ -2743,6 +2819,43 @@ void SPMeshNodeArray::transform(Geom::Affine const &m) { } } +// Transform mesh to fill box. Return true if mesh transformed. +bool SPMeshNodeArray::fill_box(Geom::OptRect &box) { + + // If gradientTransfor is set (as happens when an object is transformed + // with the "optimized" preferences set true), we need to remove it. + if (mg->gradientTransform_set) { + Geom::Affine gt = mg->gradientTransform; + transform( gt ); + mg->gradientTransform_set = false; + mg->gradientTransform.setIdentity(); + } + + SPCurve *outline = outline_path(); + Geom::OptRect mesh_bbox = outline->get_pathvector().boundsExact(); + outline->unref(); + + if ((*mesh_bbox).width() == 0 || (*mesh_bbox).height() == 0) { + return false; + } + + double scale_x = (*box).width() /(*mesh_bbox).width() ; + double scale_y = (*box).height()/(*mesh_bbox).height(); + + Geom::Translate t1(-(*mesh_bbox).min()); + Geom::Scale scale(scale_x,scale_y); + Geom::Translate t2((*box).min()); + Geom::Affine trans = t1 * scale * t2; + if (!trans.isIdentity() ) { + transform(trans); + write( mg ); + mg->requestModified(SP_OBJECT_MODIFIED_FLAG); + return true; + } + + return false; +} + // Defined in gradient-chemistry.cpp guint32 average_color(guint32 c1, guint32 c2, gdouble p); diff --git a/src/sp-mesh-array.h b/src/sp-mesh-array.h index 2ba1bfd5d..df43638db 100644 --- a/src/sp-mesh-array.h +++ b/src/sp-mesh-array.h @@ -76,7 +76,8 @@ enum MeshCornerOperation { MG_CORNER_SIDE_ARC, MG_CORNER_TENSOR_TOGGLE, MG_CORNER_COLOR_SMOOTH, - MG_CORNER_COLOR_PICK + MG_CORNER_COLOR_PICK, + MG_CORNER_INSERT }; enum MeshNodeOperation { @@ -192,6 +193,7 @@ public: unsigned int tensor_toggle( std::vector< unsigned int > ); unsigned int color_smooth( std::vector< unsigned int > ); unsigned int color_pick( std::vector< unsigned int >, SPItem* ); + unsigned int insert( std::vector< unsigned int > ); // Update other nodes in response to a node move. void update_handles( unsigned int corner, std::vector< unsigned int > selected_corners, Geom::Point old_p, MeshNodeOperation op ); @@ -202,6 +204,9 @@ public: // Transform array void transform(Geom::Affine const &m); + // Transform mesh to fill box. Return true if not identity transform. + bool fill_box(Geom::OptRect &box); + // Find bounding box // Geom::OptRect findBoundingBox(); diff --git a/src/sp-object.cpp b/src/sp-object.cpp index e9c60fc7d..75f4657ef 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -660,7 +660,9 @@ void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) { object->readAttr("xml:space"); object->readAttr("inkscape:label"); object->readAttr("inkscape:collect"); - if(object->cloned) + if(object->cloned && (repr->attribute("id")) ) // The cases where this happens are when the "original" has no id. This happens + // if it is a SPString (a TextNode, e.g. in a <title>), or when importing + // stuff externally modified to have no id. object->clone_original = document->getObjectById(repr->attribute("id")); for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) { diff --git a/src/sp-path.cpp b/src/sp-path.cpp index a7119dd31..b593b7937 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -187,35 +187,35 @@ void SPPath::release() { void SPPath::set(unsigned int key, const gchar* value) { switch (key) { case SP_ATTR_INKSCAPE_ORIGINAL_D: - if (value) { - Geom::PathVector pv = sp_svg_read_pathv(value); - SPCurve *curve = new SPCurve(pv); - - if (curve) { - this->set_original_curve(curve, TRUE, true); - curve->unref(); - } - } else { - this->set_original_curve(NULL, TRUE, true); - } - - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (value) { + Geom::PathVector pv = sp_svg_read_pathv(value); + SPCurve *curve = new SPCurve(pv); + + if (curve) { + this->set_original_curve(curve, TRUE, true); + curve->unref(); + } + } else { + this->set_original_curve(NULL, TRUE, true); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_D: - if (value) { - Geom::PathVector pv = sp_svg_read_pathv(value); - SPCurve *curve = new SPCurve(pv); - - if (curve) { - this->setCurve(curve, TRUE); - curve->unref(); - } - } else { - this->setCurve(NULL, TRUE); - } - - this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + if (value) { + Geom::PathVector pv = sp_svg_read_pathv(value); + SPCurve *curve = new SPCurve(pv); + + if (curve) { + this->setCurve(curve, TRUE); + curve->unref(); + } + } else { + this->setCurve(NULL, TRUE); + } + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; case SP_PROP_MARKER: @@ -276,13 +276,13 @@ g_message("sp_path_write writes 'd' attribute"); } void SPPath::update(SPCtx *ctx, guint flags) { - if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { - flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; // since we change the description, it's not a "just translation" anymore - } + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + flags &= ~SP_OBJECT_USER_MODIFIED_FLAG_B; // since we change the description, it's not a "just translation" anymore + } - SPShape::update(ctx, flags); + SPShape::update(ctx, flags); - this->connEndPair.update(); + this->connEndPair.update(); } Geom::Affine SPPath::set_transform(Geom::Affine const &transform) { diff --git a/src/splivarot.cpp b/src/splivarot.cpp index 6b9fd5f83..531a48e44 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -47,96 +47,51 @@ #include "verbs.h" #include "2geom/svg-path-parser.h" // to get from SVG on boolean to Geom::Path -enum BoolOpErrors { - DONE, - DONE_NO_PATH, - DONE_NO_ACTION, - ERR_TOO_LESS_PATHS_1, - ERR_TOO_LESS_PATHS_2, - ERR_NO_PATHS, - ERR_Z_ORDER -}; - using Inkscape::DocumentUndo; bool Ancetre(Inkscape::XML::Node *a, Inkscape::XML::Node *who); -void sp_selected_path_boolop_ui(Inkscape::Selection *selection, SPDesktop *desktop, bool_op bop, - const unsigned int verb = SP_VERB_NONE, const Glib::ustring description = ""); -BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet *set, bool_op bop); void sp_selected_path_do_offset(SPDesktop *desktop, bool expand, double prefOffset); void sp_selected_path_create_offset_object(SPDesktop *desktop, int expand, bool updating); -void -sp_selected_path_union(Inkscape::Selection *selection, SPDesktop *desktop) -{ - sp_selected_path_boolop_ui(selection, desktop, bool_op_union, SP_VERB_SELECTION_UNION, _("Union")); -} - -void -sp_selected_path_union_skip_undo(Inkscape::ObjectSet *set) -{ - sp_selected_path_boolop(set, bool_op_union); -} - -void -sp_selected_path_intersect(Inkscape::Selection *selection, SPDesktop *desktop) -{ - sp_selected_path_boolop_ui(selection, desktop, bool_op_inters, SP_VERB_SELECTION_INTERSECT, _("Intersection")); -} - -void -sp_selected_path_intersect_skip_undo(Inkscape::ObjectSet *set) -{ - sp_selected_path_boolop(set, bool_op_inters); -} - -void -sp_selected_path_diff(Inkscape::Selection *selection, SPDesktop *desktop) -{ - sp_selected_path_boolop_ui(selection, desktop, bool_op_diff, SP_VERB_SELECTION_DIFF, _("Difference")); -} - -void -sp_selected_path_diff_skip_undo(Inkscape::ObjectSet *set) -{ - sp_selected_path_boolop(set, bool_op_diff); +bool Inkscape::ObjectSet::pathUnion(const bool skip_undo) { + BoolOpErrors result = pathBoolOp(bool_op_union, skip_undo, SP_VERB_SELECTION_UNION, _("Union")); + return DONE == result; } -void -sp_selected_path_symdiff(Inkscape::Selection *selection, SPDesktop *desktop) -{ - sp_selected_path_boolop_ui(selection, desktop, bool_op_symdiff, SP_VERB_SELECTION_SYMDIFF, _("Exclusion")); -} - -void -sp_selected_path_symdiff_skip_undo(Inkscape::ObjectSet *set) +bool +Inkscape::ObjectSet::pathIntersect(const bool skip_undo) { - sp_selected_path_boolop(set, bool_op_symdiff); + BoolOpErrors result = pathBoolOp(bool_op_inters, skip_undo, SP_VERB_SELECTION_INTERSECT, _("Intersection")); + return DONE == result; } -void -sp_selected_path_cut(Inkscape::Selection *selection, SPDesktop *desktop) +bool +Inkscape::ObjectSet::pathDiff(const bool skip_undo) { - sp_selected_path_boolop_ui(selection, desktop, bool_op_cut, SP_VERB_SELECTION_CUT, _("Division")); + BoolOpErrors result = pathBoolOp(bool_op_diff, skip_undo, SP_VERB_SELECTION_DIFF, _("Difference")); + return DONE == result; } -void -sp_selected_path_cut_skip_undo(Inkscape::ObjectSet *set) +bool +Inkscape::ObjectSet::pathSymDiff(const bool skip_undo) { - sp_selected_path_boolop(set, bool_op_cut); + BoolOpErrors result = pathBoolOp(bool_op_symdiff, skip_undo, SP_VERB_SELECTION_SYMDIFF, _("Exclusion")); + return DONE == result; } -void -sp_selected_path_slice(Inkscape::Selection *selection, SPDesktop *desktop) +bool +Inkscape::ObjectSet::pathCut(const bool skip_undo) { - sp_selected_path_boolop_ui(selection, desktop, bool_op_slice, SP_VERB_SELECTION_SLICE, _("Cut path")); + BoolOpErrors result = pathBoolOp(bool_op_cut, skip_undo, SP_VERB_SELECTION_CUT, _("Division")); + return DONE == result; } -void -sp_selected_path_slice_skip_undo(Inkscape::ObjectSet *set) +bool +Inkscape::ObjectSet::pathSlice(const bool skip_undo) { - sp_selected_path_boolop(set, bool_op_slice); + BoolOpErrors result = pathBoolOp(bool_op_slice, skip_undo, SP_VERB_SELECTION_SLICE, _("Cut path")); + return DONE == result; } // helper for printing error messages, regardless of whether we have a GUI or not @@ -352,10 +307,36 @@ Geom::PathVector pathliv_to_pathvector(Path *pathliv){ // boolean operations on the desktop // take the source paths from the file, do the operation, delete the originals and add the results -BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) +BoolOpErrors Inkscape::ObjectSet::pathBoolOp(bool_op bop, const bool skip_undo, const unsigned int verb, const Glib::ustring description) { - SPDocument *doc = set->desktop()->getDocument(); - std::vector<SPItem*> il(set->items().begin(), set->items().end()); + if (nullptr != desktop() && !skip_undo) { + SPDocument *doc = desktop()->getDocument(); + BoolOpErrors returnCode = ObjectSet::pathBoolOp(bop, true); + switch(returnCode) { + case ERR_TOO_LESS_PATHS_1: + boolop_display_error_message(desktop(), _("Select <b>at least 1 path</b> to perform a boolean union.")); + break; + case ERR_TOO_LESS_PATHS_2: + boolop_display_error_message(desktop(), _("Select <b>at least 2 paths</b> to perform a boolean operation.")); + break; + case ERR_NO_PATHS: + boolop_display_error_message(desktop(), _("One of the objects is <b>not a path</b>, cannot perform boolean operation.")); + break; + case ERR_Z_ORDER: + boolop_display_error_message(desktop(), _("Unable to determine the <b>z-order</b> of the objects selected for difference, XOR, division, or path cut.")); + break; + case DONE_NO_PATH: + DocumentUndo::done(doc, SP_VERB_NONE, description); + break; + case DONE: + DocumentUndo::done(doc, verb, description); + break; + } + return returnCode; + } + + SPDocument *doc = document(); + std::vector<SPItem*> il(items().begin(), items().end()); // allow union on a single object for the purpose of removing self overlapse (svn log, revision 13334) if (il.size() < 2 && bop != bool_op_union) { @@ -687,7 +668,7 @@ BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ (*l)->deleteObject(); } - set->clear(); + clear(); delete res; return DONE_NO_PATH; @@ -703,7 +684,7 @@ BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) } } else { // find out the bottom object - std::vector<Inkscape::XML::Node*> sorted(set->xmlNodes().begin(), set->xmlNodes().end()); + std::vector<Inkscape::XML::Node*> sorted(xmlNodes().begin(), xmlNodes().end()); sort(sorted.begin(),sorted.end(),sp_repr_compare_position_bool); @@ -731,7 +712,7 @@ BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) gchar *title = source->title(); gchar *desc = source->desc(); // remove source paths - set->clear(); + clear(); for (std::vector<SPItem*>::const_iterator l = il.begin(); l != il.end(); l++){ // if this is the bottommost object, if (!strcmp(reinterpret_cast<SPObject *>(*l)->getRepr()->attribute("id"), id)) { @@ -810,7 +791,7 @@ BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) // move to the saved position repr->setPosition(pos > 0 ? pos : 0); - set->add(doc->getObjectByRepr(repr)); + add(doc->getObjectByRepr(repr)); Inkscape::GC::release(repr); delete resPath[i]; @@ -845,7 +826,7 @@ BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) } repr->setPosition(pos > 0 ? pos : 0); - set->add(doc->getObjectByRepr(repr)); + add(doc->getObjectByRepr(repr)); Inkscape::GC::release(repr); } @@ -858,36 +839,6 @@ BoolOpErrors sp_selected_path_boolop(Inkscape::ObjectSet * set, bool_op bop) return DONE; } -void sp_selected_path_boolop_ui(Inkscape::Selection *selection, SPDesktop *desktop, bool_op bop, const unsigned int verb, - const Glib::ustring description) -{ - SPDocument *doc = selection->desktop()->getDocument(); - BoolOpErrors returnCode = sp_selected_path_boolop(selection, bop); - switch(returnCode) { - case ERR_TOO_LESS_PATHS_1: - boolop_display_error_message(desktop, _("Select <b>at least 1 path</b> to perform a boolean union.")); - return; - case ERR_TOO_LESS_PATHS_2: - boolop_display_error_message(desktop, _("Select <b>at least 2 paths</b> to perform a boolean operation.")); - return; - case ERR_NO_PATHS: - boolop_display_error_message(desktop, _("One of the objects is <b>not a path</b>, cannot perform boolean operation.")); - return; - case ERR_Z_ORDER: - boolop_display_error_message(desktop, _("Unable to determine the <b>z-order</b> of the objects selected for difference, XOR, division, or path cut.")); - return; - case DONE_NO_PATH: - DocumentUndo::done(doc, SP_VERB_NONE, description); - return; - case DONE: - DocumentUndo::done(doc, verb, description); - return; - case DONE_NO_ACTION: - default: - return; - } -} - static void sp_selected_path_outline_add_marker( SPObject *marker_object, Geom::Affine marker_transform, Geom::Scale stroke_scale, Geom::Affine transform, diff --git a/src/splivarot.h b/src/splivarot.h index 6adc05946..17dd4f66b 100644 --- a/src/splivarot.h +++ b/src/splivarot.h @@ -10,6 +10,7 @@ #include <2geom/forward.h> #include <2geom/path.h> #include "livarot/Path.h" +#include "object-set.h" class SPCurve; class SPDesktop; @@ -20,26 +21,6 @@ namespace Inkscape { class ObjectSet; } -// boolean operations -// work on the current selection -// selection has 2 contain exactly 2 items - -// UPDATE: these signatures have been modified so they may work in -// command-line mode, i.e. without a desktop. If a desktop is not -// provided (desktop == NULL), error messages will be shown on stderr. -void sp_selected_path_union (Inkscape::Selection *selection, SPDesktop *desktop); -void sp_selected_path_union_skip_undo (Inkscape::ObjectSet *set); -void sp_selected_path_intersect (Inkscape::Selection *selection, SPDesktop *desktop); -void sp_selected_path_intersect_skip_undo (Inkscape::ObjectSet *set); -void sp_selected_path_diff (Inkscape::Selection *selection, SPDesktop *desktop); -void sp_selected_path_diff_skip_undo (Inkscape::ObjectSet *set); -void sp_selected_path_symdiff (Inkscape::Selection *selection, SPDesktop *desktop); -void sp_selected_path_symdiff_skip_undo (Inkscape::ObjectSet *set); -void sp_selected_path_cut (Inkscape::Selection *selection, SPDesktop *desktop); -void sp_selected_path_cut_skip_undo (Inkscape::ObjectSet *set); -void sp_selected_path_slice (Inkscape::Selection *selection, SPDesktop *desktop); -void sp_selected_path_slice_skip_undo (Inkscape::ObjectSet *set); - // offset/inset of a curve // takes the fill-rule in consideration // offset amount is the stroke-width of the curve diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index a8e708597..c1e824c1e 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -643,7 +643,6 @@ Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop) return svgd; } - /** * Iterate over a list of items and copy them to the clipboard. */ @@ -689,25 +688,6 @@ void ClipboardManagerImpl::_copySelection(ObjectSet *selection) else obj_copy = _copyNode(obj, _doc, _clipnode); - // For lpe items, copy lpe stack if applicable - SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item); - if (lpeitem) { - Inkscape::SVGOStringStream os; - if (lpeitem->hasPathEffect()) { - for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) - { - LivePathEffectObject *lpeobj = (*it)->lpeobject; - if (lpeobj) { - Inkscape::XML::Node * lpeobjcopy = _copyNode(lpeobj->getRepr(), _doc, _defs); - gchar *new_conflict_id = sp_object_get_unique_id(lpeobj, lpeobj->getAttribute("id")); - lpeobjcopy->setAttribute("id", new_conflict_id); - g_free(new_conflict_id); - os << "#" << lpeobjcopy->attribute("id") << ";"; - } - } - } - obj_copy->setAttribute("inkscape:path-effect", os.str().c_str()); - } // copy complete inherited style SPCSSAttr *css = sp_repr_css_attr_inherited(obj, "style"); sp_repr_css_set(obj_copy, css, "style"); @@ -739,6 +719,13 @@ void ClipboardManagerImpl::_copySelection(ObjectSet *selection) sp_repr_css_set(_clipnode, style, "style"); sp_repr_css_attr_unref(style); } + // copy path effect from the first path + if (object) { + gchar const *effect =object->getRepr()->attribute("inkscape:path-effect"); + if (effect) { + _clipnode->setAttribute("inkscape:path-effect", effect); + } + } } Geom::OptRect size = selection->visualBounds(); @@ -841,6 +828,19 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) } } + // For lpe items, copy lpe stack if applicable + SPLPEItem *lpeitem = dynamic_cast<SPLPEItem *>(item); + if (lpeitem) { + if (lpeitem->hasPathEffect()) { + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it){ + LivePathEffectObject *lpeobj = (*it)->lpeobject; + if (lpeobj) { + _copyNode(lpeobj->getRepr(), _doc, _defs); + } + } + } + } + // recurse for(auto& o: item->children) { SPItem *childItem = dynamic_cast<SPItem *>(&o); diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp index 552500fe8..8f0545e96 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -27,7 +27,6 @@ #include <gtkmm/scrolledwindow.h> #include <gtkmm/aspectframe.h> #include <gtkmm/textview.h> -#include <gtkmm/stock.h> #include "path-prefix.h" #include "document.h" @@ -39,8 +38,6 @@ #include "inkscape-version.h" - - namespace Inkscape { namespace UI { namespace Dialog { @@ -102,7 +99,7 @@ AboutBox::AboutBox() : Gtk::Dialog(_("About Inkscape")) { tabs->show_all(); - add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + add_button(_("_Close"), Gtk::RESPONSE_CLOSE); set_default_response(Gtk::RESPONSE_CLOSE); Gtk::Label *label=new Gtk::Label(); diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 236832beb..1c88fc849 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -614,7 +614,7 @@ private : } }; -// instantiae the private static member +// instantiate the private static member boost::optional<Geom::Point> ActionExchangePositions::center; class ActionUnclump : public Action { @@ -928,7 +928,7 @@ AlignAndDistribute::AlignAndDistribute() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - //Instanciate the align buttons + //Instantiate the align buttons addAlignButton(INKSCAPE_ICON("align-horizontal-right-to-anchor"), _("Align right edges of objects to the left edge of the anchor"), 0, 0); diff --git a/src/ui/dialog/calligraphic-profile-rename.cpp b/src/ui/dialog/calligraphic-profile-rename.cpp index 50dae0fd8..36570e769 100644 --- a/src/ui/dialog/calligraphic-profile-rename.cpp +++ b/src/ui/dialog/calligraphic-profile-rename.cpp @@ -15,7 +15,6 @@ #include "calligraphic-profile-rename.h" #include <glibmm/i18n.h> -#include <gtkmm/stock.h> #include <gtkmm/grid.h> #include "desktop.h" @@ -46,17 +45,17 @@ CalligraphicProfileRename::CalligraphicProfileRename() : mainVBox->pack_start(*_layout_table, false, false, 4); // Buttons - _close_button.set_use_stock(true); - _close_button.set_label(Gtk::Stock::CANCEL.id); + _close_button.set_use_underline(); + _close_button.set_label(_("_Cancel")); _close_button.set_can_default(); _delete_button.set_use_underline(true); - _delete_button.set_label(_("Delete")); + _delete_button.set_label(_("_Delete")); _delete_button.set_can_default(); _delete_button.set_visible(false); _apply_button.set_use_underline(true); - _apply_button.set_label(_("Save")); + _apply_button.set_label(_("_Save")); _apply_button.set_can_default(); _close_button.signal_clicked() diff --git a/src/ui/dialog/clonetiler.cpp b/src/ui/dialog/clonetiler.cpp index 8e9d3dbbf..3cdcf594a 100644 --- a/src/ui/dialog/clonetiler.cpp +++ b/src/ui/dialog/clonetiler.cpp @@ -936,7 +936,7 @@ CloneTiler::CloneTiler () : auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, VB_MARGIN); gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); gtk_box_pack_start (GTK_BOX (mainbox), hb, FALSE, FALSE, 0); - GtkWidget *l = gtk_label_new(_("")); + GtkWidget *l = gtk_label_new(""); gtk_label_set_markup (GTK_LABEL(l), _("Apply to tiled clones:")); gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 0); } diff --git a/src/ui/dialog/dialog.cpp b/src/ui/dialog/dialog.cpp index d0b618c65..27a6e55d9 100644 --- a/src/ui/dialog/dialog.cpp +++ b/src/ui/dialog/dialog.cpp @@ -19,7 +19,6 @@ #include "dialog-manager.h" #include <gtkmm/dialog.h> -#include <gtkmm/stock.h> #include <gdk/gdkkeysyms.h> #include "inkscape.h" diff --git a/src/ui/dialog/dock-behavior.cpp b/src/ui/dialog/dock-behavior.cpp index ec630c08f..02955b9a8 100644 --- a/src/ui/dialog/dock-behavior.cpp +++ b/src/ui/dialog/dock-behavior.cpp @@ -25,9 +25,6 @@ #include "dialog.h" #include "ui/dialog-events.h" -#include <gtkmm/invisible.h> -#include <gtkmm/stock.h> - namespace Inkscape { namespace UI { namespace Dialog { diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 053549b73..8593223c1 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -44,9 +44,6 @@ #include "color-profile.h" #endif // defined(HAVE_LIBLCMS1) || defined(HAVE_LIBLCMS2) -#include <gtkmm/stock.h> -#include <gtkmm/imagemenuitem.h> - #include "ui/icon-names.h" using std::pair; @@ -558,7 +555,7 @@ void DocumentProperties::linked_profiles_list_button_release(GdkEventButton* eve void DocumentProperties::cms_create_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem) { - Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + Gtk::MenuItem* mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); _EmbProfContextMenu.append(*mi); mi->signal_activate().connect(rem); mi->show(); @@ -568,7 +565,7 @@ void DocumentProperties::cms_create_popup_menu(Gtk::Widget& parent, sigc::slot<v void DocumentProperties::external_create_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem) { - Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + Gtk::MenuItem* mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); _ExternalScriptsContextMenu.append(*mi); mi->signal_activate().connect(rem); mi->show(); @@ -577,7 +574,7 @@ void DocumentProperties::external_create_popup_menu(Gtk::Widget& parent, sigc::s void DocumentProperties::embedded_create_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem) { - Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + Gtk::MenuItem* mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); _EmbeddedScriptsContextMenu.append(*mi); mi->signal_activate().connect(rem); mi->show(); diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index c38e798ec..b7207fd50 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -26,7 +26,6 @@ #include <gtkmm/entry.h> #include <gtkmm/grid.h> #include <gtkmm/spinbutton.h> -#include <gtkmm/stock.h> #ifdef WITH_GNOME_VFS # include <libgnomevfs/gnome-vfs-init.h> // gnome_vfs_initialized @@ -142,7 +141,7 @@ Export::Export (void) : units_label(_("Units:")), filename_box(false, 5), browse_label(_("_Export As..."), 1), - browse_image(Gtk::StockID(Gtk::Stock::INDEX), Gtk::ICON_SIZE_BUTTON), + browse_image(), batch_box(false, 5), batch_export(_("B_atch export all selected objects"), _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)")), interlacing(_("Use interlacing"),_("Enables ADAM7 interlacing for PNG output. This results in slightly heavier images, but big images will look better sooner when loading the file")), @@ -152,12 +151,12 @@ Export::Export (void) : zlib_compression(), pHYs_label(_("pHYs dpi")), pHYs_sb(pHYs_adj, 1.0, 2), + antialiasing_label(_("Antialiasing")), + antialiasing_cb(), hide_box(false, 5), - hide_export(_("Hide a_ll except selected"), _("In the exported image, hide all objects except those that are selected")), + hide_export(_("Hide all except selected"), _("In the exported image, hide all objects except those that are selected")), closeWhenDone(_("Close when complete"), _("Once the export completes, close this dialog")), button_box(false, 3), - export_label(_("_Export"), 1), - export_image(Gtk::StockID(Gtk::Stock::APPLY), Gtk::ICON_SIZE_BUTTON), _prog(), prog_dlg(NULL), interrupted(false), @@ -290,6 +289,7 @@ Export::Export (void) : filename_box.pack_start (filename_entry, true, true, 0); Gtk::HBox* browser_im_label = new Gtk::HBox(false, 3); + browse_image.set_from_icon_name("folder", Gtk::ICON_SIZE_BUTTON); browser_im_label->pack_start(browse_image); browser_im_label->pack_start(browse_label); browse_button.add(*browser_im_label); @@ -318,11 +318,8 @@ Export::Export (void) : /* Export Button row */ button_box.set_border_width(3); - Gtk::HBox* export_image_label = new Gtk::HBox(false, 3); - export_image_label->pack_start(export_image); - export_image_label->pack_start(export_label); - - export_button.add(*export_image_label); + export_button.set_label(_("_Export")); + export_button.set_use_underline(); export_button.set_tooltip_text (_("Export the bitmap file with these settings")); button_box.pack_start(closeWhenDone, true, true, 0 ); @@ -345,6 +342,10 @@ Export::Export (void) : pHYs_sb.set_width_chars(7); pHYs_sb.set_tooltip_text( _("Will force-set the physical dpi for the png file. Set this to 72 if you're planning to work on your png with Photoshop") ); zlib_compression.set_hexpand(); + const char* const antialising_list[] = {"CAIRO_ANTIALIAS_NONE","CAIRO_ANTIALIAS_FAST","CAIRO_ANTIALIAS_GOOD (default)","CAIRO_ANTIALIAS_BEST"}; + for(int i=0; i<4; ++i) + antialiasing_cb.append(antialising_list[i]); + antialiasing_cb.set_active_text(antialising_list[2]); auto table = new Gtk::Grid(); gtk_container_add(GTK_CONTAINER(expander.gobj()), (GtkWidget*)(table->gobj())); table->attach(interlacing,0,0,1,1); @@ -354,6 +355,8 @@ Export::Export (void) : table->attach(zlib_compression,1,2,1,1); table->attach(pHYs_label,0,3,1,1); table->attach(pHYs_sb,1,3,1,1); + table->attach(antialiasing_label,0,4,1,1); + table->attach(antialiasing_cb,1,4,1,1); table->show(); /* Main dialog */ @@ -906,7 +909,7 @@ Gtk::Dialog * Export::create_progress_dialog (Glib::ustring progress_text) { auto CA = dlg->get_content_area(); CA->pack_start(*prg, FALSE, FALSE, 4); - Gtk::Button* btn = dlg->add_button (Gtk::Stock::CANCEL,Gtk::RESPONSE_CANCEL ); + Gtk::Button* btn = dlg->add_button (_("_Cancel"),Gtk::RESPONSE_CANCEL ); btn->signal_clicked().connect( sigc::mem_fun(*this, &Export::onProgressCancel) ); dlg->signal_delete_event().connect( sigc::mem_fun(*this, &Export::onProgressDelete) ); @@ -991,6 +994,7 @@ void Export::onExport () int bitdepths[] = {1,2,4,8,16,8,16,8,16,8,16}; int color_type = colortypes[bitdepth_cb.get_active_row_number()] ; int bit_depth = bitdepths[bitdepth_cb.get_active_row_number()] ; + int antialiasing = antialiasing_cb.get_active_row_number(); if (batch_export.get_active ()) { @@ -1060,7 +1064,7 @@ void Export::onExport () onProgressCallback, (void*)prog_dlg, TRUE, // overwrite without asking hide ? selected : x, - do_interlace, color_type, bit_depth, zlib + do_interlace, color_type, bit_depth, zlib, antialiasing )) { gchar * error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile); @@ -1156,7 +1160,7 @@ void Export::onExport () onProgressCallback, (void*)prog_dlg, FALSE, hide ? selected : x, - do_interlace, color_type, bit_depth, zlib + do_interlace, color_type, bit_depth, zlib, antialiasing ); if (status == EXPORT_ERROR) { gchar * safeFile = Inkscape::IO::sanitizeString(path.c_str()); diff --git a/src/ui/dialog/export.h b/src/ui/dialog/export.h index 112f42312..1f8e87dbc 100644 --- a/src/ui/dialog/export.h +++ b/src/ui/dialog/export.h @@ -327,12 +327,12 @@ private: Gtk::Label pHYs_label; Glib::RefPtr<Gtk::Adjustment> pHYs_adj; Gtk::SpinButton pHYs_sb; + Gtk::Label antialiasing_label; + Gtk::ComboBoxText antialiasing_cb; /* Export Button widgets */ Gtk::HBox button_box; Gtk::Button export_button; - Gtk::Label export_label; - Gtk::Image export_image; Gtk::ProgressBar _prog; diff --git a/src/ui/dialog/filedialogimpl-gtkmm.cpp b/src/ui/dialog/filedialogimpl-gtkmm.cpp index e8c1bf723..92e9ce834 100644 --- a/src/ui/dialog/filedialogimpl-gtkmm.cpp +++ b/src/ui/dialog/filedialogimpl-gtkmm.cpp @@ -35,7 +35,6 @@ #endif #include <gtkmm/expander.h> -#include <gtkmm/stock.h> #include <glibmm/convert.h> #include <glibmm/fileutils.h> @@ -555,7 +554,7 @@ bool SVGPreview::set(Glib::ustring &fileName, int dialogType) Glib::ustring fileNameUtf8 = Glib::filename_to_utf8(fileName); gchar *fName = const_cast<gchar *>( fileNameUtf8.c_str()); // const-cast probably not necessary? (not necessary on Windows version of stat()) - struct stat info; + GStatBuf info; if (g_stat(fName, &info)) // stat returns 0 upon success { g_warning("SVGPreview::set() : %s : %s", fName, strerror(errno)); @@ -735,8 +734,8 @@ FileOpenDialogImplGtk::FileOpenDialogImplGtk(Gtk::Window &parentWindow, const Gl //###### Add the file types menu createFilterMenu(); - add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - set_default(*add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK)); + add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); + set_default(*add_button(_("_Open"), Gtk::RESPONSE_OK)); //###### Allow easy access to our examples folder if (Inkscape::IO::file_test(INKSCAPE_EXAMPLESDIR, G_FILE_TEST_EXISTS) && @@ -1050,8 +1049,8 @@ FileSaveDialogImplGtk::FileSaveDialogImplGtk(Gtk::Window &parentWindow, const Gl // if (extension == NULL) // checkbox.set_sensitive(FALSE); - add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - set_default(*add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK)); + add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); + set_default(*add_button(_("_Save"), Gtk::RESPONSE_OK)); show_all_children(); } @@ -1597,8 +1596,8 @@ FileExportDialogImpl::FileExportDialogImpl(Gtk::Window &parentWindow, const Glib // if (extension == NULL) // checkbox.set_sensitive(FALSE); - add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - set_default(*add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK)); + add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); + set_default(*add_button(_("_Save"), Gtk::RESPONSE_OK)); show_all_children(); } diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index f657e1b76..80dc08ccf 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -64,7 +64,6 @@ #include "selection-chemistry.h" #include <gtkmm/colorbutton.h> -#include <gtkmm/stock.h> #include <gdkmm/general.h> #include <gtkmm/checkbutton.h> @@ -1305,7 +1304,7 @@ static Glib::RefPtr<Gtk::Menu> create_popup_menu(Gtk::Widget& parent, sigc::slot mi->show(); menu->append(*mi); - mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); menu->append(*mi); mi->signal_activate().connect(rem); mi->show(); @@ -1319,7 +1318,7 @@ FilterEffectsDialog::FilterModifier::FilterModifier(FilterEffectsDialog& d) : _desktop(NULL), _deskTrack(), _dialog(d), - _add(Gtk::Stock::NEW), + _add(_("_New"), true), _observer(new Inkscape::XML::SignalObserver) { Gtk::ScrolledWindow* sw = Gtk::manage(new Gtk::ScrolledWindow); diff --git a/src/ui/dialog/floating-behavior.cpp b/src/ui/dialog/floating-behavior.cpp index fa8e11dfd..9abad3e7b 100644 --- a/src/ui/dialog/floating-behavior.cpp +++ b/src/ui/dialog/floating-behavior.cpp @@ -15,7 +15,6 @@ #endif #include <gtkmm/dialog.h> -#include <gtkmm/stock.h> #include <glibmm/main.h> #include "floating-behavior.h" diff --git a/src/ui/dialog/grid-arrange-tab.cpp b/src/ui/dialog/grid-arrange-tab.cpp index 5872393ae..9ec8d3148 100644 --- a/src/ui/dialog/grid-arrange-tab.cpp +++ b/src/ui/dialog/grid-arrange-tab.cpp @@ -1,25 +1,24 @@ -/* - * A simple dialog for creating grid type arrangements of selected objects - * - * Authors: - * Bob Jamison ( based off trace dialog) - * John Cliff - * Other dudes from The Inkscape Organization - * Abhishek Sharma - * Declara Denis - * - * Copyright (C) 2004 Bob Jamison - * Copyright (C) 2004 John Cliff - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ -//#define DEBUG_GRID_ARRANGE 1 + /* + * A simple dialog for creating grid type arrangements of selected objects + * + * Authors: + * Bob Jamison ( based off trace dialog) + * John Cliff + * Other dudes from The Inkscape Organization + * Abhishek Sharma + * Declara Denis + * + * Copyright (C) 2004 Bob Jamison + * Copyright (C) 2004 John Cliff + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + //#define DEBUG_GRID_ARRANGE 1 #include "ui/dialog/grid-arrange-tab.h" #include <glibmm/i18n.h> #include <gtkmm/grid.h> -#include <gtkmm/stock.h> #include <2geom/transforms.h> @@ -31,193 +30,193 @@ #include "document-undo.h" #include "widgets/icon.h" #include "desktop.h" -//#include "sp-item-transform.h" FIXME + //#include "sp-item-transform.h" FIXME #include "ui/dialog/tile.h" // for Inkscape::UI::Dialog::ArrangeDialog -/* - * Sort items by their x co-ordinates, taking account of y (keeps rows intact) - * - * <0 *elem1 goes before *elem2 - * 0 *elem1 == *elem2 - * >0 *elem1 goes after *elem2 - */ -static bool sp_compare_x_position(SPItem *first, SPItem *second) -{ - using Geom::X; - using Geom::Y; + /* + * Sort items by their x co-ordinates, taking account of y (keeps rows intact) + * + * <0 *elem1 goes before *elem2 + * 0 *elem1 == *elem2 + * >0 *elem1 goes after *elem2 + */ + static bool sp_compare_x_position(SPItem *first, SPItem *second) + { + using Geom::X; + using Geom::Y; - Geom::OptRect a = first->documentVisualBounds(); - Geom::OptRect b = second->documentVisualBounds(); + Geom::OptRect a = first->documentVisualBounds(); + Geom::OptRect b = second->documentVisualBounds(); - if ( !a || !b ) { - // FIXME? - return false; - } + if ( !a || !b ) { + // FIXME? + return false; + } - double const a_height = a->dimensions()[Y]; - double const b_height = b->dimensions()[Y]; + double const a_height = a->dimensions()[Y]; + double const b_height = b->dimensions()[Y]; - bool a_in_b_vert = false; - if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) { - a_in_b_vert = true; - } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) { - a_in_b_vert = true; - } else if (b->min()[Y] == a->min()[Y]) { - a_in_b_vert = true; - } else { - a_in_b_vert = false; - } + bool a_in_b_vert = false; + if ((a->min()[Y] < b->min()[Y] + 0.1) && (a->min()[Y] > b->min()[Y] - b_height)) { + a_in_b_vert = true; + } else if ((b->min()[Y] < a->min()[Y] + 0.1) && (b->min()[Y] > a->min()[Y] - a_height)) { + a_in_b_vert = true; + } else if (b->min()[Y] == a->min()[Y]) { + a_in_b_vert = true; + } else { + a_in_b_vert = false; + } - if (!a_in_b_vert) { // a and b are not in the same row - return (a->min()[Y] < b->min()[Y]); + if (!a_in_b_vert) { // a and b are not in the same row + return (a->min()[Y] < b->min()[Y]); + } + return (a->min()[X] < b->min()[X]); } - return (a->min()[X] < b->min()[X]); -} -/* - * Sort items by their y co-ordinates. - */ -static bool sp_compare_y_position(SPItem *first, SPItem *second) -{ - Geom::OptRect a = first->documentVisualBounds(); - Geom::OptRect b = second->documentVisualBounds(); + /* + * Sort items by their y co-ordinates. + */ + static bool sp_compare_y_position(SPItem *first, SPItem *second) + { + Geom::OptRect a = first->documentVisualBounds(); + Geom::OptRect b = second->documentVisualBounds(); - if ( !a || !b ) { - // FIXME? - return false; - } + if ( !a || !b ) { + // FIXME? + return false; + } + + if (a->min()[Geom::Y] > b->min()[Geom::Y]) { + return false; + } + if (a->min()[Geom::Y] < b->min()[Geom::Y]) { + return true; + } - if (a->min()[Geom::Y] > b->min()[Geom::Y]) { return false; } - if (a->min()[Geom::Y] < b->min()[Geom::Y]) { - return true; - } - - return false; -} -namespace Inkscape { -namespace UI { -namespace Dialog { + namespace Inkscape { + namespace UI { + namespace Dialog { -//######################################################################### -//## E V E N T S -//######################################################################### - -/* - * - * This arranges the selection in a grid pattern. - * - */ + //######################################################################### + //## E V E N T S + //######################################################################### -void GridArrangeTab::arrange() -{ + /* + * + * This arranges the selection in a grid pattern. + * + */ - int cnt,row_cnt,col_cnt,a,row,col; - double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y; - double total_col_width,total_row_height; - col_width = 0; - row_height = 0; - total_col_width=0; - total_row_height=0; + void GridArrangeTab::arrange() + { - // check for correct numbers in the row- and col-spinners - on_col_spinbutton_changed(); - on_row_spinbutton_changed(); + int cnt,row_cnt,col_cnt,a,row,col; + double grid_left,grid_top,col_width,row_height,paddingx,paddingy,width, height, new_x, new_y; + double total_col_width,total_row_height; + col_width = 0; + row_height = 0; + total_col_width=0; + total_row_height=0; - // set padding to manual values - paddingx = XPadding.getValue("px"); - paddingy = YPadding.getValue("px"); + // check for correct numbers in the row- and col-spinners + on_col_spinbutton_changed(); + on_row_spinbutton_changed(); - std::vector<double> row_heights; - std::vector<double> col_widths; - std::vector<double> row_ys; - std::vector<double> col_xs; + // set padding to manual values + paddingx = XPadding.getValue("px"); + paddingy = YPadding.getValue("px"); - int NoOfCols = NoOfColsSpinner.get_value_as_int(); - int NoOfRows = NoOfRowsSpinner.get_value_as_int(); + std::vector<double> row_heights; + std::vector<double> col_widths; + std::vector<double> row_ys; + std::vector<double> col_xs; - width = 0; - for (a=0;a<NoOfCols; a++){ - col_widths.push_back(width); - } + int NoOfCols = NoOfColsSpinner.get_value_as_int(); + int NoOfRows = NoOfRowsSpinner.get_value_as_int(); - height = 0; - for (a=0;a<NoOfRows; a++){ - row_heights.push_back(height); - } - grid_left = 99999; - grid_top = 99999; + width = 0; + for (a=0;a<NoOfCols; a++){ + col_widths.push_back(width); + } - SPDesktop *desktop = Parent->getDesktop(); - desktop->getDocument()->ensureUpToDate(); + height = 0; + for (a=0;a<NoOfRows; a++){ + row_heights.push_back(height); + } + grid_left = 99999; + grid_top = 99999; - Inkscape::Selection *selection = desktop->getSelection(); - std::vector<SPItem*> items; - if (selection) { - items.insert(items.end(), selection->items().begin(), selection->items().end()); - } + SPDesktop *desktop = Parent->getDesktop(); + desktop->getDocument()->ensureUpToDate(); - for(std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end(); ++i){ - SPItem *item = *i; - Geom::OptRect b = item->documentVisualBounds(); - if (!b) { - continue; + Inkscape::Selection *selection = desktop->getSelection(); + std::vector<SPItem*> items; + if (selection) { + items.insert(items.end(), selection->items().begin(), selection->items().end()); } - width = b->dimensions()[Geom::X]; - height = b->dimensions()[Geom::Y]; + for(std::vector<SPItem*>::const_iterator i = items.begin();i!=items.end(); ++i){ + SPItem *item = *i; + Geom::OptRect b = item->documentVisualBounds(); + if (!b) { + continue; + } + + width = b->dimensions()[Geom::X]; + height = b->dimensions()[Geom::Y]; - if (b->min()[Geom::X] < grid_left) { - grid_left = b->min()[Geom::X]; - } - if (b->min()[Geom::Y] < grid_top) { - grid_top = b->min()[Geom::Y]; - } - if (width > col_width) { - col_width = width; - } - if (height > row_height) { - row_height = height; + if (b->min()[Geom::X] < grid_left) { + grid_left = b->min()[Geom::X]; + } + if (b->min()[Geom::Y] < grid_top) { + grid_top = b->min()[Geom::Y]; + } + if (width > col_width) { + col_width = width; + } + if (height > row_height) { + row_height = height; + } } - } - // require the sorting done before we can calculate row heights etc. + // require the sorting done before we can calculate row heights etc. - g_return_if_fail(selection); - std::vector<SPItem*> sorted(selection->items().begin(), selection->items().end()); - sort(sorted.begin(),sorted.end(),sp_compare_y_position); - sort(sorted.begin(),sorted.end(),sp_compare_x_position); + g_return_if_fail(selection); + std::vector<SPItem*> sorted(selection->items().begin(), selection->items().end()); + sort(sorted.begin(),sorted.end(),sp_compare_y_position); + sort(sorted.begin(),sorted.end(),sp_compare_x_position); - // Calculate individual Row and Column sizes if necessary + // Calculate individual Row and Column sizes if necessary - cnt=0; - const std::vector<SPItem*> sizes(sorted); - for (std::vector<SPItem*>::const_iterator i = sizes.begin();i!=sizes.end(); ++i) { - SPItem *item = *i; - Geom::OptRect b = item->documentVisualBounds(); - if (b) { - width = b->dimensions()[Geom::X]; - height = b->dimensions()[Geom::Y]; - if (width > col_widths[(cnt % NoOfCols)]) { - col_widths[(cnt % NoOfCols)] = width; + cnt=0; + const std::vector<SPItem*> sizes(sorted); + for (std::vector<SPItem*>::const_iterator i = sizes.begin();i!=sizes.end(); ++i) { + SPItem *item = *i; + Geom::OptRect b = item->documentVisualBounds(); + if (b) { + width = b->dimensions()[Geom::X]; + height = b->dimensions()[Geom::Y]; + if (width > col_widths[(cnt % NoOfCols)]) { + col_widths[(cnt % NoOfCols)] = width; + } + if (height > row_heights[(cnt / NoOfCols)]) { + row_heights[(cnt / NoOfCols)] = height; + } } - if (height > row_heights[(cnt / NoOfCols)]) { - row_heights[(cnt / NoOfCols)] = height; - } - } - cnt++; - } + cnt++; + } - /// Make sure the top and left of the grid dont move by compensating for align values. + /// Make sure the top and left of the grid dont move by compensating for align values. if (RowHeightButton.get_active()){ grid_top = grid_top - (((row_height - row_heights[0]) / 2)*(VertAlign)); } diff --git a/src/ui/dialog/guides.cpp b/src/ui/dialog/guides.cpp index 8d6ca4143..9c8b14953 100644 --- a/src/ui/dialog/guides.cpp +++ b/src/ui/dialog/guides.cpp @@ -32,8 +32,6 @@ #include "message-context.h" #include "verbs.h" -#include <gtkmm/stock.h> - namespace Inkscape { namespace UI { namespace Dialogs { @@ -158,9 +156,9 @@ void GuidelinePropertiesDialog::_response(gint response) void GuidelinePropertiesDialog::_setup() { set_title(_("Guideline")); - add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK); - add_button(Gtk::Stock::DELETE, -12); - add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + add_button(_("_OK"), Gtk::RESPONSE_OK); + add_button(_("_Delete"), -12); + add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL); auto mainVBox = get_content_area(); _layout_table.set_row_spacing(4); diff --git a/src/ui/dialog/icon-preview.cpp b/src/ui/dialog/icon-preview.cpp index a4fcc9947..19050fb1d 100644 --- a/src/ui/dialog/icon-preview.cpp +++ b/src/ui/dialog/icon-preview.cpp @@ -27,7 +27,6 @@ #include <gtkmm/alignment.h> #include <gtkmm/checkbutton.h> #include <gtkmm/frame.h> -#include <gtkmm/stock.h> #include "ui/widget/frame.h" #include "desktop.h" diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index f9540c989..c5e8a6de6 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -534,7 +534,7 @@ void InkscapePreferences::initPageUI() _("Hebrew (he)"), _("Hindi (hi)"), _("Hungarian (hu)"), _("Icelandic (is)"), _("Indonesian (id)"), _("Irish (ga)"), _("Italian (it)"), _("Japanese (ja)"), - _("Kannada (kn)"), _("Kashmiri in Peso-Arabic script (ks@aran)"), _("Kashmiri in Devanagari script (ks@deva)"), _("Khmer (km)"), _("Kinyarwanda (rw)"), _("Konkani (kok)"), _("Konkani in Latin script (kok@latin)"), _("Korean (ko)"), + _("Kannada (kn)"), _("Kashmiri in Perso-Arabic script (ks@aran)"), _("Kashmiri in Devanagari script (ks@deva)"), _("Khmer (km)"), _("Kinyarwanda (rw)"), _("Konkani (kok)"), _("Konkani in Latin script (kok@latin)"), _("Korean (ko)"), _("Latvian (lv)"), _("Lithuanian (lt)"), _("Macedonian (mk)"), _("Maithili (mai)"), _("Malayalam (ml)"), _("Manipuri (mni)"), _("Manipuri in Bengali script (mni@beng)"), _("Marathi (mr)"), _("Mongolian (mn)"), _("Nepali (ne)"), _("Norwegian BokmÃ¥l (nb)"), _("Norwegian Nynorsk (nn)"), diff --git a/src/ui/dialog/knot-properties.cpp b/src/ui/dialog/knot-properties.cpp index 133ed6f4c..954fe2a66 100644 --- a/src/ui/dialog/knot-properties.cpp +++ b/src/ui/dialog/knot-properties.cpp @@ -14,11 +14,14 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif + #include "ui/dialog/knot-properties.h" + #include <boost/lexical_cast.hpp> #include <glibmm/i18n.h> +#include <glibmm/main.h> #include "inkscape.h" #include "util/units.h" #include "desktop.h" @@ -35,7 +38,10 @@ namespace UI { namespace Dialogs { KnotPropertiesDialog::KnotPropertiesDialog() -: _desktop(NULL), _knotpoint(NULL), _position_visible(false) + : _desktop(NULL), + _knotpoint(NULL), + _position_visible(false), + _close_button(_("_Close"), true) { Gtk::Box *mainVBox = get_vbox(); @@ -68,8 +74,6 @@ KnotPropertiesDialog::KnotPropertiesDialog() mainVBox->pack_start(_layout_table, true, true, 4); // Buttons - _close_button.set_use_stock(true); - _close_button.set_label(Gtk::Stock::CANCEL.id); _close_button.set_can_default(); _apply_button.set_use_underline(true); diff --git a/src/ui/dialog/knot-properties.h b/src/ui/dialog/knot-properties.h index fd87df03d..f6157168f 100644 --- a/src/ui/dialog/knot-properties.h +++ b/src/ui/dialog/knot-properties.h @@ -12,11 +12,10 @@ #ifndef INKSCAPE_DIALOG_KNOT_PROPERTIES_H #define INKSCAPE_DIALOG_KNOT_PROPERTIES_H -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <gtkmm.h> +#include <gtkmm/dialog.h> +#include <gtkmm/label.h> +#include <gtkmm/spinbutton.h> +#include <gtkmm/table.h> #include <2geom/point.h> #include "knot.h" #include "ui/tools/measure-tool.h" @@ -40,7 +39,7 @@ class KnotPropertiesDialog : public Gtk::Dialog { protected: SPDesktop *_desktop; - SPKnot *_knotpoint; + SPKnot *_knotpoint; Gtk::Label _knot_x_label; Gtk::SpinButton _knot_x_entry; diff --git a/src/ui/dialog/layer-properties.cpp b/src/ui/dialog/layer-properties.cpp index f28f8336a..c8c42ef90 100644 --- a/src/ui/dialog/layer-properties.cpp +++ b/src/ui/dialog/layer-properties.cpp @@ -14,7 +14,6 @@ */ #include "layer-properties.h" -#include <gtkmm/stock.h> #include <glibmm/i18n.h> #include <glibmm/main.h> @@ -36,7 +35,11 @@ namespace UI { namespace Dialogs { LayerPropertiesDialog::LayerPropertiesDialog() -: _strategy(NULL), _desktop(NULL), _layer(NULL), _position_visible(false) + : _strategy(NULL), + _desktop(NULL), + _layer(NULL), + _position_visible(false), + _close_button(_("_Cancel"), true) { auto mainVBox = get_content_area(); _layout_table.set_row_spacing(4); @@ -59,8 +62,6 @@ LayerPropertiesDialog::LayerPropertiesDialog() mainVBox->pack_start(_layout_table, true, true, 4); // Buttons - _close_button.set_use_stock(true); - _close_button.set_label(Gtk::Stock::CANCEL.id); _close_button.set_can_default(); _apply_button.set_use_underline(true); diff --git a/src/ui/dialog/livepatheffect-add.cpp b/src/ui/dialog/livepatheffect-add.cpp index 917e48ac6..cada915c9 100644 --- a/src/ui/dialog/livepatheffect-add.cpp +++ b/src/ui/dialog/livepatheffect-add.cpp @@ -14,7 +14,6 @@ #include "livepatheffect-add.h" #include <glibmm/i18n.h> -#include <gtkmm/stock.h> #include "desktop.h" @@ -23,8 +22,8 @@ namespace UI { namespace Dialog { LivePathEffectAdd::LivePathEffectAdd() : - add_button(Gtk::Stock::ADD), - close_button(Gtk::Stock::CANCEL), + add_button(_("_Add"), true), + close_button(_("_Cancel"), true), converter(Inkscape::LivePathEffect::LPETypeConverter), applied(false) { @@ -68,7 +67,6 @@ LivePathEffectAdd::LivePathEffectAdd() : /** * Buttons */ - close_button.set_use_stock(true); //close_button.set_can_default(); add_button.set_use_underline(true); add_button.set_can_default(); diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp index 9bd4c093e..e5becdb5c 100644 --- a/src/ui/dialog/livepatheffect-editor.cpp +++ b/src/ui/dialog/livepatheffect-editor.cpp @@ -17,7 +17,6 @@ #endif #include "livepatheffect-editor.h" -#include <gtkmm/stock.h> #include "desktop.h" @@ -539,9 +538,11 @@ void LivePathEffectEditor::onDown() void LivePathEffectEditor::on_effect_selection_changed() { Glib::RefPtr<Gtk::TreeSelection> sel = effectlist_view.get_selection(); - if (sel->count_selected_rows () == 0) + if (sel->count_selected_rows () == 0) { + button_remove.set_sensitive(false); return; - + } + button_remove.set_sensitive(true); Gtk::TreeModel::iterator it = sel->get_selected(); LivePathEffect::LPEObjectReference * lperef = (*it)[columns.lperef]; diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp index d33ee758d..d6ecae5a9 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp @@ -27,7 +27,10 @@ namespace UI { namespace Dialogs { FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() - : _desktop(NULL), _knotpoint(NULL), _position_visible(false) + : _desktop(NULL), + _knotpoint(NULL), + _position_visible(false), + _close_button(_("_Cancel"), true) { Gtk::Box *mainVBox = get_vbox(); mainVBox->set_homogeneous(false); @@ -76,8 +79,6 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() mainVBox->pack_start(_fillet_chamfer_type_inverse_chamfer, true, true, 4); // Buttons - _close_button.set_use_stock(true); - _close_button.set_label(Gtk::Stock::CANCEL.id); _close_button.set_can_default(); _apply_button.set_use_underline(true); @@ -264,6 +265,4 @@ void FilletChamferPropertiesDialog::_set_desktop(SPDesktop *desktop) fill-column:99 End: */ -// vim: -// filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 -// : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 diff --git a/src/ui/dialog/lpe-powerstroke-properties.cpp b/src/ui/dialog/lpe-powerstroke-properties.cpp index d5b3bb30d..ca10721db 100644 --- a/src/ui/dialog/lpe-powerstroke-properties.cpp +++ b/src/ui/dialog/lpe-powerstroke-properties.cpp @@ -33,7 +33,10 @@ namespace UI { namespace Dialogs { PowerstrokePropertiesDialog::PowerstrokePropertiesDialog() -: _desktop(NULL), _knotpoint(NULL), _position_visible(false) + : _desktop(NULL), + _knotpoint(NULL), + _position_visible(false), + _close_button(_("_Cancel"), true) { Gtk::Box *mainVBox = get_vbox(); @@ -66,8 +69,6 @@ PowerstrokePropertiesDialog::PowerstrokePropertiesDialog() mainVBox->pack_start(_layout_table, true, true, 4); // Buttons - _close_button.set_use_stock(true); - _close_button.set_label(Gtk::Stock::CANCEL.id); _close_button.set_can_default(); _apply_button.set_use_underline(true); diff --git a/src/ui/dialog/objects.cpp b/src/ui/dialog/objects.cpp index 446af4ccb..c7c696cdc 100644 --- a/src/ui/dialog/objects.cpp +++ b/src/ui/dialog/objects.cpp @@ -15,7 +15,6 @@ #include "objects.h" #include <gtkmm/icontheme.h> -#include <gtkmm/stock.h> #include <gtkmm/imagemenuitem.h> #include <gtkmm/separatormenuitem.h> #include <glibmm/main.h> diff --git a/src/ui/dialog/ocaldialogs.cpp b/src/ui/dialog/ocaldialogs.cpp index 878b68d55..72a2814ed 100644 --- a/src/ui/dialog/ocaldialogs.cpp +++ b/src/ui/dialog/ocaldialogs.cpp @@ -27,7 +27,6 @@ #include <gtkmm/notebook.h> #include <gtkmm/spinner.h> -#include <gtkmm/stock.h> #include <gdkmm/general.h> #include <libxml/tree.h> @@ -463,7 +462,8 @@ bool PreviewWidget::_on_draw(const Cairo::RefPtr<Cairo::Context>& cr) StatusWidget::StatusWidget() : Gtk::HBox(false, 6) { - image = new Gtk::Image(Gtk::Stock::DIALOG_ERROR, Gtk::ICON_SIZE_MENU); + image = new Gtk::Image(); + image->set_from_icon_name("dialog-error", Gtk::ICON_SIZE_MENU); spinner = new Gtk::Spinner(); label = new Gtk::Label(); @@ -488,7 +488,7 @@ void StatusWidget::set_info(Glib::ustring text) spinner->hide(); image->show(); label->show(); - image->set(Gtk::Stock::DIALOG_INFO, Gtk::ICON_SIZE_MENU); + image->set_from_icon_name("dialog-information", Gtk::ICON_SIZE_MENU); label->set_text(text); } @@ -497,7 +497,7 @@ void StatusWidget::set_error(Glib::ustring text) spinner->hide(); image->show(); label->show(); - image->set(Gtk::Stock::DIALOG_ERROR, Gtk::ICON_SIZE_MENU); + image->set_from_icon_name("dialog-error", Gtk::ICON_SIZE_MENU); label->set_text(text); } @@ -1086,7 +1086,7 @@ ImportDialog::ImportDialog(Gtk::Window& parent_window, FileDialogType file_types Gtk::ScrolledWindow* scrolledwindow_preview = new Gtk::ScrolledWindow(); preview_files = new PreviewWidget(); /// Add the buttons in the bottom of the dialog - button_cancel = new Gtk::Button(Gtk::Stock::CANCEL); + button_cancel = new Gtk::Button(_("_Cancel"), true); button_close = new Gtk::Button(_("Close")); button_import = new Gtk::Button(_("Import")); list_results = new SearchResultList(RESULTS_COLUMN_LENGTH); diff --git a/src/ui/dialog/panel-dialog.h b/src/ui/dialog/panel-dialog.h index 39110f47a..5919fcaeb 100644 --- a/src/ui/dialog/panel-dialog.h +++ b/src/ui/dialog/panel-dialog.h @@ -15,8 +15,8 @@ # include <config.h> #endif +#include <glibmm/i18n.h> #include <gtkmm/dialog.h> -#include <gtkmm/stock.h> #include "verbs.h" #include "dialog.h" @@ -165,7 +165,7 @@ PanelDialog<B>::PanelDialog(Widget::Panel &panel, char const *prefs_path, int co panel.addResponseButton(apply_label, Gtk::RESPONSE_APPLY); panel.setDefaultResponse(Gtk::RESPONSE_APPLY); } - panel.addResponseButton(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + panel.addResponseButton(_("_Close"), Gtk::RESPONSE_CLOSE); } show_all_children(); @@ -214,7 +214,7 @@ PanelDialog<Behavior::FloatingBehavior>::PanelDialog(UI::Widget::Panel &panel, c panel.addResponseButton(apply_label, Gtk::RESPONSE_APPLY); panel.setDefaultResponse(Gtk::RESPONSE_APPLY); } - panel.addResponseButton(Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + panel.addResponseButton(_("_Close"), Gtk::RESPONSE_CLOSE); } show_all_children(); diff --git a/src/ui/dialog/pixelartdialog.cpp b/src/ui/dialog/pixelartdialog.cpp index b838b0842..3e6617cfe 100644 --- a/src/ui/dialog/pixelartdialog.cpp +++ b/src/ui/dialog/pixelartdialog.cpp @@ -28,7 +28,6 @@ #include "pixelartdialog.h" #include <gtkmm/radiobutton.h> -#include <gtkmm/stock.h> #include <gtkmm/messagedialog.h> #include <glibmm/i18n.h> @@ -291,12 +290,12 @@ PixelArtDialogImpl::PixelArtDialogImpl() : mainResetButton ->set_tooltip_text(_("Reset all settings to defaults")); //## The OK button - mainCancelButton = addResponseButton(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL); + mainCancelButton = addResponseButton(_("_Stop"), GTK_RESPONSE_CANCEL); if (mainCancelButton) { mainCancelButton->set_tooltip_text(_("Abort a trace in progress")); mainCancelButton->set_sensitive(false); } - mainOkButton = addResponseButton(Gtk::Stock::OK, GTK_RESPONSE_OK); + mainOkButton = addResponseButton(_("_OK"), GTK_RESPONSE_OK); mainOkButton->set_tooltip_text(_("Execute the trace")); contents->pack_start(buttonsHBox); diff --git a/src/ui/dialog/svg-fonts-dialog.cpp b/src/ui/dialog/svg-fonts-dialog.cpp index e55a32bd5..791677807 100644 --- a/src/ui/dialog/svg-fonts-dialog.cpp +++ b/src/ui/dialog/svg-fonts-dialog.cpp @@ -20,7 +20,6 @@ #include "document-undo.h" #include <gtkmm/notebook.h> #include <gtkmm/scale.h> -#include <gtkmm/stock.h> #include <gtkmm/imagemenuitem.h> #include <message-stack.h> #include "selection.h" @@ -299,7 +298,7 @@ void SvgFontsDialog::fonts_list_button_release(GdkEventButton* event) void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem) { - Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + auto mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); _GlyphsContextMenu.append(*mi); mi->signal_activate().connect(rem); mi->show(); @@ -308,7 +307,7 @@ void SvgFontsDialog::create_glyphs_popup_menu(Gtk::Widget& parent, sigc::slot<vo void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem) { - Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + auto mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); _KerningPairsContextMenu.append(*mi); mi->signal_activate().connect(rem); mi->show(); @@ -317,7 +316,7 @@ void SvgFontsDialog::create_kerning_pairs_popup_menu(Gtk::Widget& parent, sigc:: void SvgFontsDialog::create_fonts_popup_menu(Gtk::Widget& parent, sigc::slot<void> rem) { - Gtk::MenuItem* mi = Gtk::manage(new Gtk::ImageMenuItem(Gtk::Stock::REMOVE)); + auto mi = Gtk::manage(new Gtk::MenuItem(_("_Remove"), true)); _FontsContextMenu.append(*mi); mi->signal_activate().connect(rem); mi->show(); @@ -986,7 +985,8 @@ void SvgFontsDialog::add_font(){ } SvgFontsDialog::SvgFontsDialog() - : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), _add(Gtk::Stock::NEW) + : UI::Widget::Panel("", "/dialogs/svgfonts", SP_VERB_DIALOG_SVG_FONTS), + _add(_("_New"), true) { kerning_slider = Gtk::manage(new Gtk::Scale(Gtk::ORIENTATION_HORIZONTAL)); _add.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::add_font)); diff --git a/src/ui/dialog/text-edit.cpp b/src/ui/dialog/text-edit.cpp index a38085c85..94f307828 100644 --- a/src/ui/dialog/text-edit.cpp +++ b/src/ui/dialog/text-edit.cpp @@ -29,7 +29,6 @@ extern "C" { } #endif -#include <gtkmm/stock.h> #include <libnrtype/font-lister.h> #include "helper/window.h" @@ -64,8 +63,8 @@ TextEdit::TextEdit() text_label(_("_Text"), true), vari_label(_("_Variants"), true), setasdefault_button(_("Set as _default")), - close_button(Gtk::Stock::CLOSE), - apply_button(Gtk::Stock::APPLY), + close_button(_("_Close"), true), + apply_button(_("_Apply"), true), desktop(NULL), deskTrack(), selectChangedConn(), diff --git a/src/ui/dialog/text-edit.h b/src/ui/dialog/text-edit.h index e974874d2..1fc0099fb 100644 --- a/src/ui/dialog/text-edit.h +++ b/src/ui/dialog/text-edit.h @@ -22,6 +22,7 @@ # include <config.h> #endif +#include <gtkmm/alignment.h> #include <gtkmm/box.h> #include <gtkmm/notebook.h> #include <gtkmm/button.h> diff --git a/src/ui/dialog/tracedialog.cpp b/src/ui/dialog/tracedialog.cpp index 1c0cb5708..a1d4d88b8 100644 --- a/src/ui/dialog/tracedialog.cpp +++ b/src/ui/dialog/tracedialog.cpp @@ -21,7 +21,6 @@ #include <gtkmm/radiobutton.h> #include "ui/widget/spinbutton.h" #include "ui/widget/frame.h" -#include <gtkmm/stock.h> #include <glibmm/i18n.h> @@ -813,12 +812,12 @@ TraceDialogImpl::TraceDialogImpl() : mainResetButton ->set_tooltip_text(_("Reset all settings to defaults")); //## The OK button - mainCancelButton = addResponseButton(Gtk::Stock::STOP, GTK_RESPONSE_CANCEL); + mainCancelButton = addResponseButton(_("_Stop"), GTK_RESPONSE_CANCEL); if (mainCancelButton) { mainCancelButton->set_tooltip_text(_("Abort a trace in progress")); mainCancelButton->set_sensitive(false); } - mainOkButton = addResponseButton(Gtk::Stock::OK, GTK_RESPONSE_OK); + mainOkButton = addResponseButton(_("_OK"), GTK_RESPONSE_OK); mainOkButton->set_tooltip_text(_("Execute the trace")); show_all_children(); diff --git a/src/ui/dialog/transformation.cpp b/src/ui/dialog/transformation.cpp index 5ec47b0dc..d209a450c 100644 --- a/src/ui/dialog/transformation.cpp +++ b/src/ui/dialog/transformation.cpp @@ -16,7 +16,6 @@ #endif #include <gtkmm/dialog.h> -#include <gtkmm/stock.h> #include <2geom/transforms.h> #include "document.h" @@ -134,14 +133,14 @@ Transformation::Transformation() updateSelection(PAGE_MOVE, _getSelection()); - resetButton = addResponseButton(Gtk::Stock::CLEAR, 0); + resetButton = addResponseButton(_("_Clear"), 0); if (resetButton) { resetButton->set_tooltip_text(_("Reset the values on the current tab to defaults")); resetButton->set_sensitive(true); resetButton->signal_clicked().connect(sigc::mem_fun(*this, &Transformation::onClear)); } - applyButton = addResponseButton(Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY); + applyButton = addResponseButton(_("_Apply"), Gtk::RESPONSE_APPLY); if (applyButton) { applyButton->set_tooltip_text(_("Apply transformation to selection")); applyButton->set_sensitive(false); diff --git a/src/ui/dialog/undo-history.cpp b/src/ui/dialog/undo-history.cpp index 53aa7e6ff..bf5bdc76d 100644 --- a/src/ui/dialog/undo-history.cpp +++ b/src/ui/dialog/undo-history.cpp @@ -24,7 +24,7 @@ #include "util/signal-blocker.h" #include "desktop.h" -#include <gtkmm/invisible.h> +#include <gtkmm/icontheme.h> namespace Inkscape { namespace UI { @@ -43,8 +43,8 @@ void CellRendererSPIcon::render_vfunc(const Cairo::RefPtr<Cairo::Context>& cr, // if the icon isn't cached, render it to a pixbuf if ( !_icon_cache[_property_event_type] ) { - Glib::ustring image = Inkscape::Verb::get(_property_event_type)->get_image(); - Gtk::Widget* icon = sp_icon_get_icon(image, Inkscape::ICON_SIZE_MENU); + Glib::ustring image_name = Inkscape::Verb::get(_property_event_type)->get_image(); + Gtk::Widget* icon = sp_icon_get_icon(image_name, Inkscape::ICON_SIZE_MENU); if (icon) { @@ -54,8 +54,8 @@ void CellRendererSPIcon::render_vfunc(const Cairo::RefPtr<Cairo::Context>& cr, sp_icon_fetch_pixbuf(sp_icon); _property_icon = Glib::wrap(sp_icon->pb, true); } else if ( GTK_IS_IMAGE(icon->gobj()) ) { - _property_icon = Gtk::Invisible().render_icon_pixbuf(Gtk::StockID(image), - Gtk::ICON_SIZE_MENU); + auto icon_theme = Gtk::IconTheme::get_default(); + _property_icon = icon_theme->load_icon(image_name, 16); } else { delete icon; return; diff --git a/src/ui/dialog/xml-tree.cpp b/src/ui/dialog/xml-tree.cpp index d05c52531..fa35b092a 100644 --- a/src/ui/dialog/xml-tree.cpp +++ b/src/ui/dialog/xml-tree.cpp @@ -19,7 +19,6 @@ #include "xml-tree.h" #include "widgets/icon.h" #include <glibmm/i18n.h> -#include <gtkmm/stock.h> #include "desktop.h" diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index e0c767509..d50c56b76 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -73,8 +73,6 @@ #include "message-stack.h" #include "ui/dialog/layer-properties.h" -#include "widgets/image-menu-item.h" - using Inkscape::DocumentUndo; /* Drag and Drop */ @@ -405,7 +403,7 @@ sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name ) icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name ); gtk_widget_show(icon); - image_menu_item_set_image((ImageMenuItem *) item, icon); + gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon); } // end of sp_ui_menu_add_icon void @@ -459,7 +457,7 @@ static GtkWidget *sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb if (radio) { item = gtk_radio_menu_item_new_with_mnemonic(group, action->name); } else { - item = image_menu_item_new_with_mnemonic(action->name); + item = gtk_image_menu_item_new_with_mnemonic(action->name); } gtk_label_set_markup_with_mnemonic( GTK_LABEL(gtk_bin_get_child(GTK_BIN (item))), action->name); diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp index 2763e6c4b..cf2c03396 100644 --- a/src/ui/object-edit.cpp +++ b/src/ui/object-edit.cpp @@ -43,7 +43,7 @@ static KnotHolder *sp_lpe_knot_holder(SPLPEItem *item, SPDesktop *desktop) KnotHolder *knot_holder = new KnotHolder(desktop, item, NULL); Inkscape::LivePathEffect::Effect *effect = item->getCurrentLPE(); - effect->addHandles(knot_holder, desktop, item); + effect->addHandles(knot_holder, item); return knot_holder; } diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp index 0e5a9279d..d6e491ac3 100644 --- a/src/ui/tool/node.cpp +++ b/src/ui/tool/node.cpp @@ -326,10 +326,7 @@ bool Handle::grabbed(GdkEventMotion *) void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) { - if (tools_isactive(_desktop, TOOLS_NODES)) { - Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(_desktop->event_context); - nt->update_helperpath(); - } + Inkscape::UI::Tools::sp_update_helperpath(); Geom::Point parent_pos = _parent->position(); Geom::Point origin = _last_drag_origin(); SnapManager &sm = _desktop->namedview->snap_manager; @@ -1199,10 +1196,7 @@ bool Node::grabbed(GdkEventMotion *event) void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) { - if (tools_isactive(_desktop, TOOLS_NODES)) { - Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(_desktop->event_context); - nt->update_helperpath(); - } + Inkscape::UI::Tools::sp_update_helperpath(); // For a note on how snapping is implemented in Inkscape, see snap.h. SnapManager &sm = _desktop->namedview->snap_manager; // even if we won't really snap, we might still call the one of the diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp index f316bea38..5746a8f86 100644 --- a/src/ui/tool/path-manipulator.cpp +++ b/src/ui/tool/path-manipulator.cpp @@ -1492,11 +1492,14 @@ void PathManipulator::_setGeometry() } } else { if (empty()) return; - //XML Tree being used here directly while it shouldn't be. - if (_path->getRepr()->attribute("inkscape:original-d")) - _path->set_original_curve(_spcurve, false, false); - else + if (SPCurve * original = _path->get_original_curve()){ + if(!_spcurve->is_equal(original)) { + _path->set_original_curve(_spcurve, false, false); + delete original; + } + } else if(!_spcurve->is_equal(_path->get_curve())) { _path->setCurve(_spcurve, false); + } } } diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp index 33015fe11..083a7d0ba 100644 --- a/src/ui/tool/transform-handle-set.cpp +++ b/src/ui/tool/transform-handle-set.cpp @@ -183,6 +183,11 @@ void TransformHandle::ungrabbed(GdkEventButton *) _setState(_state); endTransform(); _th.signal_commit.emit(getCommitEvent()); + + //updates the positions of the nodes + Inkscape::UI::Tools::NodeTool *nt = INK_NODE_TOOL(_th._desktop->event_context); + ControlPointSelection* selection = nt->_selected_nodes; + selection->setOriginalPoints(); } diff --git a/src/ui/tools/calligraphic-tool.cpp b/src/ui/tools/calligraphic-tool.cpp index 9b4dbb1a2..c2b86b538 100644 --- a/src/ui/tools/calligraphic-tool.cpp +++ b/src/ui/tools/calligraphic-tool.cpp @@ -921,10 +921,10 @@ void CalligraphicTool::set_to_accumulated(bool unionize, bool subtract) { if (unionize) { desktop->getSelection()->add(this->repr); - sp_selected_path_union_skip_undo(desktop->getSelection()); + desktop->getSelection()->pathUnion(true); } else if (subtract) { desktop->getSelection()->add(this->repr); - sp_selected_path_diff_skip_undo(desktop->getSelection()); + desktop->getSelection()->pathDiff(true); } else { if (this->keep_selected) { desktop->getSelection()->set(this->repr); diff --git a/src/ui/tools/eraser-tool.cpp b/src/ui/tools/eraser-tool.cpp index 4f941e534..ae312e054 100644 --- a/src/ui/tools/eraser-tool.cpp +++ b/src/ui/tools/eraser-tool.cpp @@ -714,7 +714,7 @@ void EraserTool::set_to_accumulated() { Inkscape::GC::release(dup); // parent takes over selection->set(dup); if (!this->nowidth) { - sp_selected_path_union_skip_undo(selection); + selection->pathUnion(true); } selection->add(item); if(item->style->fill_rule.value == SP_WIND_RULE_EVENODD){ @@ -725,9 +725,9 @@ void EraserTool::set_to_accumulated() { css = 0; } if (this->nowidth) { - sp_selected_path_cut_skip_undo(selection); + selection->pathCut(true); } else { - sp_selected_path_diff_skip_undo(selection); + selection->pathDiff(true); } workDone = true; // TODO set this only if something was cut. bool break_apart = prefs->getBool("/tools/eraser/break_apart", false); @@ -762,7 +762,7 @@ void EraserTool::set_to_accumulated() { this->repr->parent()->appendChild(dup); Inkscape::GC::release(dup); // parent takes over selection->set(dup); - sp_selected_path_union_skip_undo(selection); + selection->pathUnion(true); if (bbox && bbox->intersects(*eraserBbox)) { SPClipPath *clip_path = item->clip_ref->getObject(); if (clip_path) { @@ -786,7 +786,7 @@ void EraserTool::set_to_accumulated() { sp_object_unref(clip_path); selection->raiseToTop(true); selection->add(dup_clip); - sp_selected_path_diff_skip_undo(selection); + selection->pathDiff(true); SPItem * clip = SP_ITEM(*(selection->items().begin())); } } @@ -802,7 +802,7 @@ void EraserTool::set_to_accumulated() { rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); selection->raiseToTop(true); selection->add(rect); - sp_selected_path_diff_skip_undo(selection); + selection->pathDiff(true); } selection->raiseToTop(true); selection->add(item); diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp index 0b893a7ba..1801a0ea0 100644 --- a/src/ui/tools/flood-tool.cpp +++ b/src/ui/tools/flood-tool.cpp @@ -89,10 +89,10 @@ Glib::ustring ch_init[8] = { const std::vector<Glib::ustring> FloodTool::channel_list( ch_init, ch_init+8 ); Glib::ustring gap_init[4] = { - C_("Flood autogap", "None"), - C_("Flood autogap", "Small"), - C_("Flood autogap", "Medium"), - C_("Flood autogap", "Large") + NC_("Flood autogap", "None"), + NC_("Flood autogap", "Small"), + NC_("Flood autogap", "Medium"), + NC_("Flood autogap", "Large") }; const std::vector<Glib::ustring> FloodTool::gap_list( gap_init, gap_init+4 ); @@ -446,7 +446,7 @@ static void do_trace(bitmap_coords_info bci, guchar *trace_px, SPDesktop *deskto ngettext("Area filled, path with <b>%d</b> node created and unioned with selection.","Area filled, path with <b>%d</b> nodes created and unioned with selection.", SP_PATH(reprobj)->nodesInPath()), SP_PATH(reprobj)->nodesInPath() ); selection->add(reprobj); - sp_selected_path_union_skip_undo(desktop->getSelection()); + selection->pathUnion(true); } else { desktop->messageStack()->flashF( Inkscape::WARNING_MESSAGE, ngettext("Area filled, path with <b>%d</b> node created.","Area filled, path with <b>%d</b> nodes created.", diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp index 16df225e0..750596808 100644 --- a/src/ui/tools/gradient-tool.cpp +++ b/src/ui/tools/gradient-tool.cpp @@ -79,6 +79,9 @@ GradientTool::~GradientTool() { delete this->subselcon; } +// This must match GrPointType enum sp-gradient.h +// We should move this to a shared header (can't simply move to gradient.h since that would require +// including <glibmm/i18n.h> which messes up "N_" in extensions... argh!). const gchar *gr_handle_descr [] = { N_("Linear gradient <b>start</b>"), //POINT_LG_BEGIN N_("Linear gradient <b>end</b>"), @@ -88,7 +91,10 @@ const gchar *gr_handle_descr [] = { N_("Radial gradient <b>radius</b>"), N_("Radial gradient <b>focus</b>"), // POINT_RG_FOCUS N_("Radial gradient <b>mid stop</b>"), - N_("Radial gradient <b>mid stop</b>") + N_("Radial gradient <b>mid stop</b>"), + N_("Mesh gradient <b>corner</b>"), + N_("Mesh gradient <b>handle</b>"), + N_("Mesh gradient <b>tensor</b>") }; void GradientTool::selection_changed(Inkscape::Selection*) { @@ -831,6 +837,16 @@ bool GradientTool::root_handler(GdkEvent* event) { ret = TRUE; break; + case GDK_KEY_i: + case GDK_KEY_I: + if (MOD__SHIFT_ONLY(event)) { + // Shift+I - insert stops (alternate keybinding for keyboards + // that don't have the Insert key) + sp_gradient_context_add_stops_between_selected_stops (this); + ret = TRUE; + } + break; + case GDK_KEY_Delete: case GDK_KEY_KP_Delete: case GDK_KEY_BackSpace: diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index c6983b94a..e628094d9 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -41,6 +41,7 @@ #include "sp-text.h" #include "sp-defs.h" #include "style.h" +#include "ui/control-manager.h" // Gradient specific #include "gradient-drag.h" @@ -74,6 +75,9 @@ MeshTool::MeshTool() : ToolBase(cursor_gradient_xpm, 4, 4) , cursor_addnode(false) , node_added(false) + , show_handles(true) + , edit_fill(true) + , edit_stroke(true) // TODO: Why are these connections stored as pointers? , selcon(NULL) , subselcon(NULL) @@ -92,7 +96,19 @@ MeshTool::~MeshTool() { delete this->subselcon; } +// This must match GrPointType enum sp-gradient.h +// We should move this to a shared header (can't simply move to gradient.h since that would require +// including <glibmm/i18n.h> which messes up "N_" in extensions... argh!). const gchar *ms_handle_descr [] = { + N_("Linear gradient <b>start</b>"), //POINT_LG_BEGIN + N_("Linear gradient <b>end</b>"), + N_("Linear gradient <b>mid stop</b>"), + N_("Radial gradient <b>center</b>"), + N_("Radial gradient <b>radius</b>"), + N_("Radial gradient <b>radius</b>"), + N_("Radial gradient <b>focus</b>"), // POINT_RG_FOCUS + N_("Radial gradient <b>mid stop</b>"), + N_("Radial gradient <b>mid stop</b>"), N_("Mesh gradient <b>corner</b>"), N_("Mesh gradient <b>handle</b>"), N_("Mesh gradient <b>tensor</b>") @@ -240,7 +256,25 @@ void MeshTool::setup() { ) )); + sp_event_context_read(this, "show_handles"); + sp_event_context_read(this, "edit_fill"); + sp_event_context_read(this, "edit_stroke"); + this->selection_changed(selection); + +} + +void MeshTool::set(const Inkscape::Preferences::Entry& value) { + Glib::ustring entry_name = value.getEntryName(); + if (entry_name == "show_handles") { + this->show_handles = value.getBool(true); + } else if (entry_name == "edit_fill") { + this->edit_fill = value.getBool(true); + } else if (entry_name == "edit_stroke") { + this->edit_stroke = value.getBool(true); + } else { + ToolBase::set(value); + } } void @@ -266,32 +300,39 @@ sp_mesh_context_select_prev (ToolBase *event_context) } /** -Returns true if mouse cursor over mesh edge. +Returns vector of control lines mouse is over. Returns only first if 'first' is true. */ -static bool -sp_mesh_context_is_over_line (MeshTool *rc, SPCtrlLine *line, Geom::Point event_p) +static std::vector<SPCtrlCurve *> +sp_mesh_context_over_line (MeshTool *rc, Geom::Point event_p, bool first = true) { - if (!SP_IS_CTRLCURVE(line) ) { - return false; - } - SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop; //Translate mouse point into proper coord system rc->mousepoint_doc = desktop->w2d(event_p); - SPCtrlCurve *curve = SP_CTRLCURVE(line); - Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 ); - Geom::Coord coord = b.nearestTime( rc->mousepoint_doc ); // Coord == double - Geom::Point nearest = b( coord ); + double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance; + + GrDrag *drag = rc->_grdrag; - double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); + std::vector<SPCtrlCurve *> selected; - double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance; + for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end(); ++l) { + if (!SP_IS_CTRLCURVE(*l)) continue; - bool close = (dist_screen < tolerance); + SPCtrlCurve *curve = SP_CTRLCURVE(*l); + Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 ); + Geom::Coord coord = b.nearestTime( rc->mousepoint_doc ); // Coord == double + Geom::Point nearest = b( coord ); - return close; + double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); + if (dist_screen < tolerance) { + selected.push_back(curve); + if (first) { + break; + } + } + } + return selected; } @@ -336,12 +377,13 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation ) std::map<SPMeshGradient*, std::vector<guint> > points; std::map<SPMeshGradient*, SPItem*> items; - + std::map<SPMeshGradient*, Inkscape::PaintTarget> fill_or_stroke; + // Get list of selected draggers for each mesh. - // For all selected draggers + // For all selected draggers (a dragger may include draggerables from different meshes). for (std::set<GrDragger *>::const_iterator i = drag->selected.begin(); i != drag->selected.end(); ++i) { GrDragger *dragger = *i; - // For all draggables of dragger + // For all draggables of dragger (a draggable corresponds to a unique mesh). for (std::vector<GrDraggable *>::const_iterator j = dragger->draggables.begin(); j != dragger->draggables.end() ; ++j) { GrDraggable *d = *j; @@ -354,6 +396,7 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation ) // Collect points together for same gradient points[gradient].push_back( d->point_i ); items[gradient] = d->item; + fill_or_stroke[gradient] = d->fill_or_stroke ? Inkscape::FOR_FILL: Inkscape::FOR_STROKE; } } @@ -389,6 +432,11 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation ) noperation += mg->array.color_pick( iter->second, items[iter->first] ); break; + case MG_CORNER_INSERT: + // std::cout << "INSERT" << std::endl; + noperation += mg->array.insert( iter->second ); + break; + default: std::cout << "sp_mesh_corner_operation: unknown operation" << std::endl; } @@ -402,22 +450,31 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation ) case MG_CORNER_SIDE_TOGGLE: DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Toggled mesh path type.")); + drag->local_change = true; // Don't create new draggers. break; case MG_CORNER_SIDE_ARC: DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Approximated arc for mesh side.")); + drag->local_change = true; // Don't create new draggers. break; case MG_CORNER_TENSOR_TOGGLE: DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Toggled mesh tensors.")); + drag->local_change = true; // Don't create new draggers. break; case MG_CORNER_COLOR_SMOOTH: DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Smoothed mesh corner color.")); + drag->local_change = true; // Don't create new draggers. break; case MG_CORNER_COLOR_PICK: DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Picked mesh corner color.")); + drag->local_change = true; // Don't create new draggers. + break; + + case MG_CORNER_INSERT: + DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Inserted new row or column.")); break; default: @@ -426,7 +483,9 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation ) } } } - drag->updateDraggers(); + + // Not needed. Update is done via gr_drag_sel_modified(). + // drag->updateDraggers(); } @@ -456,38 +515,33 @@ sp_mesh_context_fit_mesh_in_bbox (MeshTool *rc) SPItem *item = *i; SPStyle *style = item->style; - if (style && (style->fill.isPaintserver())) { - SPPaintServer *server = item->style->getFillPaintServer(); - if ( SP_IS_MESHGRADIENT(server) ) { - - SPMeshGradient *gradient = SP_MESHGRADIENT(server); - SPCurve * outline = gradient->array.outline_path(); - Geom::OptRect mesh_bbox = outline->get_pathvector().boundsExact(); - outline->unref(); - Geom::OptRect item_bbox = item->geometricBounds(); - - if ((*mesh_bbox).width() == 0) { - continue; - } - if ((*mesh_bbox).height() == 0) { - continue; + if (style) { + + if (style->fill.isPaintserver()) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_MESHGRADIENT(server) ) { + + Geom::OptRect item_bbox = item->geometricBounds(); + SPMeshGradient *gradient = SP_MESHGRADIENT(server); + if (gradient->array.fill_box( item_bbox )) { + changed = true; + } } - double scale_x = (*item_bbox).width() /(*mesh_bbox).width() ; - double scale_y = (*item_bbox).height()/(*mesh_bbox).height(); - - Geom::Translate t1(-(*mesh_bbox).min()); - Geom::Scale scale(scale_x,scale_y); - Geom::Translate t2((*item_bbox).min()); - Geom::Affine transform = t1 * scale * t2; - if (!transform.isIdentity() ) { - gradient->array.transform(transform); - gradient->array.write( gradient ); - gradient->requestModified(SP_OBJECT_MODIFIED_FLAG); - changed = true; + } + + if (style->stroke.isPaintserver()) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_MESHGRADIENT(server) ) { + + Geom::OptRect item_bbox = item->visualBounds(); + SPMeshGradient *gradient = SP_MESHGRADIENT(server); + if (gradient->array.fill_box( item_bbox )) { + changed = true; + } } } + } - } if (changed) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH, @@ -523,42 +577,42 @@ bool MeshTool::root_handler(GdkEvent* event) { // Double click: // If over a mesh line, divide mesh row/column - // If not over a line, create new gradients for selected objects. + // If not over a line and no mesh, create new mesh for top selected object. if ( event->button.button == 1 ) { - // Are we over a mesh line? - bool over_line = false; - if (! drag->lines.empty()) { - for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); ++l) { - over_line |= sp_mesh_context_is_over_line (this, *l, Geom::Point(event->motion.x, event->motion.y)); - } - } + // Are we over a mesh line? + std::vector<SPCtrlCurve *> over_line = + sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y)); - if (over_line) { + if (!over_line.empty()) { // We take the first item in selection, because with doubleclick, the first click // always resets selection to the single object under cursor sp_mesh_context_split_near_point(this, selection->items().front(), this->mousepoint_doc, event->button.time); } else { - sp_mesh_new_default(*this); // Create a new gradient with default coordinates. -// auto items= selection->items(); -// for(auto i=items.begin();i!=items.end();++i){ -// SPItem *item = *i; -// SPGradientType new_type = SP_GRADIENT_TYPE_MESH; -// Inkscape::PaintTarget fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; - -// #ifdef DEBUG_MESH -// std::cout << "sp_mesh_context_root_handler: creating new mesh on: " << (fsmode == Inkscape::FOR_FILL ? "Fill" : "Stroke") << std::endl; -// #endif -// SPGradient *vector = sp_gradient_vector_for_object(desktop->getDocument(), desktop, item, fsmode); - -// SPGradient *priv = sp_item_set_gradient(item, vector, new_type, fsmode); -// sp_gradient_reset_to_userspace(priv, item); -// } - -// DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH, -// _("Create default mesh")); + + // Check if object already has mesh... if it does, + // don't create new mesh with click-drag. + bool has_mesh = false; + if (!selection->isEmpty()) { + SPStyle *style = selection->items().front()->style; + if (style) { + Inkscape::PaintTarget fill_or_stroke = + (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? + Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + SPPaintServer *server = + (fill_or_stroke == Inkscape::FOR_FILL) ? + style->getFillPaintServer(): + style->getStrokePaintServer(); + if (server && SP_IS_MESHGRADIENT(server)) + has_mesh = true; + } + } + + if (!has_mesh) { + sp_mesh_new_default(*this); + } } ret = TRUE; @@ -570,10 +624,36 @@ bool MeshTool::root_handler(GdkEvent* event) { #ifdef DEBUG_MESH std::cout << "sp_mesh_context_root_handler: GDK_BUTTON_PRESS" << std::endl; #endif + // Button down - // If Shift key down: do rubber band selection - // Else set origin for drag. A drag creates a new gradient if one does not exist + // If mesh already exists, do rubber band selection. + // Else set origin for drag which will create a new gradient. if ( event->button.button == 1 && !this->space_panning ) { + + // Are we over a mesh line? + std::vector<SPCtrlCurve *> over_line = + sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y), false); + + if (!over_line.empty()) { + for (std::vector<SPCtrlCurve *>::const_iterator it = over_line.begin(); + it != over_line.end(); ++it ) { + SPItem *item = (*it)->item; + Inkscape::PaintTarget fill_or_stroke = + (*it)->is_fill ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + GrDragger* dragger0 = drag->getDraggerFor(item, POINT_MG_CORNER, (*it)->corner0, fill_or_stroke); + GrDragger* dragger1 = drag->getDraggerFor(item, POINT_MG_CORNER, (*it)->corner1, fill_or_stroke); + bool add = (event->button.state & GDK_SHIFT_MASK); + bool toggle = (event->button.state & GDK_CONTROL_MASK); + if ( !add && !toggle ) { + drag->deselectAll(); + } + drag->setSelected( dragger0, true, !toggle ); + drag->setSelected( dragger1, true, !toggle ); + } + ret = true; + break; // To avoid putting the following code in an else block. + } + Geom::Point button_w(event->button.x, event->button.y); // save drag origin @@ -584,25 +664,43 @@ bool MeshTool::root_handler(GdkEvent* event) { dragging = true; Geom::Point button_dt = desktop->w2d(button_w); - if (event->button.state & GDK_SHIFT_MASK) { - Inkscape::Rubberband::get(desktop)->start(desktop, button_dt); - } else { - // remember clicked item, disregarding groups, honoring Alt; do nothing with Crtl to - // enable Ctrl+doubleclick of exactly the selected item(s) - if (!(event->button.state & GDK_CONTROL_MASK)) { - this->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE); + // Check if object already has mesh... if it does, + // don't create new mesh with click-drag. + bool has_mesh = false; + if (!selection->isEmpty()) { + SPStyle *style = selection->items().front()->style; + if (style) { + Inkscape::PaintTarget fill_or_stroke = + (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? + Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + SPPaintServer *server = + (fill_or_stroke == Inkscape::FOR_FILL) ? + style->getFillPaintServer(): + style->getStrokePaintServer(); + if (server && SP_IS_MESHGRADIENT(server)) + has_mesh = true; } + } - if (!selection->isEmpty()) { - SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop); - m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); - m.unSetup(); - } + if (has_mesh) { + Inkscape::Rubberband::get(desktop)->start(desktop, button_dt); + } - this->origin = button_dt; + // remember clicked item, disregarding groups, honoring Alt; do nothing with Crtl to + // enable Ctrl+doubleclick of exactly the selected item(s) + if (!(event->button.state & GDK_CONTROL_MASK)) { + this->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE); } + if (!selection->isEmpty()) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); + m.unSetup(); + } + + this->origin = button_dt; + ret = TRUE; } break; @@ -663,19 +761,14 @@ bool MeshTool::root_handler(GdkEvent* event) { } // Change cursor shape if over line - bool over_line = false; + std::vector<SPCtrlCurve *> over_line = + sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y)); - if (!drag->lines.empty()) { - for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() ; ++l) { - over_line |= sp_mesh_context_is_over_line (this, *l, Geom::Point(event->motion.x, event->motion.y)); - } - } - - if (this->cursor_addnode && !over_line) { + if (this->cursor_addnode && over_line.empty()) { this->cursor_shape = cursor_gradient_xpm; this->sp_event_context_update_cursor(); this->cursor_addnode = false; - } else if (!this->cursor_addnode && over_line) { + } else if (!this->cursor_addnode && !over_line.empty()) { this->cursor_shape = cursor_gradient_add_xpm; this->sp_event_context_update_cursor(); this->cursor_addnode = true; @@ -692,23 +785,15 @@ bool MeshTool::root_handler(GdkEvent* event) { this->xp = this->yp = 0; if ( event->button.button == 1 && !this->space_panning ) { - // Check if over line - bool over_line = false; - SPCtrlLine *line = NULL; - - if (!drag->lines.empty()) { - for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); ++l) { - over_line = sp_mesh_context_is_over_line (this, *l, Geom::Point(event->motion.x, event->motion.y)); - if (over_line) { - break; - } - } - } + // Check if over line + std::vector<SPCtrlCurve *> over_line = + sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y)); if ( (event->button.state & GDK_CONTROL_MASK) && (event->button.state & GDK_MOD1_MASK ) ) { - if (over_line && line) { - sp_mesh_context_split_near_point(this, line->item, this->mousepoint_doc, 0); + if (!over_line.empty()) { + sp_mesh_context_split_near_point(this, over_line[0]->item, + this->mousepoint_doc, 0); ret = TRUE; } } else { @@ -721,22 +806,47 @@ bool MeshTool::root_handler(GdkEvent* event) { } if (!this->within_tolerance) { - // we've been dragging, either create a new gradient - // or rubberband-select if we have rubberband - Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); - - if (r->is_started() && !this->within_tolerance) { - // this was a rubberband drag - if (r->getMode() == RUBBERBAND_MODE_RECT) { - Geom::OptRect const b = r->getRectangle(); - drag->selectRect(*b); + + // Check if object already has mesh... if it does, + // don't create new mesh with click-drag. + bool has_mesh = false; + if (!selection->isEmpty()) { + SPStyle *style = selection->items().front()->style; + if (style) { + Inkscape::PaintTarget fill_or_stroke = + (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? + Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + SPPaintServer *server = + (fill_or_stroke == Inkscape::FOR_FILL) ? + style->getFillPaintServer(): + style->getStrokePaintServer(); + if (server && SP_IS_MESHGRADIENT(server)) + has_mesh = true; } - } else { - // Create a new mesh gradient + } + + if (!has_mesh) { sp_mesh_new_default(*this); + } else { + + // we've been dragging, either create a new gradient + // or rubberband-select if we have rubberband + Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop); + + if (r->is_started() && !this->within_tolerance) { + // this was a rubberband drag + if (r->getMode() == RUBBERBAND_MODE_RECT) { + Geom::OptRect const b = r->getRectangle(); + if (!(event->button.state & GDK_SHIFT_MASK)) { + drag->deselectAll(); + } + drag->selectRect(*b); + } + } } + } else if (this->item_to_select) { - if (over_line && line) { + if (!over_line.empty()) { // Clicked on an existing mesh line, don't change selection. This stops // possible change in selection during a double click with overlapping objects } else { @@ -744,15 +854,21 @@ bool MeshTool::root_handler(GdkEvent* event) { if (event->button.state & GDK_SHIFT_MASK) { selection->toggle(this->item_to_select); } else { + drag->deselectAll(); selection->set(this->item_to_select); } } } else { - // click in an empty space; do the same as Esc - if (!drag->selected.empty()) { - drag->deselectAll(); + if (!over_line.empty()) { + // Clicked on an existing mesh line, don't change selection. This stops + // possible change in selection during a double click with overlapping objects } else { - selection->clear(); + // click in an empty space; do the same as Esc + if (!drag->selected.empty()) { + drag->deselectAll(); + } else { + selection->clear(); + } } } @@ -780,10 +896,11 @@ bool MeshTool::root_handler(GdkEvent* event) { case GDK_KEY_Shift_R: case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt (at least on my machine) case GDK_KEY_Meta_R: - sp_event_show_modifier_tip (this->defaultMessageContext(), event, - _("FIXME<b>Ctrl</b>: snap mesh angle"), - _("FIXME<b>Shift</b>: draw mesh around the starting point"), - NULL); + + // sp_event_show_modifier_tip (this->defaultMessageContext(), event, + // _("FIXME<b>Ctrl</b>: snap mesh angle"), + // _("FIXME<b>Shift</b>: draw mesh around the starting point"), + // NULL); break; case GDK_KEY_A: @@ -901,25 +1018,33 @@ bool MeshTool::root_handler(GdkEvent* event) { } break; + // Mesh Operations -------------------------------------------- + case GDK_KEY_Insert: case GDK_KEY_KP_Insert: // with any modifiers: - //sp_gradient_context_add_stops_between_selected_stops (rc); - std::cout << "Inserting stops between selected stops not implemented yet" << std::endl; + sp_mesh_context_corner_operation ( this, MG_CORNER_INSERT ); ret = TRUE; break; + case GDK_KEY_i: + case GDK_KEY_I: + if (MOD__SHIFT_ONLY(event)) { + // Shift+I - insert corners (alternate keybinding for keyboards + // that don't have the Insert key) + sp_mesh_context_corner_operation ( this, MG_CORNER_INSERT ); + ret = TRUE; + } + break; + case GDK_KEY_Delete: case GDK_KEY_KP_Delete: case GDK_KEY_BackSpace: if ( !drag->selected.empty() ) { - std::cout << "Deleting mesh stops not implemented yet" << std::endl; ret = TRUE; } break; - // Mesh Operations -------------------------------------------- - case GDK_KEY_b: // Toggle mesh side between lineto and curveto. case GDK_KEY_B: if (MOD__ALT(event) && drag->isNonEmpty() && drag->hasSelection()) { @@ -1011,6 +1136,16 @@ static void sp_mesh_new_default(MeshTool &rc) { (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + // Ensure mesh is immediately editable. + // Editting both fill and stroke at same time doesn't work well so avoid. + if (fill_or_stroke == Inkscape::FOR_FILL) { + prefs->setBool("/tools/mesh/edit_fill", true ); + prefs->setBool("/tools/mesh/edit_stroke", false); + } else { + prefs->setBool("/tools/mesh/edit_fill", false); + prefs->setBool("/tools/mesh/edit_stroke", true ); + } + // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property(css, "fill-opacity", "1.0"); @@ -1065,7 +1200,6 @@ static void sp_mesh_new_default(MeshTool &rc) { } } - } } } diff --git a/src/ui/tools/mesh-tool.h b/src/ui/tools/mesh-tool.h index f142bfd9c..1f012dc53 100644 --- a/src/ui/tools/mesh-tool.h +++ b/src/ui/tools/mesh-tool.h @@ -31,29 +31,34 @@ namespace Tools { class MeshTool : public ToolBase { public: - MeshTool(); - virtual ~MeshTool(); + MeshTool(); + virtual ~MeshTool(); Geom::Point origin; - bool cursor_addnode; - - bool node_added; - Geom::Point mousepoint_doc; // stores mousepoint when over_line in doc coords sigc::connection *selcon; sigc::connection *subselcon; - static const std::string prefsPath; + static const std::string prefsPath; - virtual void setup(); - virtual bool root_handler(GdkEvent* event); + virtual void setup(); + virtual void set(const Inkscape::Preferences::Entry& val); + virtual bool root_handler(GdkEvent* event); - virtual const std::string& getPrefsPath(); + virtual const std::string& getPrefsPath(); private: - void selection_changed(Inkscape::Selection* sel); + void selection_changed(Inkscape::Selection* sel); + + bool cursor_addnode; + bool node_added; + bool show_handles; + bool edit_fill; + bool edit_stroke; + + }; void sp_mesh_context_select_next(ToolBase *event_context); diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp index f3679b40f..0c948c91c 100644 --- a/src/ui/tools/node-tool.cpp +++ b/src/ui/tools/node-tool.cpp @@ -32,6 +32,8 @@ #include "sp-text.h" #include "ui/control-manager.h" #include "ui/tools/node-tool.h" +#include "ui/tools-switch.h" +#include "ui/tools/tool-base.h" #include "ui/tool/control-point-selection.h" #include "ui/tool/event-utils.h" #include "ui/tool/multi-path-manipulator.h" @@ -209,7 +211,7 @@ void NodeTool::setup() { this->_sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged( sigc::mem_fun(this, &NodeTool::handleControlUiStyleChange) ); - + this->helperpath_tmpitem = NULL; this->_selected_nodes = new Inkscape::UI::ControlPointSelection(this->desktop, this->_transform_handle_group); data.node_data.selection = this->_selected_nodes; @@ -237,7 +239,6 @@ void NodeTool::setup() { ))) ); - this->helperpath_tmpitem = NULL; this->cursor_drag = false; this->show_transform_handles = true; this->single_node_transform_handles = false; @@ -270,29 +271,34 @@ void NodeTool::setup() { } this->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive - this->update_helperpath(); + sp_update_helperpath(); } // show helper paths of the applied LPE, if any -void NodeTool::update_helperpath () { - Inkscape::Selection *selection = this->desktop->getSelection(); - - if (this->helperpath_tmpitem) { - this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem); - this->helperpath_tmpitem = NULL; +void sp_update_helperpath() { + SPDesktop * desktop = SP_ACTIVE_DESKTOP; + if (!desktop || !tools_isactive(desktop, TOOLS_NODES)) { + return; + } + Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(desktop->event_context); + Inkscape::Selection *selection = desktop->getSelection(); + if (nt->helperpath_tmpitem) { + desktop->remove_temporary_canvasitem(nt->helperpath_tmpitem); + nt->helperpath_tmpitem = NULL; } if (SP_IS_LPE_ITEM(selection->singleItem())) { Inkscape::LivePathEffect::Effect *lpe = SP_LPE_ITEM(selection->singleItem())->getCurrentLPE(); if (lpe && lpe->isVisible()/* && lpe->showOrigPath()*/) { - Inkscape::UI::ControlPointSelection *selectionNodes = _selected_nodes; + + Inkscape::UI::ControlPointSelection *selectionNodes = nt->_selected_nodes; std::vector<Geom::Point> selectedNodesPositions; for (Inkscape::UI::ControlPointSelection::iterator i = selectionNodes->begin(); i != selectionNodes->end(); ++i) { Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(*i); selectedNodesPositions.push_back(n->position()); } lpe->setSelectedNodePoints(selectedNodesPositions); - lpe->setCurrentZoom(this->desktop->current_zoom()); + lpe->setCurrentZoom(desktop->current_zoom()); SPCurve *c = new SPCurve(); SPCurve *cc = new SPCurve(); std::vector<Geom::PathVector> cs = lpe->getCanvasIndicators(SP_LPE_ITEM(selection->singleItem())); @@ -302,11 +308,11 @@ void NodeTool::update_helperpath () { cc->reset(); } if (!c->is_empty()) { - SPCanvasItem *helperpath = sp_canvas_bpath_new(this->desktop->getTempGroup(), c, true); + SPCanvasItem *helperpath = sp_canvas_bpath_new(desktop->getTempGroup(), c, true); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helperpath), 0x0000ff9A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helperpath), 0, SP_WIND_RULE_NONZERO); sp_canvas_item_affine_absolute(helperpath, selection->singleItem()->i2dt_affine()); - this->helperpath_tmpitem = this->desktop->add_temporary_canvasitem(helperpath, 0); + nt->helperpath_tmpitem = desktop->add_temporary_canvasitem(helperpath, 0); } c->unref(); cc->unref(); @@ -468,7 +474,7 @@ bool NodeTool::root_handler(GdkEvent* event) { switch (event->type) { case GDK_MOTION_NOTIFY: { - update_helperpath(); + sp_update_helperpath(); combine_motion_events(desktop->canvas, event->motion, 0); SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), FALSE, TRUE); diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h index 8342d66a6..983ba6cee 100644 --- a/src/ui/tools/node-tool.h +++ b/src/ui/tools/node-tool.h @@ -49,14 +49,14 @@ public: Inkscape::UI::ControlPointSelection* _selected_nodes; Inkscape::UI::MultiPathManipulator* _multipath; - + Inkscape::Display::TemporaryItem *helperpath_tmpitem; + bool edit_clipping_paths; bool edit_masks; static const std::string prefsPath; virtual void setup(); - virtual void update_helperpath(); virtual void set(const Inkscape::Preferences::Entry& val); virtual bool root_handler(GdkEvent* event); @@ -68,7 +68,7 @@ private: sigc::connection _sizeUpdatedConn; SPItem *flashed_item; - Inkscape::Display::TemporaryItem *helperpath_tmpitem; + Inkscape::Display::TemporaryItem *flash_tempitem; Inkscape::UI::Selector* _selector; Inkscape::UI::PathSharedData* _path_data; @@ -96,8 +96,9 @@ private: void update_tip(GdkEvent *event); void handleControlUiStyleChange(); }; - + void sp_update_helperpath(); } + } } diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp index 3649008ff..ad006627c 100644 --- a/src/ui/tools/spray-tool.cpp +++ b/src/ui/tools/spray-tool.cpp @@ -1026,7 +1026,7 @@ static bool sp_spray_recursive(SPDesktop *desktop, if (unionResult) { // No need to add the very first item (initialized with NULL). set->add(unionResult); } - sp_selected_path_union_skip_undo(set); + set->pathUnion(true); set->add(parent_item); Inkscape::GC::release(copy); did = true; @@ -1364,7 +1364,7 @@ bool SprayTool::root_handler(GdkEvent* event) { SP_VERB_CONTEXT_SPRAY, _("Spray with clones")); break; case SPRAY_MODE_SINGLE_PATH: - sp_selected_path_union_skip_undo(objectSet()); + objectSet()->pathUnion(true); desktop->getSelection()->add(object_set.objects().begin(), object_set.objects().end()); DocumentUndo::done(this->desktop->getDocument(), SP_VERB_CONTEXT_SPRAY, _("Spray in single path")); diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp index a0394ecd4..ff5d623c2 100644 --- a/src/ui/tools/tweak-tool.cpp +++ b/src/ui/tools/tweak-tool.cpp @@ -56,6 +56,8 @@ #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" +#include "sp-mesh-gradient.h" +#include "sp-mesh-array.h" #include "gradient-chemistry.h" #include "sp-text.h" #include "sp-flowtext.h" @@ -761,112 +763,132 @@ static void tweak_colors_in_gradient(SPItem *item, Inkscape::PaintTarget fill_or p *= (gradient->gradientTransform).inverse(); // now p is in gradient's original coordinates - double pos = 0; - double r = 0; - SPLinearGradient *lg = dynamic_cast<SPLinearGradient *>(gradient); - if (lg) { - Geom::Point p1(lg->x1.computed, lg->y1.computed); - Geom::Point p2(lg->x2.computed, lg->y2.computed); - Geom::Point pdiff(p2 - p1); - double vl = Geom::L2(pdiff); + SPRadialGradient *rg = dynamic_cast<SPRadialGradient *>(gradient); + if (lg || rg) { - // This is the matrix which moves and rotates the gradient line - // so it's oriented along the X axis: - Geom::Affine norm = Geom::Affine(Geom::Translate(-p1)) * Geom::Affine(Geom::Rotate(-atan2(pdiff[Geom::Y], pdiff[Geom::X]))); + double pos = 0; + double r = 0; - // Transform the mouse point by it to find out its projection onto the gradient line: - Geom::Point pnorm = p * norm; + if (lg) { + Geom::Point p1(lg->x1.computed, lg->y1.computed); + Geom::Point p2(lg->x2.computed, lg->y2.computed); + Geom::Point pdiff(p2 - p1); + double vl = Geom::L2(pdiff); - // Scale its X coordinate to match the length of the gradient line: - pos = pnorm[Geom::X] / vl; - // Calculate radius in lenfth-of-gradient-line units - r = radius / vl; + // This is the matrix which moves and rotates the gradient line + // so it's oriented along the X axis: + Geom::Affine norm = Geom::Affine(Geom::Translate(-p1)) * + Geom::Affine(Geom::Rotate(-atan2(pdiff[Geom::Y], pdiff[Geom::X]))); - } else { - SPRadialGradient *rg = dynamic_cast<SPRadialGradient *>(gradient); + // Transform the mouse point by it to find out its projection onto the gradient line: + Geom::Point pnorm = p * norm; + + // Scale its X coordinate to match the length of the gradient line: + pos = pnorm[Geom::X] / vl; + // Calculate radius in lenfth-of-gradient-line units + r = radius / vl; + + } if (rg) { Geom::Point c (rg->cx.computed, rg->cy.computed); pos = Geom::L2(p - c) / rg->r.computed; r = radius / rg->r.computed; } - } - // Normalize pos to 0..1, taking into accound gradient spread: - double pos_e = pos; - if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) { - if (pos > 1) { - pos_e = 1; - } - if (pos < 0) { - pos_e = 0; - } - } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) { - if (pos > 1 || pos < 0) { - pos_e = pos - floor(pos); - } - } 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) { - pos_e = 1 - pos_e; + // Normalize pos to 0..1, taking into accound gradient spread: + double pos_e = pos; + if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) { + if (pos > 1) { + pos_e = 1; + } + if (pos < 0) { + pos_e = 0; + } + } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) { + if (pos > 1 || pos < 0) { + pos_e = pos - floor(pos); + } + } 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) { + pos_e = 1 - pos_e; + } } } - } - SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(gradient, false); + SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(gradient, false); - double offset_l = 0; - double offset_h = 0; - SPObject *child_prev = NULL; - for (auto& child: vector->children) { - SPStop *stop = dynamic_cast<SPStop *>(&child); - if (!stop) { - continue; - } + double offset_l = 0; + double offset_h = 0; + SPObject *child_prev = NULL; + for (auto& child: vector->children) { + SPStop *stop = dynamic_cast<SPStop *>(&child); + if (!stop) { + continue; + } - offset_h = stop->offset; - - if (child_prev) { - SPStop *prevStop = dynamic_cast<SPStop *>(child_prev); - g_assert(prevStop != NULL); - - if (offset_h - offset_l > r && pos_e >= offset_l && pos_e <= offset_h) { - // the summit falls in this interstop, and the radius is small, - // so it only affects the ends of this interstop; - // distribute the force between the two endstops so that they - // get all the painting even if they are not touched by the brush - tweak_color (mode, stop->specified_color.v.c, rgb_goal, - force * (pos_e - offset_l) / (offset_h - offset_l), - do_h, do_s, do_l); - tweak_color(mode, prevStop->specified_color.v.c, rgb_goal, - force * (offset_h - pos_e) / (offset_h - offset_l), - do_h, do_s, do_l); - stop->updateRepr(); - child_prev->updateRepr(); - break; - } else { - // wide brush, may affect more than 2 stops, - // paint each stop by the force from the profile curve - if (offset_l <= pos_e && offset_l > pos_e - r) { + offset_h = stop->offset; + + if (child_prev) { + SPStop *prevStop = dynamic_cast<SPStop *>(child_prev); + g_assert(prevStop != NULL); + + if (offset_h - offset_l > r && pos_e >= offset_l && pos_e <= offset_h) { + // the summit falls in this interstop, and the radius is small, + // so it only affects the ends of this interstop; + // distribute the force between the two endstops so that they + // get all the painting even if they are not touched by the brush + tweak_color (mode, stop->specified_color.v.c, rgb_goal, + force * (pos_e - offset_l) / (offset_h - offset_l), + do_h, do_s, do_l); tweak_color(mode, prevStop->specified_color.v.c, rgb_goal, - force * tweak_profile (fabs (pos_e - offset_l), r), + force * (offset_h - pos_e) / (offset_h - offset_l), do_h, do_s, do_l); + stop->updateRepr(); child_prev->updateRepr(); + break; + } else { + // wide brush, may affect more than 2 stops, + // paint each stop by the force from the profile curve + if (offset_l <= pos_e && offset_l > pos_e - r) { + tweak_color(mode, prevStop->specified_color.v.c, rgb_goal, + force * tweak_profile (fabs (pos_e - offset_l), r), + do_h, do_s, do_l); + child_prev->updateRepr(); + } + + if (offset_h >= pos_e && offset_h < pos_e + r) { + tweak_color (mode, stop->specified_color.v.c, rgb_goal, + force * tweak_profile (fabs (pos_e - offset_h), r), + do_h, do_s, do_l); + stop->updateRepr(); + } } + } - if (offset_h >= pos_e && offset_h < pos_e + r) { + offset_l = offset_h; + child_prev = &child; + } + } else { + // Mesh + SPMeshGradient *mg = dynamic_cast<SPMeshGradient *>(gradient); + if (mg) { + SPMeshGradient *mg_array = dynamic_cast<SPMeshGradient *>(mg->getArray()); + SPMeshNodeArray *array = &(mg_array->array); + // Every third node is a corner node + for( unsigned i=0; i < array->nodes.size(); i+=3 ) { + for( unsigned j=0; j < array->nodes[i].size(); j+=3 ) { + SPStop *stop = array->nodes[i][j]->stop; + double distance = Geom::L2(Geom::Point(p - array->nodes[i][j]->p)); tweak_color (mode, stop->specified_color.v.c, rgb_goal, - force * tweak_profile (fabs (pos_e - offset_h), r), - do_h, do_s, do_l); + force * tweak_profile (distance, radius), do_h, do_s, do_l); stop->updateRepr(); } } } - - offset_l = offset_h; - child_prev = &child; } } @@ -1062,7 +1084,8 @@ sp_tweak_dilate (TweakTool *tc, Geom::Point event_p, Geom::Point p, Geom::Point double move_force = get_move_force(tc); double color_force = MIN(sqrt(path_force)/20.0, 1); - auto items= selection->items(); +// auto items= selection->items(); + std::vector<SPItem*> items(selection->items().begin(), selection->items().end()); for(auto i=items.begin();i!=items.end(); ++i){ SPItem *item = *i; diff --git a/src/ui/widget/dock-item.cpp b/src/ui/widget/dock-item.cpp index 979c09d2f..d124854ae 100644 --- a/src/ui/widget/dock-item.cpp +++ b/src/ui/widget/dock-item.cpp @@ -15,7 +15,6 @@ #include "widgets/icon.h" #include <gtkmm/icontheme.h> -#include <gtkmm/stockitem.h> #include <glibmm/exceptionhandler.h> namespace Inkscape { @@ -47,18 +46,11 @@ DockItem::DockItem(Dock& dock, const Glib::ustring& name, const Glib::ustring& l if (!iconTheme->has_icon(icon_name)) { Inkscape::queueIconPrerender( INKSCAPE_ICON(icon_name.data()), Inkscape::ICON_SIZE_MENU ); } - // Icon might be in the icon theme, or might be a stock item. Check the proper source: if ( iconTheme->has_icon(icon_name) ) { int width = 0; int height = 0; Gtk::IconSize::lookup(Gtk::ICON_SIZE_MENU, width, height); _icon_pixbuf = iconTheme->load_icon(icon_name, width); - } else { - Gtk::StockItem item; - Gtk::StockID stockId(icon_name); - if ( Gtk::StockItem::lookup(stockId, item) ) { - _icon_pixbuf = _dock.getWidget().render_icon_pixbuf( stockId, Gtk::ICON_SIZE_MENU ); - } } } @@ -423,6 +415,8 @@ void DockItem::_onStateChanged(State /*prev_state*/, State new_state) { _window = getWindow(); + if(_window) + _window->set_type_hint(Gdk::WINDOW_TYPE_HINT_NORMAL); if (new_state == FLOATING_STATE && _window) { _window->signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHideWindow)); diff --git a/src/ui/widget/dock.cpp b/src/ui/widget/dock.cpp index 7744cb695..b2dec401f 100644 --- a/src/ui/widget/dock.cpp +++ b/src/ui/widget/dock.cpp @@ -256,7 +256,7 @@ void Dock::_onLayoutChanged() _paned->get_child1()->set_size_request(-1, -1); _scrolled_window->set_size_request(_default_empty_width); } - getParentPaned()->set_position(INT_MAX); + getParentPaned()->set_position(10000); } else { // unset any forced size requests diff --git a/src/ui/widget/font-button.cpp b/src/ui/widget/font-button.cpp index a472ac6a4..ed57c803a 100644 --- a/src/ui/widget/font-button.cpp +++ b/src/ui/widget/font-button.cpp @@ -8,8 +8,11 @@ #endif #include "font-button.h" + #include <glibmm/i18n.h> +#include <gtkmm/fontbutton.h> + namespace Inkscape { namespace UI { namespace Widget { diff --git a/src/ui/widget/font-button.h b/src/ui/widget/font-button.h index 1f1ad2d01..98c3db440 100644 --- a/src/ui/widget/font-button.h +++ b/src/ui/widget/font-button.h @@ -8,7 +8,6 @@ #ifndef INKSCAPE_UI_WIDGET_FONT_BUTTON_H #define INKSCAPE_UI_WIDGET_FONT_BUTTON_H -#include <gtkmm.h> #include "labelled.h" namespace Inkscape { diff --git a/src/ui/widget/frame.cpp b/src/ui/widget/frame.cpp index 65d10dcc4..6593d9c7c 100644 --- a/src/ui/widget/frame.cpp +++ b/src/ui/widget/frame.cpp @@ -21,26 +21,20 @@ namespace UI { namespace Widget { Frame::Frame(Glib::ustring const &label_text /*= ""*/, gboolean label_bold /*= TRUE*/ ) - : _label(label_text, 1.0, 0.5, TRUE), - _alignment() + : _label(label_text, 1.0, 0.5, TRUE) { set_shadow_type(Gtk::SHADOW_NONE); - //Put an indented GtkAlignment inside the frame. - //Further children should be children of this GtkAlignment: - Gtk::Frame::add(_alignment); - set_padding(4, 0, 8, 0); - set_label_widget(_label); set_label(label_text, label_bold); - - show_all_children(); } void Frame::add(Widget& widget) { - _alignment.add(widget); + Gtk::Frame::add(widget); + set_padding(4, 0, 8, 0); + show_all_children(); } void @@ -56,7 +50,21 @@ Frame::set_label(const Glib::ustring &label_text, gboolean label_bold /*= TRUE*/ void Frame::set_padding (guint padding_top, guint padding_bottom, guint padding_left, guint padding_right) { - _alignment.set_padding(padding_top, padding_bottom, padding_left, padding_right); + auto child = get_child(); + + if(child) + { + child->set_margin_top(padding_top); + child->set_margin_bottom(padding_bottom); + +#if GTK_CHECK_VERSION(3,12,0) + child->set_margin_start(padding_left); + child->set_margin_end(padding_right); +#else + child->set_margin_left(padding_left); + child->set_margin_right(padding_right); +#endif + } } Gtk::Label const * diff --git a/src/ui/widget/frame.h b/src/ui/widget/frame.h index a04666651..24dd716e6 100644 --- a/src/ui/widget/frame.h +++ b/src/ui/widget/frame.h @@ -10,7 +10,6 @@ #ifndef INKSCAPE_UI_WIDGET_FRAME_H #define INKSCAPE_UI_WIDGET_FRAME_H -#include <gtkmm/alignment.h> #include <gtkmm/frame.h> #include <gtkmm/label.h> @@ -55,8 +54,6 @@ public: protected: Gtk::Label _label; - Gtk::Alignment _alignment; - }; } // namespace Widget diff --git a/src/ui/widget/panel.cpp b/src/ui/widget/panel.cpp index 6e6e6c527..dff92594b 100644 --- a/src/ui/widget/panel.cpp +++ b/src/ui/widget/panel.cpp @@ -17,7 +17,6 @@ #include <gtkmm/dialog.h> // for Gtk::RESPONSE_* #include <gtkmm/menu.h> -#include <gtkmm/stock.h> #include <gtkmm/checkmenuitem.h> #include <gtkmm/radiomenuitem.h> #include <gtkmm/separatormenuitem.h> @@ -66,7 +65,7 @@ Panel::Panel(Glib::ustring const &label, gchar const *prefs_path, _label(label), _apply_label(apply_label), _verb_num(verb_num), - _temp_arrow(Gtk::ARROW_LEFT, Gtk::SHADOW_ETCHED_OUT), + _temp_arrow(), _menu(0), _action_area(0), _fillable(0) @@ -269,11 +268,9 @@ void Panel::_init() _top_bar.pack_end(_menu_popper, false, false); gint width = 0; gint height = 0; - - if ( gtk_icon_size_lookup( Inkscape::getRegisteredIconSize(Inkscape::ICON_SIZE_DECORATION), &width, &height ) ) { - _temp_arrow.set_size_request(width, height); - } - + gtk_image_set_from_icon_name(_temp_arrow.gobj(), + "pan-start-symbolic", + Inkscape::getRegisteredIconSize(Inkscape::ICON_SIZE_SMALL_TOOLBAR)); _menu_popper.add(_temp_arrow); _menu_popper.signal_button_press_event().connect_notify(sigc::mem_fun(*this, &Panel::_popper)); } @@ -578,14 +575,7 @@ void Panel::_apply() Gtk::Button *Panel::addResponseButton(const Glib::ustring &button_text, int response_id, bool pack_start) { - Gtk::Button *button = new Gtk::Button(button_text); - _addResponseButton(button, response_id, pack_start); - return button; -} - -Gtk::Button *Panel::addResponseButton(const Gtk::StockID &stock_id, int response_id, bool pack_start) -{ - Gtk::Button *button = new Gtk::Button(stock_id); + Gtk::Button *button = new Gtk::Button(button_text, true); _addResponseButton(button, response_id, pack_start); return button; } diff --git a/src/ui/widget/panel.h b/src/ui/widget/panel.h index 370779586..9cbf39de9 100644 --- a/src/ui/widget/panel.h +++ b/src/ui/widget/panel.h @@ -18,9 +18,9 @@ #endif #include <gtkmm/box.h> -#include <gtkmm/arrow.h> #include <gtkmm/button.h> #include <gtkmm/eventbox.h> +#include <gtkmm/image.h> #include <gtkmm/label.h> #include "enums.h" #include <vector> @@ -101,7 +101,6 @@ public: /* Methods providing a Gtk::Dialog like interface for adding buttons that emit Gtk::RESPONSE * signals on click. */ Gtk::Button* addResponseButton (const Glib::ustring &button_text, int response_id, bool pack_start=false); - Gtk::Button* addResponseButton (const Gtk::StockID &stock_id, int response_id, bool pack_start=false); void setDefaultResponse(int response_id); void setResponseSensitive(int response_id, bool setting); @@ -157,7 +156,7 @@ private: Gtk::VBox _right_bar; Gtk::VBox _contents; Gtk::Label _tab_title; - Gtk::Arrow _temp_arrow; + Gtk::Image _temp_arrow; Gtk::EventBox _menu_popper; Gtk::Button _close_button; Gtk::Menu *_menu; diff --git a/src/ui/widget/preferences-widget.cpp b/src/ui/widget/preferences-widget.cpp index 1205cd012..b2cebcaa6 100644 --- a/src/ui/widget/preferences-widget.cpp +++ b/src/ui/widget/preferences-widget.cpp @@ -18,7 +18,6 @@ #include <gtkmm/frame.h> #include <gtkmm/alignment.h> #include <gtkmm/scale.h> -#include <gtkmm/stock.h> #include <gtkmm/table.h> #include "preferences.h" @@ -708,8 +707,9 @@ void PrefEntryFileButtonHBox::init(Glib::ustring const &prefs_path, relatedButton = new Gtk::Button(); Gtk::HBox* pixlabel = new Gtk::HBox(false, 3); - Gtk::Image *im = new Gtk::Image(Gtk::StockID(Gtk::Stock::INDEX), - Gtk::ICON_SIZE_BUTTON); + Gtk::Image *im = new Gtk::Image(); + im->set_from_icon_name("applications-graphics", + Gtk::ICON_SIZE_BUTTON); pixlabel->pack_start(*im); Gtk::Label *l = new Gtk::Label(); l->set_markup_with_mnemonic(_("_Browse...")); diff --git a/src/ui/widget/registered-widget.h b/src/ui/widget/registered-widget.h index d410dbfe6..f66d5cbf2 100644 --- a/src/ui/widget/registered-widget.h +++ b/src/ui/widget/registered-widget.h @@ -135,7 +135,7 @@ private: repr = NULL; doc = NULL; write_undo = false; - event_type = -1; + event_type = 0; //SP_VERB_INVALID } }; diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index ebc6fe919..0370d55db 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -781,56 +781,7 @@ void SelectedStyle::on_stroke_paste() { } void SelectedStyle::on_fillstroke_swap() { - SPCSSAttr *css = sp_repr_css_attr_new (); - - switch (_mode[SS_FILL]) { - case SS_NA: - case SS_MANY: - break; - case SS_NONE: - sp_repr_css_set_property (css, "stroke", "none"); - break; - case SS_UNSET: - sp_repr_css_unset_property (css, "stroke"); - break; - case SS_COLOR: - gchar c[64]; - sp_svg_write_color (c, sizeof(c), _thisselected[SS_FILL]); - sp_repr_css_set_property (css, "stroke", c); - break; - case SS_LGRADIENT: - case SS_RGRADIENT: - case SS_PATTERN: - sp_repr_css_set_property (css, "stroke", _paintserver_id[SS_FILL].c_str()); - break; - } - - switch (_mode[SS_STROKE]) { - case SS_NA: - case SS_MANY: - break; - case SS_NONE: - sp_repr_css_set_property (css, "fill", "none"); - break; - case SS_UNSET: - sp_repr_css_unset_property (css, "fill"); - break; - case SS_COLOR: - gchar c[64]; - sp_svg_write_color (c, sizeof(c), _thisselected[SS_STROKE]); - sp_repr_css_set_property (css, "fill", c); - break; - case SS_LGRADIENT: - case SS_RGRADIENT: - case SS_PATTERN: - sp_repr_css_set_property (css, "fill", _paintserver_id[SS_STROKE].c_str()); - break; - } - - sp_desktop_set_style (_desktop, css); - sp_repr_css_attr_unref (css); - DocumentUndo::done(_desktop->getDocument(), SP_VERB_DIALOG_FILL_STROKE, - _("Swap fill and stroke")); + _desktop->getSelection()->swapFillStroke(); } void SelectedStyle::on_fill_edit() { diff --git a/src/verbs.cpp b/src/verbs.cpp index a5426f324..aeb742105 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -37,7 +37,6 @@ // If this is not done, then errors will be generate relating to Glib::Threads being undefined #include <gtkmm/filechooserdialog.h> #include <gtkmm/messagedialog.h> -#include <gtkmm/stock.h> #include "desktop.h" @@ -1087,6 +1086,9 @@ void EditVerb::perform(SPAction *action, void *data) case SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER: sp_selection_next_patheffect_param(dt); break; + case SP_VERB_EDIT_SWAP_FILL_STROKE: + dt->selection->swapFillStroke(); + break; case SP_VERB_EDIT_LINK_COLOR_PROFILE: break; case SP_VERB_EDIT_REMOVE_COLOR_PROFILE: @@ -1115,22 +1117,22 @@ void SelectionVerb::perform(SPAction *action, void *data) bool handled = true; switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_SELECTION_UNION: - sp_selected_path_union(selection, dt); + selection->pathUnion(); break; case SP_VERB_SELECTION_INTERSECT: - sp_selected_path_intersect(selection, dt); + selection->pathIntersect(); break; case SP_VERB_SELECTION_DIFF: - sp_selected_path_diff(selection, dt); + selection->pathDiff(); break; case SP_VERB_SELECTION_SYMDIFF: - sp_selected_path_symdiff(selection, dt); + selection->pathSymDiff(); break; case SP_VERB_SELECTION_CUT: - sp_selected_path_cut(selection, dt); + selection->pathCut(); break; case SP_VERB_SELECTION_SLICE: - sp_selected_path_slice(selection, dt); + selection->pathSlice(); break; case SP_VERB_SELECTION_GROW: { @@ -2586,6 +2588,8 @@ Verb *Verb::_base_verbs[] = { N_("Create four guides aligned with the page borders"), NULL), new EditVerb(SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER, "EditNextPathEffectParameter", N_("Next path effect parameter"), N_("Show next editable path effect parameter"), INKSCAPE_ICON("path-effect-parameter-next")), + new EditVerb(SP_VERB_EDIT_SWAP_FILL_STROKE, "EditSwapFillStroke", N_("Swap fill and stroke"), + N_("Swap fill and stroke of an object"), NULL), // Selection new SelectionVerb(SP_VERB_SELECTION_TO_FRONT, "SelectionToFront", N_("Raise to _Top"), diff --git a/src/verbs.h b/src/verbs.h index d7e966ae4..76a7d19a6 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -110,6 +110,7 @@ enum { SP_VERB_EDIT_GUIDES_TOGGLE_LOCK, SP_VERB_EDIT_GUIDES_AROUND_PAGE, SP_VERB_EDIT_NEXT_PATHEFFECT_PARAMETER, + SP_VERB_EDIT_SWAP_FILL_STROKE, /* Selection */ SP_VERB_SELECTION_TO_FRONT, SP_VERB_SELECTION_TO_BACK, diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index c87fa1500..1184291f2 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -22,7 +22,6 @@ set(widgets_SRC gradient-toolbar.cpp gradient-vector.cpp icon.cpp - image-menu-item.c ink-action.cpp ink-comboboxentry-action.cpp ink-radio-action.cpp @@ -80,7 +79,6 @@ set(widgets_SRC gradient-toolbar.h gradient-vector.h icon.h - image-menu-item.h ink-action.h ink-comboboxentry-action.h ink-radio-action.h diff --git a/src/widgets/dash-selector.cpp b/src/widgets/dash-selector.cpp index 522705cec..05f3ab44c 100644 --- a/src/widgets/dash-selector.cpp +++ b/src/widgets/dash-selector.cpp @@ -174,11 +174,13 @@ void SPDashSelector::set_dash (int ndash, double *dash, double o) else if(ndash==0) { pos = 0; } - if(pos>=0){ this->set_data("pattern", dashes[pos]); this->dash_combo.set_active(pos); this->offset->set_value(o); + if(pos == 10) { + this->offset->set_value(10.0); + } } else { // Hit a custom pattern in the SVG, write it into the combobox. count--; // the one slot for custom patterns diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 4f5724ac2..cdab62f6a 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -325,13 +325,6 @@ static void canvas_tbl_size_allocate(GtkWidget * widget, { SPDesktopWidget *dtw = SP_DESKTOP_WIDGET(data); sp_desktop_widget_update_rulers(dtw); - - GtkWidget* parent = gtk_widget_get_parent(widget); - if(GTK_IS_PANED(parent)) { - GtkPaned *paned = GTK_PANED(parent); - // Could use gtk paned property 'max-position' here - gtk_paned_set_position(paned, 10000); - } } /** diff --git a/src/widgets/eraser-toolbar.cpp b/src/widgets/eraser-toolbar.cpp index 64aace4e7..7377cdc00 100644 --- a/src/widgets/eraser-toolbar.cpp +++ b/src/widgets/eraser-toolbar.cpp @@ -33,6 +33,8 @@ #include "eraser-toolbar.h" #include "calligraphy-toolbar.h" // TODO: needed for update_presets_list +#include <array> + #include "desktop.h" #include "document-undo.h" #include "widgets/ege-adjustment-action.h" diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp index 636d892f8..8946eb4b3 100644 --- a/src/widgets/fill-style.cpp +++ b/src/widgets/fill-style.cpp @@ -40,6 +40,7 @@ #include "sp-mesh-gradient.h" #include "sp-pattern.h" #include "sp-radial-gradient.h" +#include "sp-text.h" #include "style.h" #include "widgets/paint-selector.h" @@ -279,34 +280,35 @@ void FillNStroke::performUpdate() SPPaintServer *server = (kind == FILL) ? query.getFillPaintServer() : query.getStrokePaintServer(); - if (server && SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()) { - SPGradient *vector = SP_GRADIENT(server)->getVector(); - psel->setSwatch( vector ); - } else if (SP_IS_LINEARGRADIENT(server)) { - SPGradient *vector = SP_GRADIENT(server)->getVector(); - psel->setGradientLinear( vector ); - - SPLinearGradient *lg = SP_LINEARGRADIENT(server); - 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( rg->getUnits(), - rg->getSpread() ); + if (server) { + if (SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()) { + SPGradient *vector = SP_GRADIENT(server)->getVector(); + psel->setSwatch( vector ); + } else if (SP_IS_LINEARGRADIENT(server)) { + SPGradient *vector = SP_GRADIENT(server)->getVector(); + psel->setGradientLinear( vector ); + + SPLinearGradient *lg = SP_LINEARGRADIENT(server); + 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( rg->getUnits(), + rg->getSpread() ); #ifdef WITH_MESH - } else if (SP_IS_MESHGRADIENT(server)) { - SPGradient *array = SP_MESHGRADIENT(server)->getArray(); - psel->setGradientMesh( array ); - - SPMeshGradient *mg = SP_MESHGRADIENT(server); - psel->setMeshProperties( mg->getUnits() ); + } else if (SP_IS_MESHGRADIENT(server)) { + SPGradient *array = SP_GRADIENT(server)->getArray(); + psel->setGradientMesh( SP_MESHGRADIENT(array) ); + SPMeshGradient *mg = SP_MESHGRADIENT(server); + psel->updateMeshList( SP_MESHGRADIENT( array )); #endif - } else if (SP_IS_PATTERN(server)) { - SPPattern *pat = SP_PATTERN(server)->rootPattern(); - psel->updatePatternList( pat ); + } else if (SP_IS_PATTERN(server)) { + SPPattern *pat = SP_PATTERN(server)->rootPattern(); + psel->updatePatternList( pat ); + } } } break; @@ -622,6 +624,107 @@ void FillNStroke::updateFromPaint() } break; +#ifdef WITH_MESH + case SPPaintSelector::MODE_GRADIENT_MESH: + + if (!items.empty()) { + SPGradientType const gradient_type = SP_GRADIENT_TYPE_MESH; + + SPCSSAttr *css = 0; + if (kind == FILL) { + // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs + css = sp_repr_css_attr_new(); + sp_repr_css_set_property(css, "fill-opacity", "1.0"); + } + + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + SPDefs *defs = document->getDefs(); + + SPMeshGradient * mesh = psel->getMeshGradient(); + + for(std::vector<SPItem*>::const_iterator i=items.begin();i!=items.end(); ++i){ + + //FIXME: see above + if (kind == FILL) { + sp_repr_css_change_recursive((*i)->getRepr(), css, "style"); + } + + // Check if object already has mesh. + bool has_mesh = false; + SPStyle *style = (*i)->style; + if (style) { + SPPaintServer *server = + (kind==FILL) ? style->getFillPaintServer():style->getStrokePaintServer(); + if (server && SP_IS_MESHGRADIENT(server)) + has_mesh = true; + } + + if (!mesh || !has_mesh) { + // No mesh in document or object does not already have mesh -> + // Create new mesh. + + // Create mesh element + Inkscape::XML::Node *repr = xml_doc->createElement("svg:meshgradient"); + + // privates are garbage-collectable + repr->setAttribute("inkscape:collect", "always"); + + // Attach to document + defs->getRepr()->appendChild(repr); + Inkscape::GC::release(repr); + + // Get corresponding object + SPMeshGradient *mg = static_cast<SPMeshGradient *>(document->getObjectByRepr(repr)); + mg->array.create(mg, *i, (kind==FILL) ? + (*i)->geometricBounds() : (*i)->visualBounds()); + + bool isText = SP_IS_TEXT(*i); + sp_style_set_property_url (*i, ((kind == FILL) ? "fill":"stroke"), + mg, isText); + + // (*i)->requestModified(SP_OBJECT_MODIFIED_FLAG|SP_OBJECT_STYLE_MODIFIED_FLAG); + + } else { + // Using found mesh + + // Duplicate + Inkscape::XML::Node *mesh_repr = mesh->getRepr(); + Inkscape::XML::Node *copy_repr = mesh_repr->duplicate(xml_doc); + + // privates are garbage-collectable + copy_repr->setAttribute("inkscape:collect", "always"); + + // Attach to document + defs->getRepr()->appendChild(copy_repr); + Inkscape::GC::release(copy_repr); + + // Get corresponding object + SPMeshGradient *mg = + static_cast<SPMeshGradient *>(document->getObjectByRepr(copy_repr)); + // std::cout << " " << (mg->getId()?mg->getId():"null") << std::endl; + mg->array.read(mg); + + Geom::OptRect item_bbox = (kind==FILL) ? + (*i)->geometricBounds() : (*i)->visualBounds(); + mg->array.fill_box( item_bbox ); + + bool isText = SP_IS_TEXT(*i); + sp_style_set_property_url (*i, ((kind == FILL) ? "fill":"stroke"), + mg, isText); + } + } + + if (css) { + sp_repr_css_attr_unref(css); + css = 0; + } + + DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE, + (kind == FILL) ? _("Set mesh on fill") : _("Set mesh on stroke")); + } + break; +#endif + case SPPaintSelector::MODE_PATTERN: if (!items.empty()) { diff --git a/src/widgets/font-selector.cpp b/src/widgets/font-selector.cpp index f400de89c..819ac77b6 100644 --- a/src/widgets/font-selector.cpp +++ b/src/widgets/font-selector.cpp @@ -145,13 +145,6 @@ static void sp_font_selector_init(SPFontSelector *fsel) gtk_widget_set_name( GTK_WIDGET(fsel->family_treeview), "font_selector_family" ); auto css_provider = gtk_css_provider_new(); - gtk_css_provider_load_from_data(css_provider, - "#font_selector_family {\n" - " -GtkWidget-wide-separators: true;\n" - " -GtkWidget-separator-height: 6;\n" - "}\n", - -1, NULL); - auto screen = gdk_screen_get_default(); gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(css_provider), diff --git a/src/widgets/gimp/ruler.cpp b/src/widgets/gimp/ruler.cpp index bfb9c9071..2a71b5c08 100644 --- a/src/widgets/gimp/ruler.cpp +++ b/src/widgets/gimp/ruler.cpp @@ -12,7 +12,9 @@ * - We use a default font size of PANGO_SCALE_X_SMALL for labels, * GIMP uses PANGO_SCALE_SMALL (i.e., a bit larger than ours). * - * - We abbreviate large numbers in tick-labels (e.g., 10000 -> 10k) + * - We abbreviate large numbers in tick-labels (e.g., 10000 -> 10k) + * + * - GtkStateFlags are read from GtkStyleContext objects where appropriate * * Authors: * Lauris Kaplinski <lauris@kaplinski.com> @@ -548,7 +550,7 @@ sp_ruler_size_allocate (GtkWidget *widget, resized = (widget_allocation.width != allocation->width || widget_allocation.height != allocation->height); - gtk_widget_set_allocation(widget, allocation); + GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation); if (gtk_widget_get_realized (widget)) { @@ -578,7 +580,9 @@ sp_ruler_size_request (GtkWidget *widget, GtkStyleContext *context = gtk_widget_get_style_context (widget); GtkBorder border; - gtk_style_context_get_border (context, static_cast<GtkStateFlags>(0), &border); + GtkStateFlags state_flags = gtk_style_context_get_state (context); + + gtk_style_context_get_border (context, state_flags, &border); requisition->width = border.left + border.right; requisition->height = border.top + border.bottom; @@ -725,7 +729,17 @@ sp_ruler_draw_pos (SPRuler *ruler, cairo_fill (cr); } - priv->last_pos_rect = pos_rect; + if (priv->last_pos_rect.width != 0 && + priv->last_pos_rect.height != 0) + { + gdk_rectangle_union (&priv->last_pos_rect, + &pos_rect, + &priv->last_pos_rect); + } + else + { + priv->last_pos_rect = pos_rect; + } } /** @@ -999,6 +1013,12 @@ sp_ruler_set_position (SPRuler *ruler, (ABS (xdiff) > IMMEDIATE_REDRAW_THRESHOLD || ABS (ydiff) > IMMEDIATE_REDRAW_THRESHOLD)) { + if (priv->pos_redraw_idle_id) + { + g_source_remove (priv->pos_redraw_idle_id); + priv->pos_redraw_idle_id = 0; + } + sp_ruler_queue_pos_redraw (ruler); } else if (! priv->pos_redraw_idle_id) @@ -1072,7 +1092,9 @@ sp_ruler_draw_ticks (SPRuler *ruler) gtk_widget_get_allocation (widget, &allocation); - gtk_style_context_get_border (context, static_cast<GtkStateFlags>(0), &border); + GtkStateFlags state_flags = gtk_style_context_get_state (context); + + gtk_style_context_get_border (context, state_flags, &border); layout = sp_ruler_get_layout (widget, "0123456789"); pango_layout_get_extents (layout, &ink_rect, &logical_rect); @@ -1284,7 +1306,9 @@ sp_ruler_get_pos_rect (SPRuler *ruler, GtkStyleContext *context = gtk_widget_get_style_context (widget); GtkBorder padding; - gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &padding); + GtkStateFlags state_flags = gtk_style_context_get_state (context); + + gtk_style_context_get_border(context, state_flags, &padding); xthickness = padding.left + padding.right; ythickness = padding.top + padding.bottom; @@ -1351,15 +1375,16 @@ sp_ruler_queue_pos_redraw (SPRuler *ruler) gtk_widget_get_allocation (GTK_WIDGET(ruler), &allocation); - gtk_widget_queue_draw_area (GTK_WIDGET(ruler), + gtk_widget_queue_draw_area (GTK_WIDGET (ruler), rect.x + allocation.x, rect.y + allocation.y, rect.width, rect.height); - if (priv->last_pos_rect.width != 0 || priv->last_pos_rect.height != 0) + if (priv->last_pos_rect.width != 0 && + priv->last_pos_rect.height != 0) { - gtk_widget_queue_draw_area (GTK_WIDGET(ruler), + gtk_widget_queue_draw_area (GTK_WIDGET (ruler), priv->last_pos_rect.x + allocation.x, priv->last_pos_rect.y + allocation.y, priv->last_pos_rect.width, diff --git a/src/widgets/gradient-selector.h b/src/widgets/gradient-selector.h index 6b5d4ca60..e058c5112 100644 --- a/src/widgets/gradient-selector.h +++ b/src/widgets/gradient-selector.h @@ -50,9 +50,6 @@ struct SPGradientSelector { enum SelectorMode { MODE_LINEAR, MODE_RADIAL, -#ifdef WITH_MESH - MODE_MESH, -#endif MODE_SWATCH }; diff --git a/src/widgets/image-menu-item.c b/src/widgets/image-menu-item.c deleted file mode 100644 index 2b9500ba0..000000000 --- a/src/widgets/image-menu-item.c +++ /dev/null @@ -1,1071 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2001 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. - * Forked by . Icons in menus are important to us. - */ - -#include "config.h" - -#include <glib/gi18n-lib.h> -#include <gtk/gtk.h> - -#include "widgets/image-menu-item.h" - -/** - * SECTION:gtkimagemenuitem - * @Short_description: A menu item with an icon - * @Title: ImageMenuItem - * - * A ImageMenuItem is a menu item which has an icon next to the text label. - * - * Note that the user can disable display of menu icons, so make sure to still - * fill in the text label. - */ - - -struct _ImageMenuItemPrivate -{ - GtkWidget *image; - - gchar *label; - guint use_stock : 1; - guint toggle_size; -}; - -enum { - PROP_0, - PROP_IMAGE, - PROP_USE_STOCK, - PROP_ACCEL_GROUP, -}; - -static GtkActivatableIface *parent_activatable_iface; - -static void image_menu_item_destroy (GtkWidget *widget); -static void image_menu_item_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural); -static void image_menu_item_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural); -static void image_menu_item_get_preferred_height_for_width (GtkWidget *widget, - gint width, - gint *minimum, - gint *natural); -static void image_menu_item_size_allocate (GtkWidget *widget, - GtkAllocation *allocation); -static void image_menu_item_map (GtkWidget *widget); -static void image_menu_item_remove (GtkContainer *container, - GtkWidget *child); -static void image_menu_item_toggle_size_request (GtkMenuItem *menu_item, - gint *requisition); -static void image_menu_item_set_label (GtkMenuItem *menu_item, - const gchar *label); -static const gchar * image_menu_item_get_label (GtkMenuItem *menu_item); - -static void image_menu_item_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data); - -static void image_menu_item_finalize (GObject *object); -static void image_menu_item_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void image_menu_item_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void image_menu_item_screen_changed (GtkWidget *widget, - GdkScreen *previous_screen); - -static void image_menu_item_recalculate (ImageMenuItem *image_menu_item); - -static void image_menu_item_activatable_interface_init (GtkActivatableIface *iface); -static void image_menu_item_update (GtkActivatable *activatable, - GtkAction *action, - const gchar *property_name); -static void image_menu_item_sync_action_properties (GtkActivatable *activatable, - GtkAction *action); - - -G_DEFINE_TYPE_WITH_CODE (ImageMenuItem, image_menu_item, GTK_TYPE_MENU_ITEM, - G_ADD_PRIVATE (ImageMenuItem) - G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE, - image_menu_item_activatable_interface_init)) - - -static void -image_menu_item_class_init (ImageMenuItemClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass*) klass; - GtkWidgetClass *widget_class = (GtkWidgetClass*) klass; - GtkMenuItemClass *menu_item_class = (GtkMenuItemClass*) klass; - GtkContainerClass *container_class = (GtkContainerClass*) klass; - - widget_class->destroy = image_menu_item_destroy; - widget_class->screen_changed = image_menu_item_screen_changed; - widget_class->get_preferred_width = image_menu_item_get_preferred_width; - widget_class->get_preferred_height = image_menu_item_get_preferred_height; - widget_class->get_preferred_height_for_width = image_menu_item_get_preferred_height_for_width; - widget_class->size_allocate = image_menu_item_size_allocate; - widget_class->map = image_menu_item_map; - - container_class->forall = image_menu_item_forall; - container_class->remove = image_menu_item_remove; - - menu_item_class->toggle_size_request = image_menu_item_toggle_size_request; - menu_item_class->set_label = image_menu_item_set_label; - menu_item_class->get_label = image_menu_item_get_label; - - gobject_class->finalize = image_menu_item_finalize; - gobject_class->set_property = image_menu_item_set_property; - gobject_class->get_property = image_menu_item_get_property; - - /** - * ImageMenuItem:image: - * - * Child widget to appear next to the menu text. - * - */ - g_object_class_install_property (gobject_class, - PROP_IMAGE, - g_param_spec_object ("image", - _("Image widget"), - _("Child widget to appear next to the menu text"), - GTK_TYPE_WIDGET, - GTK_PARAM_READWRITE | G_PARAM_DEPRECATED)); - /** - * ImageMenuItem:use-stock: - * - * If %TRUE, the label set in the menuitem is used as a - * stock id to select the stock item for the item. - * - * Since: 2.16 - * - */ - g_object_class_install_property (gobject_class, - PROP_USE_STOCK, - g_param_spec_boolean ("use-stock", - _("Use stock"), - _("Whether to use the label text to create a stock menu item"), - FALSE, - GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_DEPRECATED)); - - /** - * ImageMenuItem:accel-group: - * - * The Accel Group to use for stock accelerator keys - * - * Since: 2.16 - * - */ - g_object_class_install_property (gobject_class, - PROP_ACCEL_GROUP, - g_param_spec_object ("accel-group", - _("Accel Group"), - _("The Accel Group to use for stock accelerator keys"), - GTK_TYPE_ACCEL_GROUP, - GTK_PARAM_WRITABLE | G_PARAM_DEPRECATED)); - -} - -static void -image_menu_item_init (ImageMenuItem *image_menu_item) -{ - ImageMenuItemPrivate *priv; - - image_menu_item->priv = image_menu_item_get_instance_private (image_menu_item); - priv = image_menu_item->priv; - - priv->image = NULL; - priv->use_stock = FALSE; - priv->label = NULL; -} - -static void -image_menu_item_finalize (GObject *object) -{ - ImageMenuItemPrivate *priv = IMAGE_MENU_ITEM (object)->priv; - - g_free (priv->label); - priv->label = NULL; - - G_OBJECT_CLASS (image_menu_item_parent_class)->finalize (object); -} - -static void -image_menu_item_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (object); - - switch (prop_id) - { - case PROP_IMAGE: - image_menu_item_set_image (image_menu_item, (GtkWidget *) g_value_get_object (value)); - break; - case PROP_USE_STOCK: - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - image_menu_item_set_use_stock (image_menu_item, g_value_get_boolean (value)); - G_GNUC_END_IGNORE_DEPRECATIONS; - break; - case PROP_ACCEL_GROUP: - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - image_menu_item_set_accel_group (image_menu_item, g_value_get_object (value)); - G_GNUC_END_IGNORE_DEPRECATIONS; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -image_menu_item_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (object); - - switch (prop_id) - { - case PROP_IMAGE: - g_value_set_object (value, image_menu_item_get_image (image_menu_item)); - break; - case PROP_USE_STOCK: - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - g_value_set_boolean (value, image_menu_item_get_use_stock (image_menu_item)); - G_GNUC_END_IGNORE_DEPRECATIONS; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -image_menu_item_map (GtkWidget *widget) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (widget); - ImageMenuItemPrivate *priv = image_menu_item->priv; - - GTK_WIDGET_CLASS (image_menu_item_parent_class)->map (widget); - - if (priv->image) - g_object_set (priv->image, "visible", TRUE, NULL); -} - -static void -image_menu_item_destroy (GtkWidget *widget) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (widget); - ImageMenuItemPrivate *priv = image_menu_item->priv; - - if (priv->image) - gtk_container_remove (GTK_CONTAINER (image_menu_item), - priv->image); - - GTK_WIDGET_CLASS (image_menu_item_parent_class)->destroy (widget); -} - -static void -image_menu_item_toggle_size_request (GtkMenuItem *menu_item, - gint *requisition) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (menu_item); - ImageMenuItemPrivate *priv = image_menu_item->priv; - GtkPackDirection pack_dir; - GtkWidget *parent; - GtkWidget *widget = GTK_WIDGET (menu_item); - - parent = gtk_widget_get_parent (widget); - - if (GTK_IS_MENU_BAR (parent)) - pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); - else - pack_dir = GTK_PACK_DIRECTION_LTR; - - *requisition = 0; - - if (priv->image && gtk_widget_get_visible (priv->image)) - { - GtkRequisition image_requisition; - guint toggle_spacing; - - gtk_widget_get_preferred_size (priv->image, &image_requisition, NULL); - - gtk_widget_style_get (GTK_WIDGET (menu_item), - "toggle-spacing", &toggle_spacing, - NULL); - - if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) - { - if (image_requisition.width > 0) - *requisition = image_requisition.width + toggle_spacing; - } - else - { - if (image_requisition.height > 0) - *requisition = image_requisition.height + toggle_spacing; - } - } -} - -static void -image_menu_item_recalculate (ImageMenuItem *image_menu_item) -{ - ImageMenuItemPrivate *priv = image_menu_item->priv; - GtkStockItem stock_item; - GtkWidget *image; - const gchar *resolved_label = priv->label; - - if (priv->use_stock && priv->label) - { - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if (!priv->image) - { - image = gtk_image_new_from_stock (priv->label, GTK_ICON_SIZE_MENU); - image_menu_item_set_image (image_menu_item, image); - } - - if (gtk_stock_lookup (priv->label, &stock_item)) - resolved_label = stock_item.label; - - gtk_menu_item_set_use_underline (GTK_MENU_ITEM (image_menu_item), TRUE); - - G_GNUC_END_IGNORE_DEPRECATIONS; - } - - GTK_MENU_ITEM_CLASS - (image_menu_item_parent_class)->set_label (GTK_MENU_ITEM (image_menu_item), resolved_label); - -} - -static void -image_menu_item_set_label (GtkMenuItem *menu_item, - const gchar *label) -{ - ImageMenuItemPrivate *priv = IMAGE_MENU_ITEM (menu_item)->priv; - - if (priv->label != label) - { - g_free (priv->label); - priv->label = g_strdup (label); - - image_menu_item_recalculate (IMAGE_MENU_ITEM (menu_item)); - - g_object_notify (G_OBJECT (menu_item), "label"); - - } -} - -static const gchar * -image_menu_item_get_label (GtkMenuItem *menu_item) -{ - ImageMenuItemPrivate *priv = IMAGE_MENU_ITEM (menu_item)->priv; - - return priv->label; -} - -static void -image_menu_item_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (widget); - ImageMenuItemPrivate *priv = image_menu_item->priv; - GtkPackDirection pack_dir; - GtkWidget *parent; - - parent = gtk_widget_get_parent (widget); - - if (GTK_IS_MENU_BAR (parent)) - pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); - else - pack_dir = GTK_PACK_DIRECTION_LTR; - - GTK_WIDGET_CLASS (image_menu_item_parent_class)->get_preferred_width (widget, minimum, natural); - - if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) && - priv->image && - gtk_widget_get_visible (priv->image)) - { - gint child_minimum, child_natural; - - gtk_widget_get_preferred_width (priv->image, &child_minimum, &child_natural); - - *minimum = MAX (*minimum, child_minimum); - *natural = MAX (*natural, child_natural); - } -} - -static void -image_menu_item_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (widget); - ImageMenuItemPrivate *priv = image_menu_item->priv; - gint child_height = 0; - GtkPackDirection pack_dir; - GtkWidget *parent; - - parent = gtk_widget_get_parent (widget); - - if (GTK_IS_MENU_BAR (parent)) - pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); - else - pack_dir = GTK_PACK_DIRECTION_LTR; - - if (priv->image && gtk_widget_get_visible (priv->image)) - { - GtkRequisition child_requisition; - - gtk_widget_get_preferred_size (priv->image, &child_requisition, NULL); - - child_height = child_requisition.height; - } - - GTK_WIDGET_CLASS (image_menu_item_parent_class)->get_preferred_height (widget, minimum, natural); - - if (pack_dir == GTK_PACK_DIRECTION_RTL || pack_dir == GTK_PACK_DIRECTION_LTR) - { - *minimum = MAX (*minimum, child_height); - *natural = MAX (*natural, child_height); - } -} - -static void -image_menu_item_get_preferred_height_for_width (GtkWidget *widget, - gint width, - gint *minimum, - gint *natural) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (widget); - ImageMenuItemPrivate *priv = image_menu_item->priv; - gint child_height = 0; - GtkPackDirection pack_dir; - GtkWidget *parent; - - parent = gtk_widget_get_parent (widget); - - if (GTK_IS_MENU_BAR (parent)) - pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); - else - pack_dir = GTK_PACK_DIRECTION_LTR; - - if (priv->image && gtk_widget_get_visible (priv->image)) - { - GtkRequisition child_requisition; - - gtk_widget_get_preferred_size (priv->image, &child_requisition, NULL); - - child_height = child_requisition.height; - } - - GTK_WIDGET_CLASS - (image_menu_item_parent_class)->get_preferred_height_for_width (widget, width, minimum, natural); - - if (pack_dir == GTK_PACK_DIRECTION_RTL || pack_dir == GTK_PACK_DIRECTION_LTR) - { - *minimum = MAX (*minimum, child_height); - *natural = MAX (*natural, child_height); - } -} - - -static void -image_menu_item_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (widget); - ImageMenuItemPrivate *priv = image_menu_item->priv; - GtkAllocation widget_allocation; - GtkPackDirection pack_dir; - GtkWidget *parent; - - parent = gtk_widget_get_parent (widget); - - if (GTK_IS_MENU_BAR (parent)) - pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent)); - else - pack_dir = GTK_PACK_DIRECTION_LTR; - - GTK_WIDGET_CLASS (image_menu_item_parent_class)->size_allocate (widget, allocation); - - if (priv->image && gtk_widget_get_visible (priv->image)) - { - gint x, y, offset; - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding; - GtkRequisition child_requisition; - GtkAllocation child_allocation; - guint horizontal_padding, toggle_spacing; - gint toggle_size; - - toggle_size = image_menu_item->priv->toggle_size; - gtk_widget_style_get (widget, - "horizontal-padding", &horizontal_padding, - "toggle-spacing", &toggle_spacing, - NULL); - - /* Man this is lame hardcoding action, but I can't - * come up with a solution that's really better. - */ - - gtk_widget_get_preferred_size (priv->image, &child_requisition, NULL); - - gtk_widget_get_allocation (widget, &widget_allocation); - - context = gtk_widget_get_style_context (widget); - state = gtk_widget_get_state_flags (widget); - gtk_style_context_get_padding (context, state, &padding); - offset = gtk_container_get_border_width (GTK_CONTAINER (image_menu_item)); - - if (pack_dir == GTK_PACK_DIRECTION_LTR || - pack_dir == GTK_PACK_DIRECTION_RTL) - { - if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) == - (pack_dir == GTK_PACK_DIRECTION_LTR)) - x = offset + horizontal_padding + padding.left + - (toggle_size - toggle_spacing - child_requisition.width) / 2; - else - x = widget_allocation.width - offset - horizontal_padding - padding.right - - toggle_size + toggle_spacing + - (toggle_size - toggle_spacing - child_requisition.width) / 2; - - y = (widget_allocation.height - child_requisition.height) / 2; - } - else - { - if ((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) == - (pack_dir == GTK_PACK_DIRECTION_TTB)) - y = offset + horizontal_padding + padding.top + - (toggle_size - toggle_spacing - child_requisition.height) / 2; - else - y = widget_allocation.height - offset - horizontal_padding - padding.bottom - - toggle_size + toggle_spacing + - (toggle_size - toggle_spacing - child_requisition.height) / 2; - - x = (widget_allocation.width - child_requisition.width) / 2; - } - - child_allocation.width = child_requisition.width; - child_allocation.height = child_requisition.height; - child_allocation.x = widget_allocation.x + MAX (x, 0); - child_allocation.y = widget_allocation.y + MAX (y, 0); - - gtk_widget_size_allocate (priv->image, &child_allocation); - } -} - -static void -image_menu_item_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (container); - ImageMenuItemPrivate *priv = image_menu_item->priv; - - GTK_CONTAINER_CLASS (image_menu_item_parent_class)->forall (container, - include_internals, - callback, - callback_data); - - if (include_internals && priv->image) - (* callback) (priv->image, callback_data); -} - - -static void -image_menu_item_activatable_interface_init (GtkActivatableIface *iface) -{ - parent_activatable_iface = g_type_interface_peek_parent (iface); - iface->update = image_menu_item_update; - iface->sync_action_properties = image_menu_item_sync_action_properties; -} - -static gboolean -activatable_update_stock_id (ImageMenuItem *image_menu_item, GtkAction *action) -{ - GtkWidget *image; - const gchar *stock_id = gtk_action_get_stock_id (action); - - image = image_menu_item_get_image (image_menu_item); - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if (GTK_IS_IMAGE (image) && - stock_id && gtk_icon_factory_lookup_default (stock_id)) - { - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - gtk_image_set_from_stock (GTK_IMAGE (image), stock_id, GTK_ICON_SIZE_MENU); - G_GNUC_END_IGNORE_DEPRECATIONS; - return TRUE; - } - - G_GNUC_END_IGNORE_DEPRECATIONS; - - return FALSE; -} - -static gboolean -activatable_update_gicon (ImageMenuItem *image_menu_item, GtkAction *action) -{ - GtkWidget *image; - GIcon *icon = gtk_action_get_gicon (action); - const gchar *stock_id; - gboolean ret = FALSE; - - stock_id = gtk_action_get_stock_id (action); - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - image = image_menu_item_get_image (image_menu_item); - - if (icon && GTK_IS_IMAGE (image) && - !(stock_id && gtk_icon_factory_lookup_default (stock_id))) - { - gtk_image_set_from_gicon (GTK_IMAGE (image), icon, GTK_ICON_SIZE_MENU); - ret = TRUE; - } - - G_GNUC_END_IGNORE_DEPRECATIONS; - - return ret; -} - -static void -activatable_update_icon_name (ImageMenuItem *image_menu_item, GtkAction *action) -{ - GtkWidget *image; - const gchar *icon_name = gtk_action_get_icon_name (action); - - image = image_menu_item_get_image (image_menu_item); - - if (GTK_IS_IMAGE (image) && - (gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_EMPTY || - gtk_image_get_storage_type (GTK_IMAGE (image)) == GTK_IMAGE_ICON_NAME)) - { - gtk_image_set_from_icon_name (GTK_IMAGE (image), icon_name, GTK_ICON_SIZE_MENU); - } -} - -static void -image_menu_item_update (GtkActivatable *activatable, - GtkAction *action, - const gchar *property_name) -{ - ImageMenuItem *image_menu_item; - gboolean use_appearance; - - image_menu_item = IMAGE_MENU_ITEM (activatable); - - parent_activatable_iface->update (activatable, action, property_name); - - use_appearance = gtk_activatable_get_use_action_appearance (activatable); - if (!use_appearance) - return; - - if (strcmp (property_name, "stock-id") == 0) - activatable_update_stock_id (image_menu_item, action); - else if (strcmp (property_name, "gicon") == 0) - activatable_update_gicon (image_menu_item, action); - else if (strcmp (property_name, "icon-name") == 0) - activatable_update_icon_name (image_menu_item, action); -} - -static void -image_menu_item_sync_action_properties (GtkActivatable *activatable, - GtkAction *action) -{ - ImageMenuItem *image_menu_item; - GtkWidget *image; - gboolean use_appearance; - - image_menu_item = IMAGE_MENU_ITEM (activatable); - - parent_activatable_iface->sync_action_properties (activatable, action); - - if (!action) - return; - - use_appearance = gtk_activatable_get_use_action_appearance (activatable); - if (!use_appearance) - return; - - image = image_menu_item_get_image (image_menu_item); - if (image && !GTK_IS_IMAGE (image)) - { - image_menu_item_set_image (image_menu_item, NULL); - image = NULL; - } - - if (!image) - { - image = gtk_image_new (); - gtk_widget_show (image); - image_menu_item_set_image (IMAGE_MENU_ITEM (activatable), - image); - } - - if (!activatable_update_stock_id (image_menu_item, action) && - !activatable_update_gicon (image_menu_item, action)) - activatable_update_icon_name (image_menu_item, action); - -} - - -/** - * image_menu_item_new: - * - * Creates a new #ImageMenuItem with an empty label. - * - * Returns: a new #ImageMenuItem - * - */ -GtkWidget* -image_menu_item_new (void) -{ - return g_object_new (TYPE_IMAGE_MENU_ITEM, NULL); -} - -/** - * image_menu_item_new_with_label: - * @label: the text of the menu item. - * - * Creates a new #ImageMenuItem containing a label. - * - * Returns: a new #ImageMenuItem. - * - */ -GtkWidget* -image_menu_item_new_with_label (const gchar *label) -{ - return g_object_new (TYPE_IMAGE_MENU_ITEM, - "label", label, - NULL); -} - -/** - * image_menu_item_new_with_mnemonic: - * @label: the text of the menu item, with an underscore in front of the - * mnemonic character - * - * Creates a new #ImageMenuItem containing a label. The label - * will be created using gtk_label_new_with_mnemonic(), so underscores - * in @label indicate the mnemonic for the menu item. - * - * Returns: a new #ImageMenuItem - * - */ -GtkWidget* -image_menu_item_new_with_mnemonic (const gchar *label) -{ - return g_object_new (TYPE_IMAGE_MENU_ITEM, - "use-underline", TRUE, - "label", label, - NULL); -} - -/** - * image_menu_item_new_from_stock: - * @stock_id: the name of the stock item. - * @accel_group: (allow-none): the #GtkAccelGroup to add the menu items - * accelerator to, or %NULL. - * - * Creates a new #ImageMenuItem containing the image and text from a - * stock item. Some stock ids have preprocessor macros like #STOCK_OK - * and #STOCK_APPLY. - * - * If you want this menu item to have changeable accelerators, then pass in - * %NULL for accel_group. Next call gtk_menu_item_set_accel_path() with an - * appropriate path for the menu item, use gtk_stock_lookup() to look up the - * standard accelerator for the stock item, and if one is found, call - * gtk_accel_map_add_entry() to register it. - * - * Returns: a new #ImageMenuItem. - * - */ -GtkWidget* -image_menu_item_new_from_stock (const gchar *stock_id, - GtkAccelGroup *accel_group) -{ - return g_object_new (TYPE_IMAGE_MENU_ITEM, - "label", stock_id, - "use-stock", TRUE, - "accel-group", accel_group, - NULL); -} - -/** - * image_menu_item_set_use_stock: - * @image_menu_item: a #ImageMenuItem - * @use_stock: %TRUE if the menuitem should use a stock item - * - * If %TRUE, the label set in the menuitem is used as a - * stock id to select the stock item for the item. - * - * Since: 2.16 - * - */ -void -image_menu_item_set_use_stock (ImageMenuItem *image_menu_item, - gboolean use_stock) -{ - ImageMenuItemPrivate *priv; - - g_return_if_fail (IS_IMAGE_MENU_ITEM (image_menu_item)); - - priv = image_menu_item->priv; - - if (priv->use_stock != use_stock) - { - priv->use_stock = use_stock; - - image_menu_item_recalculate (image_menu_item); - - g_object_notify (G_OBJECT (image_menu_item), "use-stock"); - } -} - -/** - * image_menu_item_get_use_stock: - * @image_menu_item: a #ImageMenuItem - * - * Checks whether the label set in the menuitem is used as a - * stock id to select the stock item for the item. - * - * Returns: %TRUE if the label set in the menuitem is used as a - * stock id to select the stock item for the item - * - * Since: 2.16 - * - */ -gboolean -image_menu_item_get_use_stock (ImageMenuItem *image_menu_item) -{ - g_return_val_if_fail (IS_IMAGE_MENU_ITEM (image_menu_item), FALSE); - - return image_menu_item->priv->use_stock; -} - -/** - * image_menu_item_set_accel_group: - * @image_menu_item: a #ImageMenuItem - * @accel_group: the #GtkAccelGroup - * - * Specifies an @accel_group to add the menu items accelerator to - * (this only applies to stock items so a stock item must already - * be set, make sure to call image_menu_item_set_use_stock() - * and gtk_menu_item_set_label() with a valid stock item first). - * - * If you want this menu item to have changeable accelerators then - * you shouldnt need this (see image_menu_item_new_from_stock()). - * - * Since: 2.16 - * - */ -void -image_menu_item_set_accel_group (ImageMenuItem *image_menu_item, - GtkAccelGroup *accel_group) -{ - ImageMenuItemPrivate *priv; - GtkStockItem stock_item; - - /* Silent return for the constructor */ - if (!accel_group) - return; - - g_return_if_fail (IS_IMAGE_MENU_ITEM (image_menu_item)); - g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group)); - - priv = image_menu_item->priv; - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - - if (priv->use_stock && priv->label && gtk_stock_lookup (priv->label, &stock_item)) - if (stock_item.keyval) - { - gtk_widget_add_accelerator (GTK_WIDGET (image_menu_item), - "activate", - accel_group, - stock_item.keyval, - stock_item.modifier, - GTK_ACCEL_VISIBLE); - - g_object_notify (G_OBJECT (image_menu_item), "accel-group"); - } - - G_GNUC_END_IGNORE_DEPRECATIONS; - -} - -/** - * image_menu_item_set_image: - * @image_menu_item: a #ImageMenuItem. - * @image: (allow-none): a widget to set as the image for the menu item. - * - * Sets the image of @image_menu_item to the given widget. - * Note that it depends on the show-menu-images setting whether - * the image will be displayed or not. - * - */ -void -image_menu_item_set_image (ImageMenuItem *image_menu_item, - GtkWidget *image) -{ - ImageMenuItemPrivate *priv; - - g_return_if_fail (IS_IMAGE_MENU_ITEM (image_menu_item)); - - priv = image_menu_item->priv; - - if (image == priv->image) - return; - - if (priv->image) - gtk_container_remove (GTK_CONTAINER (image_menu_item), - priv->image); - - priv->image = image; - - if (image == NULL) - return; - - gtk_widget_set_parent (image, GTK_WIDGET (image_menu_item)); - g_object_set (image, "visible", TRUE, "no-show-all", TRUE, NULL); - - g_object_notify (G_OBJECT (image_menu_item), "image"); -} - -/** - * image_menu_item_get_image: - * @image_menu_item: a #ImageMenuItem - * - * Gets the widget that is currently set as the image of @image_menu_item. - * See image_menu_item_set_image(). - * - * Return value: (transfer none): the widget set as image of @image_menu_item - * - **/ -GtkWidget* -image_menu_item_get_image (ImageMenuItem *image_menu_item) -{ - g_return_val_if_fail (IS_IMAGE_MENU_ITEM (image_menu_item), NULL); - - return image_menu_item->priv->image; -} - -static void -image_menu_item_remove (GtkContainer *container, - GtkWidget *child) -{ - ImageMenuItem *image_menu_item = IMAGE_MENU_ITEM (container); - ImageMenuItemPrivate *priv = image_menu_item->priv; - - if (child == priv->image) - { - gboolean widget_was_visible; - - widget_was_visible = gtk_widget_get_visible (child); - - gtk_widget_unparent (child); - priv->image = NULL; - - if (widget_was_visible && - gtk_widget_get_visible (GTK_WIDGET (container))) - gtk_widget_queue_resize (GTK_WIDGET (container)); - - g_object_notify (G_OBJECT (image_menu_item), "image"); - } - else - { - GTK_CONTAINER_CLASS (image_menu_item_parent_class)->remove (container, child); - } -} - -static void -show_image_change_notify (ImageMenuItem *image_menu_item) -{ - ImageMenuItemPrivate *priv = image_menu_item->priv; - - if (priv->image) - { - gtk_widget_show (priv->image); - } -} - -static void -traverse_container (GtkWidget *widget, - gpointer data) -{ - if (IS_IMAGE_MENU_ITEM (widget)) - show_image_change_notify (IMAGE_MENU_ITEM (widget)); - else if (GTK_IS_CONTAINER (widget)) - gtk_container_forall (GTK_CONTAINER (widget), traverse_container, NULL); -} - -static void -image_menu_item_setting_changed (GtkSettings *settings) -{ - GList *list, *l; - - list = gtk_window_list_toplevels (); - - for (l = list; l; l = l->next) - gtk_container_forall (GTK_CONTAINER (l->data), - traverse_container, NULL); - - g_list_free (list); -} - -static void -image_menu_item_screen_changed (GtkWidget *widget, - GdkScreen *previous_screen) -{ - GtkSettings *settings; - gulong show_image_connection; - - if (!gtk_widget_has_screen (widget)) - return; - - settings = gtk_widget_get_settings (widget); - - show_image_connection = - g_signal_handler_find (settings, G_SIGNAL_MATCH_FUNC, 0, 0, - NULL, image_menu_item_setting_changed, NULL); - - if (show_image_connection) - return; - - g_signal_connect (settings, "notify::gtk-menu-images", - G_CALLBACK (image_menu_item_setting_changed), NULL); - - show_image_change_notify (IMAGE_MENU_ITEM (widget)); -} diff --git a/src/widgets/image-menu-item.h b/src/widgets/image-menu-item.h deleted file mode 100644 index 61cc48f3a..000000000 --- a/src/widgets/image-menu-item.h +++ /dev/null @@ -1,81 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. - * Forked for , icons in menus are important to us. - */ - -#ifndef __IMAGE_MENU_ITEM_H__ -#define __IMAGE_MENU_ITEM_H__ - -#include <gtk/gtk.h> - -G_BEGIN_DECLS - -#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB -#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB -#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB - -#define TYPE_IMAGE_MENU_ITEM (image_menu_item_get_type ()) -#define IMAGE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_IMAGE_MENU_ITEM, ImageMenuItem)) -#define IMAGE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_IMAGE_MENU_ITEM, ImageMenuItemClass)) -#define IS_IMAGE_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_IMAGE_MENU_ITEM)) -#define IS_IMAGE_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_IMAGE_MENU_ITEM)) -#define IMAGE_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_IMAGE_MENU_ITEM, ImageMenuItemClass)) - -typedef struct _ImageMenuItem ImageMenuItem; -typedef struct _ImageMenuItemPrivate ImageMenuItemPrivate; -typedef struct _ImageMenuItemClass ImageMenuItemClass; - -struct _ImageMenuItem -{ - GtkMenuItem menu_item; - - /*< private >*/ - ImageMenuItemPrivate *priv; -}; - -struct _ImageMenuItemClass -{ - GtkMenuItemClass parent_class; - - /* Padding for future expansion */ - void (*_gtk_reserved1) (void); - void (*_gtk_reserved2) (void); - void (*_gtk_reserved3) (void); - void (*_gtk_reserved4) (void); -}; - -GType image_menu_item_get_type (void) G_GNUC_CONST; -GtkWidget* image_menu_item_new (void); -GtkWidget* image_menu_item_new_with_label (const gchar *label); -GtkWidget* image_menu_item_new_with_mnemonic (const gchar *label); -GtkWidget* image_menu_item_new_from_stock (const gchar *stock_id, - GtkAccelGroup *accel_group); -void image_menu_item_set_image (ImageMenuItem *image_menu_item, - GtkWidget *image); -GtkWidget* image_menu_item_get_image (ImageMenuItem *image_menu_item); -void image_menu_item_set_use_stock (ImageMenuItem *image_menu_item, - gboolean use_stock); -gboolean image_menu_item_get_use_stock (ImageMenuItem *image_menu_item); -void image_menu_item_set_accel_group (ImageMenuItem *image_menu_item, - GtkAccelGroup *accel_group); - -G_END_DECLS - -#endif /* __IMAGE_MENU_ITEM_H__ */ diff --git a/src/widgets/ink-action.cpp b/src/widgets/ink-action.cpp index 2f1bf94e4..8859306f1 100644 --- a/src/widgets/ink-action.cpp +++ b/src/widgets/ink-action.cpp @@ -1,8 +1,6 @@ #include "ink-action.h" #include "widgets/icon.h" -#include "widgets/image-menu-item.h" - #include <gtk/gtk.h> static void ink_action_finalize( GObject* obj ); @@ -155,7 +153,7 @@ static GtkWidget* ink_action_create_menu_item( GtkAction* action ) if ( act->private_data->iconId ) { gchar* label = 0; g_object_get( G_OBJECT(act), "label", &label, NULL ); - item = image_menu_item_new_with_mnemonic( label ); + item = gtk_image_menu_item_new_with_mnemonic( label ); GtkWidget* child = sp_icon_new( Inkscape::ICON_SIZE_MENU, act->private_data->iconId ); // TODO this work-around is until SPIcon will live properly inside of a popup menu @@ -170,7 +168,7 @@ static GtkWidget* ink_action_create_menu_item( GtkAction* action ) } } gtk_widget_show_all( child ); - image_menu_item_set_image( IMAGE_MENU_ITEM(item), child ); + gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(item), child ); g_free( label ); label = 0; diff --git a/src/widgets/lpe-toolbar.cpp b/src/widgets/lpe-toolbar.cpp index 5df5fde70..c714aba10 100644 --- a/src/widgets/lpe-toolbar.cpp +++ b/src/widgets/lpe-toolbar.cpp @@ -302,7 +302,7 @@ void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GO gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(), - 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(), + 1, _(Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str()), 2, lpesubtools[i].icon_name, -1 ); } diff --git a/src/widgets/measure-toolbar.cpp b/src/widgets/measure-toolbar.cpp index a8c974bbc..066c3fbfa 100644 --- a/src/widgets/measure-toolbar.cpp +++ b/src/widgets/measure-toolbar.cpp @@ -326,7 +326,7 @@ void sp_measure_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, G { eact = create_adjustment_action( "MeasureOffsetAction", _("Offset"), _("Offset:"), - _("The offset size"), + _("Mark dimension offset"), "/tools/measure/offset", 5.0, GTK_WIDGET(desktop->canvas), holder, FALSE, NULL, 0.0, 90000.0, 1.0, 4.0, diff --git a/src/widgets/mesh-toolbar.cpp b/src/widgets/mesh-toolbar.cpp index 898104ad3..7a37376db 100644 --- a/src/widgets/mesh-toolbar.cpp +++ b/src/widgets/mesh-toolbar.cpp @@ -18,6 +18,8 @@ #include <config.h> #endif +#include <gtkmm.h> + // REVIEW THESE AT END OF REWRITE #include "ui/widget/color-preview.h" #include "toolbox.h" @@ -66,76 +68,79 @@ static bool blocked = false; //## Mesh ## //######################## -/* - * Get the current selection and dragger status from the desktop - */ -void ms_read_selection( Inkscape::Selection *selection, - SPMeshGradient *&ms_selected, - bool &ms_selected_multi, - SPMeshType &ms_type, - bool &ms_type_multi ) + +// Get a list of selected meshes taking into account fill/stroke toggles +std::vector<SPMeshGradient *> ms_get_dt_selected_gradients(Inkscape::Selection *selection) { + std::vector<SPMeshGradient *> ms_selected; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool edit_fill = prefs->getBool("/tools/mesh/edit_fill", true); + bool edit_stroke = prefs->getBool("/tools/mesh/edit_stroke", true); - // Read desktop selection - bool first = true; - ms_type = SP_MESH_TYPE_COONS; - auto itemlist= selection->items(); for(auto i=itemlist.begin();i!=itemlist.end();++i){ - SPItem *item = *i; + SPItem *item = *i;// get the items gradient, not the getVector() version SPStyle *style = item->style; - if (style && (style->fill.isPaintserver())) { - SPPaintServer *server = item->style->getFillPaintServer(); - if ( SP_IS_MESHGRADIENT(server) ) { - - SPMeshGradient *gradient = SP_MESHGRADIENT(server); // ->getVector(); - SPMeshType type = gradient->type; + if (style) { - if (gradient != ms_selected) { - if (ms_selected) { - ms_selected_multi = true; - } else { - ms_selected = gradient; - } + + if (edit_fill && style->fill.isPaintserver()) { + SPPaintServer *server = item->style->getFillPaintServer(); + SPMeshGradient *mesh = dynamic_cast<SPMeshGradient *>(server); + if (mesh) { + ms_selected.push_back(mesh); } - if( type != ms_type ) { - if (ms_type != SP_MESH_TYPE_COONS && !first) { - ms_type_multi = true; - } else { - ms_type = type; - } + } + + if (edit_stroke && style->stroke.isPaintserver()) { + SPPaintServer *server = item->style->getStrokePaintServer(); + SPMeshGradient *mesh = dynamic_cast<SPMeshGradient *>(server); + if (mesh) { + ms_selected.push_back(mesh); } - first = false; } } - if (style && (style->stroke.isPaintserver())) { - SPPaintServer *server = item->style->getStrokePaintServer(); - if ( SP_IS_MESHGRADIENT(server) ) { + } + return ms_selected; +} - SPMeshGradient *gradient = SP_MESHGRADIENT(server); // ->getVector(); - SPMeshType type = gradient->type; - if (gradient != ms_selected) { - if (ms_selected) { - ms_selected_multi = true; - } else { - ms_selected = gradient; - } - } - if( type != ms_type ) { - if (ms_type != SP_MESH_TYPE_COONS && !first) { - ms_type_multi = true; - } else { - ms_type = type; - } - } - first = false; +/* + * Get the current selection status from the desktop + */ +void ms_read_selection( Inkscape::Selection *selection, + SPMeshGradient *&ms_selected, + bool &ms_selected_multi, + SPMeshType &ms_type, + bool &ms_type_multi ) +{ + ms_selected = NULL; + ms_selected_multi = false; + ms_type = SP_MESH_TYPE_COONS; + ms_type_multi = false; + + bool first = true; + + // Read desktop selection, taking into account fill/stroke toggles + std::vector<SPMeshGradient *> meshes = ms_get_dt_selected_gradients( selection ); + for (auto i = meshes.begin(); i != meshes.end(); ++i) { + if (first) { + ms_selected = (*i); + ms_type = (*i)->type; + first = false; + } else { + if (ms_selected != (*i)) { + ms_selected_multi = true; + } + if (ms_type != (*i)->type) { + ms_type_multi = true; } } } - } +} /* * Core function, setup all the widgets whenever something changes on the desktop @@ -172,7 +177,7 @@ static void ms_tb_selection_changed(Inkscape::Selection * /*selection*/, gpointe // std::cout << " type: " << ms_type << std::endl; EgeSelectOneAction* type = (EgeSelectOneAction *) g_object_get_data(G_OBJECT(widget), "mesh_select_type_action"); - gtk_action_set_sensitive( GTK_ACTION(type), (ms_selected && !ms_selected_multi) ); + gtk_action_set_sensitive( GTK_ACTION(type), (ms_selected && !ms_type_multi) ); if (ms_selected) { blocked = TRUE; ege_select_one_action_set_active( type, ms_type ); @@ -203,34 +208,6 @@ static void ms_defs_modified(SPObject * /*defs*/, guint /*flags*/, GObject *widg ms_tb_selection_changed(NULL, widget); } -void ms_get_dt_selected_gradient(Inkscape::Selection *selection, SPMeshGradient *&ms_selected) -{ - SPMeshGradient *gradient = 0; - - auto itemlist= selection->items(); - for(auto i=itemlist.begin();i!=itemlist.end();++i){ - SPItem *item = *i;// get the items gradient, not the getVector() version - SPStyle *style = item->style; - SPPaintServer *server = 0; - - if (style && (style->fill.isPaintserver())) { - server = item->style->getFillPaintServer(); - } - if (style && (style->stroke.isPaintserver())) { - server = item->style->getStrokePaintServer(); - } - - if ( SP_IS_MESHGRADIENT(server) ) { - gradient = SP_MESHGRADIENT(server); - } - } - - if (gradient) { - ms_selected = gradient; - } -} - - /* * Callback functions for user actions */ @@ -295,18 +272,17 @@ static void ms_type_changed(EgeSelectOneAction *act, GtkWidget *widget) SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(G_OBJECT(widget), "desktop")); Inkscape::Selection *selection = desktop->getSelection(); - SPMeshGradient *gradient = 0; - ms_get_dt_selected_gradient(selection, gradient); + std::vector<SPMeshGradient *> meshes = ms_get_dt_selected_gradients(selection); - if (gradient) { - SPMeshType type = (SPMeshType) ege_select_one_action_get_active(act); + SPMeshType type = (SPMeshType) ege_select_one_action_get_active(act); + for (auto i = meshes.begin(); i != meshes.end(); ++i) { // std::cout << " type: " << type << std::endl; - gradient->type = type; - gradient->type_set = true; - gradient->updateRepr(); - - DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH, - _("Set mesh type")); + (*i)->type = type; + (*i)->type_set = true; + (*i)->updateRepr(); + } + if (!meshes.empty() ) { + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH,_("Set mesh type")); } } @@ -356,6 +332,41 @@ static void ms_fit_mesh(void) } } +static void ms_toggle_handles(void) +{ + MeshTool *mt = get_mesh_tool(); + if (mt) { + GrDrag *drag = mt->_grdrag; + drag->refreshDraggers(); + } +} + +static void ms_toggle_fill_stroke(InkToggleAction * /*act*/, gpointer data) +{ + MeshTool *mt = get_mesh_tool(); + if (mt) { + GrDrag *drag = mt->_grdrag; + drag->updateDraggers(); + drag->updateLines(); + drag->updateLevels(); + ms_tb_selection_changed(NULL, data); // Need to update Type widget + } +} + +static void ms_warning_popup(void) +{ + char *msg = _("Mesh gradients are part of SVG 2:\n" + "* Syntax may change.\n" + "* Web browser implementation is not guaranteed.\n" + "\n" + "For web: convert to bitmap (Edit->Make bitmap copy).\n" + "For print: export to PDF."); + Gtk::MessageDialog dialog(msg, false, Gtk::MESSAGE_WARNING, + Gtk::BUTTONS_OK, true); + dialog.run(); + +} + static void mesh_toolbox_watch_ec(SPDesktop* dt, Inkscape::UI::Tools::ToolBase* ec, GObject* holder); /** @@ -472,6 +483,7 @@ void sp_mesh_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GObj gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/mesh/edit_fill"); g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher); + g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(ms_toggle_fill_stroke), holder); } /* Edit stroke mesh */ @@ -484,18 +496,20 @@ void sp_mesh_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GObj gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/mesh/edit_stroke"); g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher); + g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(ms_toggle_fill_stroke), holder); } /* Show/hide side and tensor handles */ { InkToggleAction* act = ink_toggle_action_new( "MeshShowHandlesAction", _("Show Handles"), - _("Show side and tensor handles"), + _("Show handles"), INKSCAPE_ICON("show-node-handles"), secondarySize ); gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/mesh/show_handles"); g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher); + g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(ms_toggle_handles), 0); } g_object_set_data(holder, "desktop", desktop); @@ -504,9 +518,14 @@ void sp_mesh_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GObj /* Warning */ { - GtkAction* act = gtk_action_new( "MeshWarningAction", - _("WARNING: Mesh SVG Syntax Subject to Change"), NULL, NULL ); + InkAction* act = ink_action_new( "MeshWarningAction", + _("WARNING: Mesh SVG Syntax Subject to Change"), + _("WARNING: Mesh SVG Syntax Subject to Change"), + INKSCAPE_ICON("dialog-warning"), + secondarySize ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); + g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(ms_warning_popup), holder ); + gtk_action_set_sensitive( GTK_ACTION(act), TRUE ); } /* Type */ @@ -520,6 +539,7 @@ void sp_mesh_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GObj gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, 0, _("Bicubic"), 1, SP_MESH_TYPE_BICUBIC, -1 ); + // TRANSLATORS: Type of Smoothing. See https://en.wikipedia.org/wiki/Coons_patch EgeSelectOneAction* act = ege_select_one_action_new( "MeshSmoothAction", _("Coons"), _("Coons: no smoothing. Bicubic: smoothing across patch boundaries."), NULL, GTK_TREE_MODEL(model) ); diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp index 3e9f0687d..855371ddd 100644 --- a/src/widgets/paint-selector.cpp +++ b/src/widgets/paint-selector.cpp @@ -285,6 +285,11 @@ static void sp_paint_selector_dispose(GObject *object) // clean up our long-living pattern menu g_object_set_data(G_OBJECT(psel),"patternmenu",NULL); +#ifdef WITH_MESH + // clean up our long-living mesh menu + g_object_set_data(G_OBJECT(psel),"meshmenu",NULL); +#endif + if (psel->selected_color) { delete psel->selected_color; psel->selected_color = NULL; @@ -492,7 +497,7 @@ void SPPaintSelector::setGradientRadial(SPGradient *vector) } #ifdef WITH_MESH -void SPPaintSelector::setGradientMesh(SPGradient *array) +void SPPaintSelector::setGradientMesh(SPMeshGradient *array) { #ifdef SP_PS_VERBOSE g_print("PaintSelector set GRADIENT MESH\n"); @@ -524,24 +529,6 @@ void SPPaintSelector::getGradientProperties( SPGradientUnits &units, SPGradientS spread = gsel->getSpread(); } -#ifdef WITH_MESH -void SPPaintSelector::setMeshProperties( SPGradientUnits units ) -{ - g_return_if_fail(mode == MODE_GRADIENT_MESH); - - // SPGradientSelector *gsel = getGradientFromData(this); - // gsel->setUnits(units); -} - -void SPPaintSelector::getMeshProperties( SPGradientUnits &units) const -{ - g_return_if_fail(mode == MODE_GRADIENT_MESH); - - // SPGradientSelector *gsel = getGradientFromData(this); - // units = gsel->getUnits(); -} -#endif - /** * \post (alpha == NULL) || (*alpha in [0.0, 1.0]). @@ -788,7 +775,214 @@ static void sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSe #endif } +// ************************* MESH ************************ #ifdef WITH_MESH +static void sp_psel_mesh_destroy(GtkWidget *widget, SPPaintSelector * /*psel*/) +{ + // drop our reference to the mesh menu widget + g_object_unref( G_OBJECT(widget) ); +} + +static void sp_psel_mesh_change(GtkWidget * /*widget*/, SPPaintSelector *psel) +{ + g_signal_emit(G_OBJECT(psel), psel_signals[CHANGED], 0); +} + + +/** + * Returns a list of meshes in the defs of the given source document as a GSList object + * Returns NULL if there are no meshes in the document. + */ +static GSList * +ink_mesh_list_get (SPDocument *source) +{ + if (source == NULL) + return NULL; + + GSList *pl = NULL; + std::vector<SPObject *> meshes = source->getResourceList("gradient"); + for (std::vector<SPObject *>::const_iterator it = meshes.begin(); it != meshes.end(); ++it) { + if (SP_IS_MESHGRADIENT(*it) && + SP_GRADIENT(*it) == SP_GRADIENT(*it)->getArray()) { // only if this is a root mesh + pl = g_slist_prepend(pl, *it); + } + } + + pl = g_slist_reverse(pl); + return pl; +} + +/** + * Adds menu items for mesh list. + */ +static void +sp_mesh_menu_build (GtkWidget *combo, GSList *mesh_list, SPDocument */*source*/) +{ + GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); + GtkTreeIter iter; + + for (; mesh_list != NULL; mesh_list = mesh_list->next) { + + Inkscape::XML::Node *repr = reinterpret_cast<SPItem *>(mesh_list->data)->getRepr(); + + gchar const *meshid = repr->attribute("id"); + gchar const *label = meshid; + + // Only relevant if we supply a set of canned meshes. + gboolean stockid = false; + if (repr->attribute("inkscape:stockid")) { + label = _(repr->attribute("inkscape:stockid")); + stockid = true; + } + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + COMBO_COL_LABEL, label, COMBO_COL_STOCK, stockid, COMBO_COL_MESH, meshid, COMBO_COL_SEP, FALSE, -1); + + } +} + +/** + * Pick up all meshes from source, except those that are in + * current_doc (if non-NULL), and add items to the mesh menu. + */ +static void sp_mesh_list_from_doc(GtkWidget *combo, SPDocument * /*current_doc*/, SPDocument *source, SPDocument * /*mesh_doc*/) +{ + GSList *pl = ink_mesh_list_get(source); + GSList *clean_pl = NULL; + + for (; pl != NULL; pl = pl->next) { + if (!SP_IS_MESHGRADIENT(pl->data)) { + continue; + } + // Add to the list of meshes we really do wish to show + clean_pl = g_slist_prepend (clean_pl, pl->data); + } + + sp_mesh_menu_build (combo, clean_pl, source); + + g_slist_free (pl); + g_slist_free (clean_pl); +} + + +static void +ink_mesh_menu_populate_menu(GtkWidget *combo, SPDocument *doc) +{ + static SPDocument *meshes_doc = NULL; + + // If we ever add a list of canned mesh gradients, uncomment following: + + // find and load meshes.svg + // if (meshes_doc == NULL) { + // char *meshes_source = g_build_filename(INKSCAPE_MESHESDIR, "meshes.svg", NULL); + // if (Inkscape::IO::file_test(meshes_source, G_FILE_TEST_IS_REGULAR)) { + // meshes_doc = SPDocument::createNewDoc(meshes_source, FALSE); + // } + // g_free(meshes_source); + // } + + // suck in from current doc + sp_mesh_list_from_doc ( combo, NULL, doc, meshes_doc ); + + // add separator + // { + // GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); + // GtkTreeIter iter; + // gtk_list_store_append (store, &iter); + // gtk_list_store_set(store, &iter, + // COMBO_COL_LABEL, "", COMBO_COL_STOCK, false, COMBO_COL_MESH, "", COMBO_COL_SEP, true, -1); + // } + + // suck in from meshes.svg + // if (meshes_doc) { + // doc->ensureUpToDate(); + // sp_mesh_list_from_doc ( combo, doc, meshes_doc, NULL ); + // } + +} + + +static GtkWidget* +ink_mesh_menu(GtkWidget *combo) +{ + SPDocument *doc = SP_ACTIVE_DOCUMENT; + + GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); + GtkTreeIter iter; + + if (!doc) { + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COMBO_COL_LABEL, _("No document selected"), COMBO_COL_STOCK, false, COMBO_COL_MESH, "", COMBO_COL_SEP, false, -1); + gtk_widget_set_sensitive(combo, FALSE); + + } else { + + ink_mesh_menu_populate_menu(combo, doc); + gtk_widget_set_sensitive(combo, TRUE); + + } + + // Select the first item that is not a separator + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) { + gboolean sep = false; + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, COMBO_COL_SEP, &sep, -1); + if (sep) { + gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); + } + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); + } + + return combo; +} + + +/*update mesh list*/ +void SPPaintSelector::updateMeshList( SPMeshGradient *mesh ) +{ + if (update) { + return; + } + + GtkWidget *combo = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "meshmenu")); + g_assert( combo != NULL ); + + /* Clear existing menu if any */ + GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + gtk_list_store_clear(GTK_LIST_STORE(store)); + + ink_mesh_menu(combo); + + /* Set history */ + + if (mesh && !g_object_get_data(G_OBJECT(combo), "update")) { + + g_object_set_data(G_OBJECT(combo), "update", GINT_TO_POINTER(TRUE)); + gchar const *meshname = mesh->getRepr()->attribute("id"); + + // Find this mesh and set it active in the combo_box + GtkTreeIter iter ; + gchar *meshid = NULL; + bool valid = gtk_tree_model_get_iter_first (store, &iter); + if (!valid) { + return; + } + gtk_tree_model_get (store, &iter, COMBO_COL_MESH, &meshid, -1); + while (valid && strcmp(meshid, meshname) != 0) { + valid = gtk_tree_model_iter_next (store, &iter); + gtk_tree_model_get (store, &iter, COMBO_COL_MESH, &meshid, -1); + } + + if (valid) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); + } + + g_object_set_data(G_OBJECT(combo), "update", GINT_TO_POINTER(FALSE)); + } +} + static void sp_paint_selector_set_mode_mesh(SPPaintSelector *psel, SPPaintSelector::Mode mode) { if (mode == SPPaintSelector::MODE_GRADIENT_MESH) { @@ -799,23 +993,50 @@ static void sp_paint_selector_set_mode_mesh(SPPaintSelector *psel, SPPaintSelect GtkWidget *tbl = NULL; if (psel->mode == SPPaintSelector::MODE_GRADIENT_MESH) { - /* Already have mesh selector */ + /* Already have mesh menu */ tbl = GTK_WIDGET(g_object_get_data(G_OBJECT(psel->selector), "mesh-selector")); } else { sp_paint_selector_clear_frame(psel); - /* We could create a new gradient selector once adapted for meshes. - But for the moment we just create an empty widget. */ /* Create vbox */ tbl = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); gtk_box_set_homogeneous(GTK_BOX(tbl), FALSE); gtk_widget_show(tbl); { + auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); + gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); + + /** + * Create a combo_box and store with 4 columns, + * The label, a pointer to the mesh, is stockid or not, is a separator or not. + */ + GtkListStore *store = gtk_list_store_new (COMBO_N_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN); + GtkWidget *combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combo), SPPaintSelector::isSeparator, NULL, NULL); + + GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); + gtk_cell_renderer_set_padding (renderer, 2, 0); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", COMBO_COL_LABEL, NULL); + + ink_mesh_menu(combo); + g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(sp_psel_mesh_change), psel); + g_signal_connect(G_OBJECT(combo), "destroy", G_CALLBACK(sp_psel_mesh_destroy), psel); + g_object_set_data(G_OBJECT(psel), "meshmenu", combo); + g_object_ref( G_OBJECT(combo)); + + gtk_container_add(GTK_CONTAINER(hb), combo); + gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); + + g_object_unref( G_OBJECT(store)); + } + + { auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); auto l = gtk_label_new(NULL); - gtk_label_set_markup(GTK_LABEL(l), _("Use the <b>Mesh tool</b> to create new and edit existing meshes. Mesh selection and copying not yet implemented.")); + gtk_label_set_markup(GTK_LABEL(l), _("Use the <b>Mesh tool</b> to modify the mesh.")); gtk_label_set_line_wrap(GTK_LABEL(l), true); gtk_widget_set_size_request(l, 180, -1); gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); @@ -828,11 +1049,66 @@ static void sp_paint_selector_set_mode_mesh(SPPaintSelector *psel, SPPaintSelect psel->selector = tbl; g_object_set_data(G_OBJECT(psel->selector), "mesh-selector", tbl); - gtk_label_set_markup(GTK_LABEL(psel->label), _("<b>Mesh gradient</b>")); + gtk_label_set_markup(GTK_LABEL(psel->label), _("<b>Mesh fill</b>")); } +#ifdef SP_PS_VERBOSE + g_print("Mesh req\n"); +#endif +} + +SPMeshGradient *SPPaintSelector::getMeshGradient() +{ + g_return_val_if_fail((mode == MODE_GRADIENT_MESH) , NULL); + + GtkWidget *combo = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "meshmenu")); + /* no mesh menu if we were just selected */ + if ( combo == NULL ) { + return NULL; + } + GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + + /* Get the selected mesh */ + GtkTreeIter iter; + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(combo), &iter) || + !gtk_list_store_iter_is_valid(GTK_LIST_STORE(store), &iter)) { + return NULL; + } + + gchar *meshid = NULL; + gboolean stockid = FALSE; + gchar *label = NULL; + gtk_tree_model_get (store, &iter, COMBO_COL_LABEL, &label, COMBO_COL_STOCK, &stockid, COMBO_COL_MESH, &meshid, -1); + // std::cout << " .. meshid: " << (meshid?meshid:"null") << " label: " << (label?label:"null") << std::endl; + if (meshid == NULL) { + return NULL; + } + + SPMeshGradient *mesh = 0; + if (strcmp(meshid, "none")){ + + gchar *mesh_name; + if (stockid) { + mesh_name = g_strconcat("urn:inkscape:mesh:", meshid, NULL); + } else { + mesh_name = g_strdup(meshid); + } + + SPObject *mesh_obj = get_stock_item(mesh_name); + if (mesh_obj && SP_IS_MESHGRADIENT(mesh_obj)) { + mesh = SP_MESHGRADIENT(mesh_obj); + } + g_free(mesh_name); + + } else { + std::cerr << "SPPaintSelector::getMeshGradient: Unexpected meshid value." << std::endl; + } + + return mesh; } + #endif +// ************************ End Mesh ************************ static void sp_paint_selector_set_style_buttons(SPPaintSelector *psel, GtkWidget *active) diff --git a/src/widgets/paint-selector.h b/src/widgets/paint-selector.h index 815a6da0b..3302632b8 100644 --- a/src/widgets/paint-selector.h +++ b/src/widgets/paint-selector.h @@ -26,6 +26,9 @@ #include "ui/selected-color.h" class SPGradient; +#ifdef WITH_MESH +class SPMeshGradient; +#endif class SPDesktop; class SPPattern; class SPStyle; @@ -98,18 +101,21 @@ struct SPPaintSelector { void setGradientLinear( SPGradient *vector ); void setGradientRadial( SPGradient *vector ); #ifdef WITH_MESH - void setGradientMesh(SPGradient *array); + void setGradientMesh(SPMeshGradient *array); #endif void setSwatch( SPGradient *vector ); void setGradientProperties( SPGradientUnits units, SPGradientSpread spread ); void getGradientProperties( SPGradientUnits &units, SPGradientSpread &spread ) const; - void setMeshProperties( SPGradientUnits units ); - void getMeshProperties( SPGradientUnits &units ) const; - void pushAttrsToGradient( SPGradient *gr ) const; SPGradient *getGradientVector(); + +#ifdef WITH_MESH + SPMeshGradient * getMeshGradient(); + void updateMeshList( SPMeshGradient *pat ); +#endif + SPPattern * getPattern(); void updatePatternList( SPPattern *pat ); @@ -124,8 +130,14 @@ struct SPPaintSelector { void onSelectedColorChanged(); }; -enum {COMBO_COL_LABEL=0, COMBO_COL_STOCK=1, COMBO_COL_PATTERN=2, COMBO_COL_SEP=3, COMBO_N_COLS=4}; - +enum { + COMBO_COL_LABEL = 0, + COMBO_COL_STOCK = 1, + COMBO_COL_PATTERN = 2, + COMBO_COL_MESH = COMBO_COL_PATTERN, + COMBO_COL_SEP = 3, + COMBO_N_COLS = 4 +}; /// The SPPaintSelector vtable struct SPPaintSelectorClass { diff --git a/src/widgets/paintbucket-toolbar.cpp b/src/widgets/paintbucket-toolbar.cpp index 3d1565924..e2212b64f 100644 --- a/src/widgets/paintbucket-toolbar.cpp +++ b/src/widgets/paintbucket-toolbar.cpp @@ -192,7 +192,7 @@ void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions iterator != gap_list.end(); ++iterator ) { GtkTreeIter iter; gtk_list_store_append( model, &iter ); - gtk_list_store_set( model, &iter, 0, _((*iterator).c_str()), 1, count, -1 ); + gtk_list_store_set( model, &iter, 0, g_dpgettext2(NULL, "Flood autogap", (*iterator).c_str()), 1, count, -1 ); count++; } EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) ); diff --git a/src/widgets/select-toolbar.cpp b/src/widgets/select-toolbar.cpp index c5ff713bd..2ac7447dd 100644 --- a/src/widgets/select-toolbar.cpp +++ b/src/widgets/select-toolbar.cpp @@ -550,14 +550,14 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb } else { g_warning("Unexpected holder type"); } - // "Transform with object" buttons { + InkToggleAction* itact = ink_toggle_action_new( "transform_stroke", _("Scale stroke width"), _("When scaling objects, scale the stroke width by the same proportion"), - INKSCAPE_ICON("transform-affect-stroke"), - Inkscape::ICON_SIZE_DECORATION ); + "transform-affect-stroke", + secondarySize ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(itact), prefs->getBool("/options/transform/stroke", true) ); g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(toggle_stroke), desktop) ; gtk_action_group_add_action( mainActions, GTK_ACTION(itact) ); @@ -567,8 +567,8 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb InkToggleAction* itact = ink_toggle_action_new( "transform_corners", _("Scale rounded corners"), _("When scaling rectangles, scale the radii of rounded corners"), - INKSCAPE_ICON("transform-affect-rounded-corners"), - Inkscape::ICON_SIZE_DECORATION ); + "transform-affect-rounded-corners", + secondarySize ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(itact), prefs->getBool("/options/transform/rectcorners", true) ); g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(toggle_corners), desktop) ; gtk_action_group_add_action( mainActions, GTK_ACTION(itact) ); @@ -578,8 +578,8 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb InkToggleAction* itact = ink_toggle_action_new( "transform_gradient", _("Move gradients"), _("Move gradients (in fill or stroke) along with the objects"), - INKSCAPE_ICON("transform-affect-gradient"), - Inkscape::ICON_SIZE_DECORATION ); + "transform-affect-gradient", + secondarySize ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(itact), prefs->getBool("/options/transform/gradient", true) ); g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(toggle_gradient), desktop) ; gtk_action_group_add_action( mainActions, GTK_ACTION(itact) ); @@ -589,8 +589,8 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb InkToggleAction* itact = ink_toggle_action_new( "transform_pattern", _("Move patterns"), _("Move patterns (in fill or stroke) along with the objects"), - INKSCAPE_ICON("transform-affect-pattern"), - Inkscape::ICON_SIZE_DECORATION ); + "transform-affect-pattern", + secondarySize ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(itact), prefs->getBool("/options/transform/pattern", true) ); g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(toggle_pattern), desktop) ; gtk_action_group_add_action( mainActions, GTK_ACTION(itact) ); diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index 81a8e8115..de3e98297 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -940,7 +940,7 @@ StrokeStyle::updateLine() SPStyle * const style = object->style; /* Markers */ - updateAllMarkers(objects); // FIXME: make this desktop query too + updateAllMarkers(objects, true); // FIXME: make this desktop query too /* Dash */ setDashSelectorFromStyle(dashSelector, style); // FIXME: make this desktop query too @@ -1192,7 +1192,7 @@ StrokeStyle::setPaintOrderButtons(Gtk::ToggleButton *active) * that marker. */ void -StrokeStyle::updateAllMarkers(std::vector<SPItem*> const &objects) +StrokeStyle::updateAllMarkers(std::vector<SPItem*> const &objects, bool skip_undo) { struct { MarkerComboBox *key; int loc; } const keyloc[] = { { startMarkerCombo, SP_MARKER_LOC_START }, @@ -1246,9 +1246,11 @@ StrokeStyle::updateAllMarkers(std::vector<SPItem*> const &objects) if (update) { setMarkerColor(marker, combo->get_loc(), SP_ITEM(object)); - SPDocument *document = desktop->getDocument(); - DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE, + if (!skip_undo) { + SPDocument *document = desktop->getDocument(); + DocumentUndo::maybeDone(document, "UaM", SP_VERB_DIALOG_FILL_STROKE, _("Set marker color")); + } } } else { diff --git a/src/widgets/stroke-style.h b/src/widgets/stroke-style.h index 2d7e187a1..cdd825c7d 100644 --- a/src/widgets/stroke-style.h +++ b/src/widgets/stroke-style.h @@ -149,7 +149,7 @@ private: }; void updateLine(); - void updateAllMarkers(std::vector<SPItem*> const &objects); + void updateAllMarkers(std::vector<SPItem*> const &objects, bool skip_undo = false); void updateMarkerHist(SPMarkerLoc const which); void setDashSelectorFromStyle(SPDashSelector *dsel, SPStyle *style); void setJoinType (unsigned const jointype); diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index 788ac4eb3..ba79517d2 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -117,6 +117,8 @@ static void sp_print_fontstyle( SPStyle *query ) { } #endif +static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl, bool subselection = false); + // Font family static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl ) { @@ -225,8 +227,35 @@ static void sp_text_fontsize_value_changed( Ink_ComboBoxEntry_Action *act, GObje sp_repr_css_set_property (css, "font-size", osfs.str().c_str()); // Apply font size to selected objects. + // Calling sp_desktop_set_style will result in a call to TextTool::_styleSet() which + // will set the style on selected text inside the <text> element. If we want to set + // the style on the outer <text> objects we need to bypass this call. + bool outer = prefs->getInt("/tools/text/outer_style", false); SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, true); + if (outer) { + Inkscape::Selection *selection = desktop->getSelection(); + auto itemlist= selection->items(); + for(auto i=itemlist.begin();i!=itemlist.end(); ++i){ + if (dynamic_cast<SPText *>(*i) || dynamic_cast<SPFlowtext *>(*i)) { + SPItem *item = *i; + + // Scale by inverse of accumulated parent transform + SPCSSAttr *css_set = sp_repr_css_attr_new(); + sp_repr_css_merge(css_set, css); + Geom::Affine const local(item->i2doc_affine()); + double const ex(local.descrim()); + if ( (ex != 0.0) && (ex != 1.0) ) { + sp_css_attr_scale(css_set, 1/ex); + } + + item->changeCSS(css_set,"style"); + + sp_repr_css_attr_unref(css_set); + } + } + } else { + sp_desktop_set_style (desktop, css, true, true); + } // If no selected objects, set default. SPStyle query(SP_ACTIVE_DOCUMENT); @@ -281,6 +310,40 @@ static void sp_text_fontstyle_value_changed( Ink_ComboBoxEntry_Action *act, GObj g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } +// Changes selection to only text outer elements. +static void sp_text_outer_style_changed( InkToggleAction*act, GObject *tbl ) +{ + bool outer = gtk_toggle_action_get_active( GTK_TOGGLE_ACTION(act) ); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/tools/text/outer_style", outer); + + // Update widgets to reflect new state of Text Outer Style button. + sp_text_toolbox_selection_changed( NULL, tbl ); +} + +// Unset line height on selection's inner text objects (tspan, etc.). +static void sp_text_lineheight_unset_changed( InkToggleAction*act, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_unset_property(css, "line-height"); + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + sp_desktop_set_style (desktop, css); + + sp_repr_css_attr_unref(css); + + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Unset line height.")); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + // Handles both Superscripts and Subscripts static void sp_text_script_changed( InkToggleAction* act, GObject *tbl ) { @@ -508,7 +571,7 @@ static void sp_text_align_mode_changed( EgeSelectOneAction *act, GObject *tbl ) static bool is_relative( Unit const *unit ) { return (unit->abbr == "" || unit->abbr == "em" || unit->abbr == "ex" || unit->abbr == "%"); } - + static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl ) { // quit if run by the _changed callbacks @@ -549,9 +612,34 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl ) sp_repr_css_set_property (css, "line-height", osfs.str().c_str()); - // Apply line-height to selected objects. + // Apply line-height to selected objects. See comment in font size function. + bool outer = prefs->getInt("/tools/text/outer_style", false); SPDesktop *desktop = SP_ACTIVE_DESKTOP; - sp_desktop_set_style (desktop, css, true, false); + if (outer) { + Inkscape::Selection *selection = desktop->getSelection(); + auto itemlist= selection->items(); + for(auto i=itemlist.begin();i!=itemlist.end(); ++i){ + if (dynamic_cast<SPText *>(*i) || dynamic_cast<SPFlowtext *>(*i)) { + SPItem *item = *i; + + // Scale by inverse of accumulated parent transform + SPCSSAttr *css_set = sp_repr_css_attr_new(); + sp_repr_css_merge(css_set, css); + Geom::Affine const local(item->i2doc_affine()); + double const ex(local.descrim()); + if ( (ex != 0.0) && (ex != 1.0) ) { + sp_css_attr_scale(css_set, 1/ex); + } + + item->changeCSS(css_set,"style"); + + sp_repr_css_attr_unref(css_set); + } + } + } else { + sp_desktop_set_style (desktop, css, true, true); + } + // Only need to save for undo if a text item has been changed. @@ -652,7 +740,7 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl ) double font_size = 0; int count = 0; for(auto i=itemlist.begin();i!=itemlist.end(); ++i){ - if (SP_IS_TEXT (*i)) { + if (SP_IS_TEXT (*i) || SP_IS_FLOWTEXT(*i)) { double doc_scale = Geom::Affine((*i)->i2dt_affine()).descrim(); font_size += (*i)->style->font_size.computed * doc_scale; ++count; @@ -681,7 +769,7 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl ) double font_size = 0; int count = 0; for(auto i=itemlist.begin();i!=itemlist.end(); ++i){ - if (SP_IS_TEXT (*i)) { + if (SP_IS_TEXT (*i) || SP_IS_FLOWTEXT (*i)) { double doc_scale = Geom::Affine((*i)->i2dt_affine()).descrim(); font_size += (*i)->style->font_size.computed * doc_scale; ++count; @@ -1073,7 +1161,7 @@ static void sp_text_set_sizes(GtkListStore* model_size, int unit) * It is called whenever a text selection is changed, including stepping cursor * through text, or setting focus to text. */ -static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl, bool subselection = false) // don't bother to update font list if subsel changed +static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl, bool subselection) // don't bother to update font list if subsel changed { #ifdef DEBUG_TEXT static int count = 0; @@ -1082,12 +1170,11 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; std::cout << "sp_text_toolbox_selection_changed: start " << count << std::endl; - std::cout << " Selected items:" << std::endl; - for (GSList const *items = SP_ACTIVE_DESKTOP->getSelection()->itemList(); - items != NULL; - items = items->next) - { - const gchar* id = reinterpret_cast<SPItem *>(items->data)->getId(); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Inkscape::Selection *selection = desktop->getSelection(); + auto itemlist0= selection->items(); + for(auto i=itemlist0.begin();i!=itemlist0.end(); ++i) { + const gchar* id = (*i)->getId(); std::cout << " " << id << std::endl; } Glib::ustring selected_text = sp_text_get_selected_text((SP_ACTIVE_DESKTOP)->event_context); @@ -1129,8 +1216,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ gboolean isFlow = false; auto itemlist= SP_ACTIVE_DESKTOP->getSelection()->items(); for(auto i=itemlist.begin();i!=itemlist.end(); ++i){ - // const gchar* id = reinterpret_cast<SPItem *>(items->data)->getId(); - // std::cout << " " << id << std::endl; + // std::cout << " " << ((*i)->getId()?(*i)->getId():"null") << std::endl; if( SP_IS_FLOWTEXT(*i)) { isFlow = true; // std::cout << " Found flowed text" << std::endl; @@ -1148,10 +1234,26 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ SPStyle query(SP_ACTIVE_DOCUMENT); int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTFAMILY); int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTSTYLE); - int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS); int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_BASELINES); int result_wmode = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_WRITINGMODES); + // Calling sp_desktop_query_style will result in a call to TextTool::_styleQueried(). + // This returns the style of the selected text inside the <text> element... which + // is often the style of one or more <tspan>s. If we want the style of the outer + // <text> objects then we need to bypass the call to TextTool::_styleQueried(). + // The desktop selection never includes the elements inside the <text> element. + int result_numbers = 0; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + bool outer = prefs->getInt("/tools/text/outer_style", false); + if (outer) { + Inkscape::Selection *selection = desktop->getSelection(); + std::vector<SPItem *> vec(selection->items().begin(), selection->items().end()); + result_numbers = sp_desktop_query_style_from_list (vec, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + } else { + result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + } + /* * If no text in selection (querying returned nothing), read the style from * the /tools/text preferencess (default style for new texts). Return if @@ -1326,7 +1428,13 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ } // Save unit so we can do convertions between new/old units. g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(line_height_unit)); - + + // Enable and turn on only if selection includes an object with line height set. + InkToggleAction* lineHeightUnset = + INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextLineHeightUnsetAction")); + gtk_action_set_sensitive(GTK_ACTION(lineHeightUnset), query.line_height.set ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(lineHeightUnset), query.line_height.set ); + // Word spacing double wordSpacing; if (query.word_spacing.normal) wordSpacing = 0.0; @@ -1573,10 +1681,6 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje gtk_css_provider_load_from_data(css_provider, "#TextFontFamilyAction_combobox {\n" " -GtkComboBox-appears-as-list: true;\n" - "}\n" - "combobox window.popup scrolledwindow treeview separator {\n" - " -GtkWidget-wide-separators: true;\n" - " -GtkWidget-separator-height: 6;\n" "}\n", -1, NULL); @@ -1602,7 +1706,7 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje _(tooltip.c_str()), NULL, GTK_TREE_MODEL(model_size), - 4, // Width in characters + 8, // Width in characters 0, // Extra list width NULL, // Cell layout NULL, // Separator @@ -2025,6 +2129,32 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje g_object_set( G_OBJECT(eact), "iconId", "text_rotation", NULL ); } + /* Text line height unset */ + { + InkToggleAction* act = ink_toggle_action_new( "TextLineHeightUnsetAction", // Name + _("Unset line height"), // Label + _("If enabled, line height is set on part of selection. Click to unset."), + INKSCAPE_ICON("paint-unknown"), + secondarySize ); // Icon size + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_lineheight_unset_changed), holder ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/line_height_unset", false) ); + g_object_set_data( holder, "TextLineHeightUnsetAction", act ); + } + + /* Text outer style */ + { + InkToggleAction* act = ink_toggle_action_new( "TextOuterStyleAction", // Name + _("Show outer style"), // Label + _("Show style of outermost text element. The 'font-size' and 'line-height' values of the outermost text element determine the minimum line spacing in the block."), + INKSCAPE_ICON("text_outer_style"), + secondarySize ); // Icon size + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_outer_style_changed), holder ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/outer_style", false) ); + g_object_set_data( holder, "TextOuterStyleAction", act ); + } + // Is this necessary to call? Shouldn't hurt. sp_text_toolbox_selection_changed(desktop->getSelection(), holder); diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index d3715cf4e..126eac9cd 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -362,8 +362,6 @@ static gchar const * ui_descr = " <separator />" " <toolitem action='MeasureScaleAction' />" " <separator />" - " <toolitem action='MeasureOffsetAction' />" - " <separator />" " <toolitem action='measure_units_label' />" " <toolitem action='MeasureUnitsAction' />" " <toolitem action='MeasureIgnore1stAndLast' />" @@ -373,8 +371,9 @@ static gchar const * ui_descr = " <toolitem action='MeasureReverse' />" " <toolitem action='MeasureToPhantom' />" " <toolitem action='MeasureToGuides' />" - " <toolitem action='MeasureMarkDimension' />" " <toolitem action='MeasureToItem' />" + " <toolitem action='MeasureMarkDimension' />" + " <toolitem action='MeasureOffsetAction' />" " </toolbar>" " <toolbar name='StarToolbar'>" @@ -508,18 +507,19 @@ static gchar const * ui_descr = " <toolbar name='TextToolbar'>" " <toolitem action='TextFontFamilyAction' />" - " <toolitem action='TextFontSizeAction' />" " <toolitem action='TextFontStyleAction' />" -// " <toolitem action='TextBoldAction' />" -// " <toolitem action='TextItalicAction' />" + " <separator />" + " <toolitem action='TextOuterStyleAction' />" + " <toolitem action='TextFontSizeAction' />" + " <toolitem action='TextLineHeightAction' />" + " <toolitem action='TextLineHeightUnitsAction' />" + " <toolitem action='TextLineHeightUnsetAction' />" " <separator />" " <toolitem action='TextAlignAction' />" " <separator />" " <toolitem action='TextSuperscriptAction' />" " <toolitem action='TextSubscriptAction' />" " <separator />" - " <toolitem action='TextLineHeightAction' />" - " <toolitem action='TextLineHeightUnitsAction' />" " <toolitem action='TextLetterSpacingAction' />" " <toolitem action='TextWordSpacingAction' />" " <toolitem action='TextDxAction' />" @@ -574,7 +574,12 @@ static gchar const * ui_descr = " <toolitem action='MeshPickColorsAction' />" " <toolitem action='MeshFitInBoundingBoxAction' />" " <separator />" + " <toolitem action='MeshShowHandlesAction' />" + " <toolitem action='MeshEditFillAction' />" + " <toolitem action='MeshEditStrokeAction' />" + " <separator />" " <toolitem action='MeshWarningAction' />" + " <separator />" " <toolitem action='MeshSmoothAction' />" " </toolbar>" @@ -655,7 +660,7 @@ Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb } VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view) : - Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(g_dpgettext2(NULL, "ContextVerb", verb->get_name())), Glib::ustring(_(verb->get_tip()))), + Gtk::Action(Glib::ustring(verb->get_id()), verb->get_image(), Glib::ustring(g_dpgettext2(NULL, "ContextVerb", verb->get_name())), Glib::ustring(_(verb->get_tip()))), verb(verb), verb2(verb2), view(view), @@ -670,7 +675,7 @@ VerbAction::~VerbAction() Gtk::Widget* VerbAction::create_menu_item_vfunc() { // First call in to get the icon rendered if present in SVG - Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU ); + Gtk::Widget *widget = sp_icon_get_icon( get_icon_name(), Inkscape::ICON_SIZE_MENU ); delete widget; widget = 0; |
