diff options
| author | Ted Gould <ted@gould.cx> | 2010-03-26 04:34:25 +0000 |
|---|---|---|
| committer | Ted Gould <ted@gould.cx> | 2010-03-26 04:34:25 +0000 |
| commit | 9e023a3aa964a0d3fa1e31e46d33657367ba68aa (patch) | |
| tree | 33f1392a340737e4eeefca6fd031f96c29befd2b /src | |
| parent | Installing the pkgconfig file (diff) | |
| parent | Adding in shape-record.h (diff) | |
| download | inkscape-9e023a3aa964a0d3fa1e31e46d33657367ba68aa.tar.gz inkscape-9e023a3aa964a0d3fa1e31e46d33657367ba68aa.zip | |
Merge from trunk
(bzr r8254.1.53)
Diffstat (limited to 'src')
353 files changed, 20823 insertions, 17486 deletions
diff --git a/src/2geom/chebyshev.cpp b/src/2geom/chebyshev.cpp index 447c5183f..73baf7b6b 100644 --- a/src/2geom/chebyshev.cpp +++ b/src/2geom/chebyshev.cpp @@ -93,7 +93,7 @@ SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), wr.fa = fa; wr.fb = fb; wr.in = in; - printf("%f %f\n", fa, fb); + //printf("%f %f\n", fa, fb); wr.f = f; wr.pp = p; return compose(Linear(in[0], in[1]), Linear(fa, fb)) + chebyshev_approximant(f_interp, order, in, &wr) + Linear(fa, fb); diff --git a/src/2geom/hvlinesegment.h b/src/2geom/hvlinesegment.h index ac91ec80a..6c42b06aa 100644 --- a/src/2geom/hvlinesegment.h +++ b/src/2geom/hvlinesegment.h @@ -216,7 +216,7 @@ class HLineSegment : public Curve { Point ip = initialPoint() * m; Point ep = finalPoint() * m; - if (m.onlyScaleAndTranslation()) { + if (ip[Y] == ep[Y]) { return new HLineSegment(ip[X], ep[X], ip[Y]); } else { return new LineSegment(ip, ep); @@ -457,7 +457,7 @@ class VLineSegment : public Curve { Point ip = initialPoint() * m; Point ep = finalPoint() * m; - if (m.onlyScaleAndTranslation()) { + if (ip[X] == ep[X]) { return new VLineSegment(ip[X], ip[Y], ep[Y]); } else { return new LineSegment(ip, ep); diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp index 981c9f044..88c7a99b9 100644 --- a/src/2geom/path.cpp +++ b/src/2geom/path.cpp @@ -203,11 +203,10 @@ Path::nearestPointPerCurve(Point const& _point) const { //return a single nearest point for each curve in this path std::vector<double> np; - const Path& _path = *this; - for (Sequence::const_iterator it = _path.get_curves().begin() ; it != _path.get_curves().end()-1 ; ++it) + for (const_iterator it = begin() ; it != end_default(); ++it) //for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){ { - np.push_back((*it)->nearestPoint(_point)); + np.push_back(it->nearestPoint(_point)); } return np; } diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp index 071b171b3..691ddf022 100644 --- a/src/2geom/svg-path-parser.cpp +++ b/src/2geom/svg-path-parser.cpp @@ -1,4 +1,3 @@ -#line 1 "/home/njh/svn/lib2geom/src/2geom/svg-path-parser.rl" /** * \file * \brief parse SVG path specifications diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46c2586d1..a956f6ad8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,6 +61,7 @@ sp-shape.cpp sp-skeleton.cpp
sp-spiral.cpp
sp-star.cpp
+sp-stop.cpp
sp-string.cpp
sp-style-elem.cpp
sp-switch.cpp
diff --git a/src/Makefile.am b/src/Makefile.am index 9d5177519..7cd194eea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -95,7 +95,8 @@ CXXTEST_TESTSUITES = if PLATFORM_WIN32 win32_sources = winmain.cpp registrytool.cpp registrytool.h -win32ldflags = -lcomdlg32 +win32ldflags = -lcomdlg32 -lmscms +mwindows = -mwindows endif if INKJAR @@ -117,7 +118,6 @@ include extension/internal/Makefile_insert include extension/script/Makefile_insert include filters/Makefile_insert include helper/Makefile_insert -include inkjar/Makefile_insert include io/Makefile_insert #include pedro/Makefile_insert #include jabber_whiteboard/Makefile_insert @@ -131,17 +131,14 @@ include live_effects/Makefile_insert include live_effects/parameter/Makefile_insert include libvpsc/Makefile_insert include libcola/Makefile_insert -include removeoverlap/Makefile_insert -include graphlayout/Makefile_insert include svg/Makefile_insert include widgets/Makefile_insert include debug/Makefile_insert include xml/Makefile_insert -include traits/Makefile_insert -include algorithms/Makefile_insert include ui/Makefile_insert include ui/cache/Makefile_insert include ui/dialog/Makefile_insert +include ui/tool/Makefile_insert include ui/view/Makefile_insert include ui/widget/Makefile_insert include util/Makefile_insert @@ -152,7 +149,7 @@ include 2geom/Makefile_insert EXTRA_DIST += \ $(top_srcdir)/Doxyfile \ sp-skeleton.cpp sp-skeleton.h \ - algorithms/makefile.in \ + util/makefile.in \ application/makefile.in \ bind/makefile.in \ debug/makefile.in \ @@ -165,7 +162,6 @@ EXTRA_DIST += \ extension/script/makefile.in \ filters/makefile.in \ helper/makefile.in \ - inkjar/makefile.in \ io/makefile.in \ io/crystalegg.xml \ io/doc2html.xsl \ @@ -179,10 +175,8 @@ EXTRA_DIST += \ livarot/makefile.in \ live_effects/makefile.in \ live_effects/parameter/makefile.in \ - removeoverlap/makefile.in \ svg/makefile.in \ trace/makefile.in \ - traits/makefile.in \ ui/makefile.in \ ui/cache/makefile.in \ ui/dialog/makefile.in \ @@ -199,10 +193,6 @@ EXTRA_DIST += \ extension/internal/emf-win32-print.cpp \ extension/internal/emf-win32-print.h \ helper/sp-marshal.list \ - traits/copy.h \ - traits/function.h \ - traits/list-copy.h \ - traits/reference.h \ show-preview.bmp \ $(jabber_whiteboard_SOURCES) \ $(CXXTEST_TEMPLATE) @@ -222,10 +212,11 @@ libinkscape_a_SOURCES = $(ink_common_sources) inkscape_SOURCES += main.cpp $(win32_sources) inkscape_LDADD = $(all_libs) -inkscape_LDFLAGS = --export-dynamic $(kdeldflags) +inkscape_LDFLAGS = --export-dynamic $(kdeldflags) $(mwindows) inkview_SOURCES += inkview.cpp $(win32_sources) inkview_LDADD = $(all_libs) +inkview_LDFLAGS = $(mwindows) # ################################################ # VERSION REPORTING @@ -233,7 +224,7 @@ inkview_LDADD = $(all_libs) libinkversion_a_SOURCES = inkscape-version.cpp inkscape-version.h -if USE_SVN_VERSION +if USE_BZR_VERSION inkscape_version_deps = $(top_srcdir)/.bzr/branch/last-revision endif @@ -241,7 +232,7 @@ endif # someone updates the BZR working directory. inkscape-version.cpp: $(inkscape_version_deps) VER_PREFIX="$(VERSION)";\ - VER_BZRREV=" r`bzr log -r '-1..' | sed -n -e '/^revno:/s/revno: \(.*\)/\1/p'`"; \ + VER_BZRREV=" r`bzr revno`"; \ if test ! -z "`bzr status -S -V $(srcdir)`"; then \ VER_CUSTOM=" custom"; \ fi; \ diff --git a/src/Makefile_insert b/src/Makefile_insert index b32889f65..fbaca931e 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -1,8 +1,8 @@ ## Makefile.am fragment, included by src/Makefile.am. ink_common_sources += \ - algorithms/find-last-if.h \ - algorithms/longest-common-suffix.h \ + util/find-last-if.h \ + util/longest-common-suffix.h \ approx-equal.h remove-last.h \ arc-context.cpp arc-context.h \ attributes.cpp attributes.h \ @@ -57,7 +57,6 @@ ink_common_sources += \ fixes.cpp \ flood-context.cpp flood-context.h \ forward.h \ - gc-allocator.h \ gc-alloc.h \ gc-anchored.h gc-anchored.cpp \ gc-core.h \ @@ -68,6 +67,7 @@ ink_common_sources += \ gradient-chemistry.cpp gradient-chemistry.h \ gradient-context.cpp gradient-context.h \ gradient-drag.cpp gradient-drag.h \ + graphlayout.cpp graphlayout.h \ guide-snapper.cpp guide-snapper.h \ help.cpp help.h \ helper-fns.h \ @@ -102,8 +102,6 @@ ink_common_sources += \ message-stack.cpp message-stack.h \ mod360.cpp mod360.h \ modifier-fns.h \ - node-context.cpp node-context.h \ - nodepath.cpp nodepath.h \ object-edit.cpp object-edit.h \ object-hierarchy.cpp object-hierarchy.h \ object-snapper.cpp object-snapper.h \ @@ -120,6 +118,7 @@ ink_common_sources += \ print.cpp print.h \ profile-manager.cpp profile-manager.h \ proj_pt.cpp proj_pt.h \ + removeoverlap.cpp removeoverlap.h \ rdf.cpp rdf.h \ rect-context.cpp rect-context.h \ require-config.h \ @@ -136,6 +135,7 @@ ink_common_sources += \ shape-editor.cpp shape-editor.h \ shortcuts.cpp shortcuts.h \ snap.cpp snap.h \ + snap-enums.h snap-candidate.h \ snapped-curve.cpp snapped-curve.h \ snapped-line.cpp snapped-line.h \ snapped-point.cpp snapped-point.h \ @@ -209,8 +209,7 @@ ink_common_sources += \ sp-shape.cpp sp-shape.h \ sp-spiral.cpp sp-spiral.h \ sp-star.cpp sp-star.h \ - sp-stop-fns.h \ - sp-stop.h \ + sp-stop.cpp sp-stop.h \ sp-string.cpp sp-string.h \ sp-style-elem.cpp sp-style-elem.h \ sp-switch.cpp sp-switch.h \ diff --git a/src/algorithms/CMakeLists.txt b/src/algorithms/CMakeLists.txt deleted file mode 100644 index 0ac17f57c..000000000 --- a/src/algorithms/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -#SET(algorithms_SRC -#find-if-before.h -#find-last-if.h -#longest-common-suffix.h -#) -#ADD_LIBRARY(algorithms STATIC ${algorithms_SRC})
\ No newline at end of file diff --git a/src/algorithms/Makefile_insert b/src/algorithms/Makefile_insert deleted file mode 100644 index dff5b578d..000000000 --- a/src/algorithms/Makefile_insert +++ /dev/null @@ -1,5 +0,0 @@ - -algorithms/all: - -algorithms/clean: - diff --git a/src/algorithms/makefile.in b/src/algorithms/makefile.in deleted file mode 100644 index 112aae15d..000000000 --- a/src/algorithms/makefile.in +++ /dev/null @@ -1,17 +0,0 @@ -# Convenience stub makefile to call the real Makefile. - -@SET_MAKE@ - -OBJEXT = @OBJEXT@ - -# Explicit so that it's the default rule. -all: - cd .. && $(MAKE) algorithms/all - -clean %.a %.$(OBJEXT): - cd .. && $(MAKE) algorithms/$@ - -.PHONY: all clean - -.SUFFIXES: -.SUFFIXES: .a .$(OBJEXT) diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt deleted file mode 100644 index e2e9ff892..000000000 --- a/src/api/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -SET(api_SRC -#inkscape.idl -) diff --git a/src/api/inkscape.idl b/src/api/inkscape.idl deleted file mode 100644 index 482184f18..000000000 --- a/src/api/inkscape.idl +++ /dev/null @@ -1,178 +0,0 @@ -#ifndef __INKSCAPE_IDL__ -#define __INKSCAPE_IDL__ -/** - * Inkscape API Description - * - * This is a place where people can discuss and modify their ideas - * of what an Inkscape exported API should look like. - * - * This IDL file should be considered to be somewhat of a Wish List, - * and people should feel free to add their ideas to it. - * - * Authors: - * Inkscape members - * - * Copyright (C) 2007 Inkscape.org - * - * 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.1 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, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - - -module inkscape -{ -module api -{ - - /** - * This will actually be defined as a Glib::ustring - * but people should use the word 'String' - */ - valuetype String sequence<unsigned short>; - - /** - * Forward declarations - */ - interface Env; - interface Application; - interface Document; - interface Desktop; - interface DialogManager; - - - - exception Exception { - unsigned short code; - }; - - /** - * ExceptionCodes - * These muse be constants, since languages handle enums - * differently. - */ - const unsigned short INKER = 1; - const unsigned short INKERR_NOT_FOUND = 2; - const unsigned short INKERR_NOT_SUPPORTED = 3; - - - - /** - * This is the top-level root of all Inkscape interfaces. - * It is the single point of contact from which all other - * interfaces can be accessed. Anything top-level or - * global should be parented here. - * - * This allows an entire set of interfaces to be exported - * to a shared object with only a single symbol. This symbol - * can be accessed something like: - * - * typedef (Env *)(EnvCreateFunc)(); - * EnvCreateFunc createEnvironment = - * (EnvCreateFunc) dlsym("createEnvironment"); - * Env *ink = createEnvironment(); - * - * - * This allows shared object linking to be much easier, and - * allows binding to other languages to be performed however - * is best for each. - * - */ - interface Env { - - /** - * This is the main application, which - */ - Application *getApplication(); - - }; - - /** - * - * Top-level Inkscape application - * - */ - interface Application { - - /** - * Return the currently-focused desktop - */ - Desktop *getActiveDesktop(); - - /** - * Return the document in the currently-focused desktop - */ - Document *getActiveDocument(); - - }; - - /** - * - * This is a main editing window, with all of its chrome. - * - */ - interface Desktop { - - /** - * Return the dialog manager for this desktop - */ - DialogManager *getDialogManager(); - - }; - - /** - * An Inkscape Document, which is SPObject + XML - * - */ - interface Document { - - - /** - * Copy the document's XML into a DOM tree and - * return a pointer to it. - */ - org::w3c::dom::Document *getDOM(); - - /** - * Set the Document's repr tree to the specified - * DOM document - */ - bool setDOM(org::w3c::dom::Document *doc); - - }; - - /** - * The interface that controls Inkscape's dialogs - * - */ - interface DialogManager { - - /** - * - */ - //some method - - }; - - - - - -}; // module api - -}; // module inkscape - - -#endif // __INKSCAPE_IDL__ - diff --git a/src/arc-context.cpp b/src/arc-context.cpp index e689c93db..799167a72 100644 --- a/src/arc-context.cpp +++ b/src/arc-context.cpp @@ -116,15 +116,15 @@ static void sp_arc_context_init(SPArcContext *arc_context) static void sp_arc_context_finish(SPEventContext *ec) { SPArcContext *ac = SP_ARC_CONTEXT(ec); - SPDesktop *desktop = ec->desktop; + SPDesktop *desktop = ec->desktop; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); - sp_arc_finish(ac); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); + sp_arc_finish(ac); ac->sel_changed_connection.disconnect(); if (((SPEventContextClass *) parent_class)->finish) { - ((SPEventContextClass *) parent_class)->finish(ec); - } + ((SPEventContextClass *) parent_class)->finish(ec); + } } static void sp_arc_context_dispose(GObject *object) @@ -244,9 +244,7 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent /* Snap center */ SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); - Geom::Point pt2g = to_2geom(ac->center); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); - ac->center = from_2geom(pt2g); + m.freeSnapReturnByRef(ac->center, Inkscape::SNAPSOURCE_NODE_HANDLE); sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | @@ -276,6 +274,13 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent gobble_motion_events(GDK_BUTTON1_MASK); ret = TRUE; + } else if (!sp_event_context_knot_mouseover(ac)){ + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case GDK_BUTTON_RELEASE: @@ -285,7 +290,7 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent sp_event_context_discard_delayed_snap_event(event_context); if (!event_context->within_tolerance) { // we've been dragging, finish the arc - sp_arc_finish(ac); + sp_arc_finish(ac); } else if (event_context->item_to_select) { // no dragging, select clicked item if any if (event->button.state & GDK_SHIFT_MASK) { @@ -337,14 +342,14 @@ static gint sp_arc_context_root_handler(SPEventContext *event_context, GdkEvent } break; case GDK_Escape: - if (dragging) { - dragging = false; - sp_event_context_discard_delayed_snap_event(event_context); - // if drawing, cancel, otherwise pass it up for deselecting - sp_arc_cancel(ac); - ret = TRUE; - } - break; + if (dragging) { + dragging = false; + sp_event_context_discard_delayed_snap_event(event_context); + // if drawing, cancel, otherwise pass it up for deselecting + sp_arc_cancel(ac); + ret = TRUE; + } + break; case GDK_space: if (dragging) { sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), @@ -482,13 +487,13 @@ static void sp_arc_finish(SPArcContext *ac) if (ac->item != NULL) { - SPGenericEllipse *ge = SP_GENERICELLIPSE(SP_ARC(ac->item)); - if (ge->rx.computed == 0 || ge->ry.computed == 0) { - sp_arc_cancel(ac); // Don't allow the creating of zero sized arc, for example when the start and and point snap to the snap grid point - return; - } + SPGenericEllipse *ge = SP_GENERICELLIPSE(SP_ARC(ac->item)); + if (ge->rx.computed == 0 || ge->ry.computed == 0) { + sp_arc_cancel(ac); // Don't allow the creating of zero sized arc, for example when the start and and point snap to the snap grid point + return; + } - SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; + SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; SP_OBJECT(ac->item)->updateRepr(); @@ -504,14 +509,14 @@ static void sp_arc_finish(SPArcContext *ac) static void sp_arc_cancel(SPArcContext *ac) { - SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; + SPDesktop *desktop = SP_EVENT_CONTEXT(ac)->desktop; - sp_desktop_selection(desktop)->clear(); - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); + sp_desktop_selection(desktop)->clear(); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); if (ac->item != NULL) { - SP_OBJECT(ac->item)->deleteObject(); - ac->item = NULL; + SP_OBJECT(ac->item)->deleteObject(); + ac->item = NULL; } ac->within_tolerance = false; diff --git a/src/attributes-test.h b/src/attributes-test.h index 1d8f32843..6677294f2 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -378,6 +378,8 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:connector-type", true}, {"inkscape:connection-start", true}, {"inkscape:connection-end", true}, + {"inkscape:connection-points", true}, + {"inkscape:connector-curvature", true}, {"inkscape:connector-avoid", true}, {"inkscape:connector-spacing", true}, {"sodipodi:cx", true}, diff --git a/src/attributes.cpp b/src/attributes.cpp index d03c3be3e..3cfe04fab 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -77,6 +77,10 @@ static SPStyleProp const props[] = { {SP_ATTR_BORDERCOLOR, "bordercolor"}, {SP_ATTR_BORDEROPACITY, "borderopacity"}, {SP_ATTR_PAGECOLOR, "pagecolor"}, + {SP_ATTR_FIT_MARGIN_TOP, "fit-margin-top"}, + {SP_ATTR_FIT_MARGIN_LEFT, "fit-margin-left"}, + {SP_ATTR_FIT_MARGIN_RIGHT, "fit-margin-right"}, + {SP_ATTR_FIT_MARGIN_BOTTOM, "fit-margin-bottom"}, {SP_ATTR_INKSCAPE_PAGEOPACITY, "inkscape:pageopacity"}, {SP_ATTR_INKSCAPE_PAGESHADOW, "inkscape:pageshadow"}, {SP_ATTR_INKSCAPE_ZOOM, "inkscape:zoom"}, @@ -98,8 +102,8 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS, "inkscape:snap-midpoints"}, {SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS, "inkscape:snap-object-midpoints"}, {SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS, "inkscape:snap-bbox-edge-midpoints"}, - {SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, "inkscape:snap-bbox-midpoints"}, - {SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, "inkscape:snap-intersection-paths"}, + {SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, "inkscape:snap-bbox-midpoints"}, + {SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, "inkscape:snap-intersection-paths"}, {SP_ATTR_INKSCAPE_OBJECT_PATHS, "inkscape:object-paths"}, {SP_ATTR_INKSCAPE_OBJECT_NODES, "inkscape:object-nodes"}, {SP_ATTR_INKSCAPE_BBOX_PATHS, "inkscape:bbox-paths"}, @@ -123,9 +127,11 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_ORIGINAL_D, "inkscape:original-d"}, /* (Note: XML representation of connectors may change in future.) */ {SP_ATTR_CONNECTOR_TYPE, "inkscape:connector-type"}, + {SP_ATTR_CONNECTOR_CURVATURE, "inkscape:connector-curvature"}, {SP_ATTR_CONNECTION_START, "inkscape:connection-start"}, {SP_ATTR_CONNECTION_END, "inkscape:connection-end"}, - {SP_ATTR_CONNECTOR_CURVATURE, "inkscape:connector-curvature"}, + {SP_ATTR_CONNECTION_START_POINT, "inkscape:connection-start-point"}, + {SP_ATTR_CONNECTION_END_POINT, "inkscape:connection-end-point"}, /* SPRect */ {SP_ATTR_RX, "rx"}, {SP_ATTR_RY, "ry"}, diff --git a/src/attributes.h b/src/attributes.h index af60b75be..c21087e36 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -77,6 +77,10 @@ enum SPAttributeEnum { SP_ATTR_BORDERCOLOR, SP_ATTR_BORDEROPACITY, SP_ATTR_PAGECOLOR, + SP_ATTR_FIT_MARGIN_TOP, + SP_ATTR_FIT_MARGIN_LEFT, + SP_ATTR_FIT_MARGIN_RIGHT, + SP_ATTR_FIT_MARGIN_BOTTOM, SP_ATTR_INKSCAPE_PAGEOPACITY, SP_ATTR_INKSCAPE_PAGESHADOW, SP_ATTR_INKSCAPE_ZOOM, @@ -98,7 +102,7 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS, SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS, - SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, + SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, //SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, SP_ATTR_INKSCAPE_OBJECT_PATHS, @@ -123,9 +127,11 @@ enum SPAttributeEnum { SP_ATTR_D, SP_ATTR_INKSCAPE_ORIGINAL_D, SP_ATTR_CONNECTOR_TYPE, + SP_ATTR_CONNECTOR_CURVATURE, SP_ATTR_CONNECTION_START, SP_ATTR_CONNECTION_END, - SP_ATTR_CONNECTOR_CURVATURE, + SP_ATTR_CONNECTION_START_POINT, + SP_ATTR_CONNECTION_END_POINT, /* SPRect */ SP_ATTR_RX, SP_ATTR_RY, diff --git a/src/box3d-context.cpp b/src/box3d-context.cpp index c8fbfa877..5534aa410 100644 --- a/src/box3d-context.cpp +++ b/src/box3d-context.cpp @@ -316,7 +316,7 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven /* Snap center */ SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, bc->item); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, button_dt, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); bc->center = from_2geom(button_dt); sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), @@ -348,7 +348,7 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, bc->item); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, motion_dt, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); bc->ctrl_dragged = event->motion.state & GDK_CONTROL_MASK; @@ -358,8 +358,8 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven } if (!bc->extruded) { - bc->drag_ptB = from_2geom(motion_dt); - bc->drag_ptC = from_2geom(motion_dt); + bc->drag_ptB = from_2geom(motion_dt); + bc->drag_ptC = from_2geom(motion_dt); bc->drag_ptB_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(motion_dt), 0, Proj::Z); bc->drag_ptC_proj = bc->drag_ptB_proj; @@ -381,14 +381,19 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven bc->drag_ptB_proj.normalize(); bc->drag_ptC_proj = cur_persp->perspective_impl->tmat.preimage (from_2geom(motion_dt), bc->drag_ptB_proj[Proj::X], Proj::X); } - Geom::Point pt2g = to_2geom(bc->drag_ptC); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); - bc->drag_ptC = from_2geom(pt2g); + m.freeSnapReturnByRef(bc->drag_ptC, Inkscape::SNAPSOURCE_NODE_HANDLE); } sp_box3d_drag(*bc, event->motion.state); ret = TRUE; + } else if (!sp_event_context_knot_mouseover(bc)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case GDK_BUTTON_RELEASE: diff --git a/src/color-profile.cpp b/src/color-profile.cpp index 310a37356..43709793c 100644 --- a/src/color-profile.cpp +++ b/src/color-profile.cpp @@ -35,7 +35,7 @@ #include "dom/util/digest.h" #ifdef WIN32 -#include <Icm.h> +#include <icm.h> #endif // WIN32 using Inkscape::ColorProfile; diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp index b2aa0ce6b..88c84a84c 100644 --- a/src/conn-avoid-ref.cpp +++ b/src/conn-avoid-ref.cpp @@ -341,7 +341,7 @@ GSList *SPAvoidRef::getAttachedShapes(const unsigned int type) GSList *list = NULL; Avoid::IntList shapes; - GQuark shapeId = g_quark_from_string(item->id); + GQuark shapeId = g_quark_from_string(item->getId()); item->document->router->attachedShapes(shapes, shapeId, type); Avoid::IntList::iterator finish = shapes.end(); @@ -365,7 +365,7 @@ GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type) GSList *list = NULL; Avoid::IntList conns; - GQuark shapeId = g_quark_from_string(item->id); + GQuark shapeId = g_quark_from_string(item->getId()); item->document->router->attachedConns(conns, shapeId, type); Avoid::IntList::iterator finish = conns.end(); diff --git a/src/connector-context.cpp b/src/connector-context.cpp index 0fc9de9d0..b0e192190 100644 --- a/src/connector-context.cpp +++ b/src/connector-context.cpp @@ -218,7 +218,7 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item); static void cc_clear_active_shape(SPConnectorContext *cc); static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item); static void cc_clear_active_conn(SPConnectorContext *cc); -static gchar *conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& w); +static bool conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid); static void cc_select_handle(SPKnot* knot); static void cc_deselect_handle(SPKnot* knot); static bool cc_item_is_shape(SPItem *item); @@ -233,6 +233,9 @@ static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *nam gpointer data); +static char* cc_knot_tips[] = { _("<b>Connection point</b>: click or drag to create a new connector"), + _("<b>Connection point</b>: click to select, drag to move") }; + /*static Geom::Point connector_drag_origin_w(0, 0); static bool connector_within_tolerance = false;*/ static SPEventContextClass *parent_class; @@ -338,8 +341,10 @@ sp_connector_context_init(SPConnectorContext *cc) cc->endpt_handle[i] = NULL; cc->endpt_handler_id[i] = 0; } - cc->sid = NULL; - cc->eid = NULL; + cc->shref = NULL; + cc->scpid = NULL; + cc->ehref = NULL; + cc->ecpid = NULL; cc->npoints = 0; cc->state = SP_CONNECTOR_CONTEXT_IDLE; } @@ -366,13 +371,21 @@ sp_connector_context_dispose(GObject *object) cc->endpt_handle[i] = NULL; } } - if (cc->sid) { - g_free(cc->sid); - cc->sid = NULL; + if (cc->shref) { + g_free(cc->shref); + cc->shref = NULL; + } + if (cc->scpid) { + g_free(cc->scpid); + cc->scpid = NULL; } - if (cc->eid) { - g_free(cc->eid); - cc->eid = NULL; + if (cc->ehref) { + g_free(cc->shref); + cc->shref = NULL; + } + if (cc->ecpid) { + g_free(cc->scpid); + cc->scpid = NULL; } g_assert( cc->newConnRef == NULL ); @@ -570,8 +583,8 @@ cc_clear_active_conn(SPConnectorContext *cc) } -static gchar * -conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p) +static bool +conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid) { // TODO: this will need to change when there are more connection // points available for each shape. @@ -580,10 +593,13 @@ conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p) { p = cc->active_handle->pos; const ConnectionPoint& cp = cc->connpthandles[cc->active_handle]; - return g_strdup_printf("#%s_%c_%d", SP_OBJECT_ID(cc->active_shape), - cp.type == ConnPointDefault ? 'd' : 'u' , cp.id); + *href = g_strdup_printf("#%s", cc->active_shape->getId()); + *cpid = g_strdup_printf("%c%d", cp.type == ConnPointDefault ? 'd' : 'u' , cp.id); + return true; } - return NULL; + *href = NULL; + *cpid = NULL; + return false; } static void @@ -769,12 +785,12 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const Geom::Point p = event_dt; // Test whether we clicked on a connection point - cc->sid = conn_pt_handle_test(cc, p); + bool found = conn_pt_handle_test(cc, p, &cc->shref, &cc->scpid); - if (!cc->sid) { + if (!found) { // This is the first point, so just snap it to the grid // as there's no other points to go off. - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); } spcc_connector_set_initial_point(cc, p); @@ -786,12 +802,12 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const case SP_CONNECTOR_CONTEXT_DRAGGING: { // This is the second click of a connector creation. - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); spcc_connector_set_subsequent_point(cc, p); spcc_connector_finish_segment(cc, p); // Test whether we clicked on a connection point - cc->eid = conn_pt_handle_test(cc, p); + /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid); if (cc->npoints != 0) { spcc_connector_finish(cc); } @@ -934,8 +950,7 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons gobble_motion_events(mevent.state); // This is movement during a connector creation. if ( cc->npoints > 0 ) { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, - Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); cc->selection->clear(); spcc_connector_set_subsequent_point(cc, p); ret = TRUE; @@ -947,8 +962,7 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons gobble_motion_events(GDK_BUTTON1_MASK); g_assert( SP_IS_PATH(cc->clickeditem)); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, - Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); // Update the hidden path Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem); @@ -978,6 +992,9 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons /* This is perfectly valid */ break; default: + if (!sp_event_context_knot_mouseover(cc)) { + m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE)); + } break; } } @@ -1023,7 +1040,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con //case SP_CONNECTOR_CONTEXT_POINT: case SP_CONNECTOR_CONTEXT_DRAGGING: { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); if (cc->within_tolerance) { @@ -1034,7 +1051,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con spcc_connector_set_subsequent_point(cc, p); spcc_connector_finish_segment(cc, p); // Test whether we clicked on a connection point - cc->eid = conn_pt_handle_test(cc, p); + /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid); if (cc->npoints != 0) { spcc_connector_finish(cc); } @@ -1044,7 +1061,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con } case SP_CONNECTOR_CONTEXT_REROUTING: { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); cc_connector_rerouting_finish(cc, &p); sp_document_ensure_up_to_date(doc); @@ -1068,7 +1085,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con if (!cc->within_tolerance) { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; cp.pos = p * sp_item_dt2i_affine(cc->active_shape); @@ -1081,7 +1098,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con case SP_CONNECTOR_CONTEXT_NEWCONNPOINT: - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); @@ -1184,7 +1201,7 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) if (!cc->within_tolerance) { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); ConnectionPoint& cp = cc->connpthandles[cc->selected_handle]; cp.pos = p * sp_item_dt2i_affine(cc->active_shape); @@ -1212,7 +1229,7 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval) m.setup(desktop); Geom::Point p = cc->selected_handle->pos; - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); sp_knot_set_position(cc->selected_handle, p, 0); @@ -1264,16 +1281,21 @@ cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p if (p != NULL) { // Test whether we clicked on a connection point - gchar *shape_label = conn_pt_handle_test(cc, *p); + gchar *shape_label, *cpid; + bool found = conn_pt_handle_test(cc, *p, &shape_label, &cpid); - if (shape_label) { + if (found) { if (cc->clickedhandle == cc->endpt_handle[0]) { sp_object_setAttribute(cc->clickeditem, - "inkscape:connection-start",shape_label, false); + "inkscape:connection-start", shape_label, false); + sp_object_setAttribute(cc->clickeditem, + "inkscape:connection-start-point", cpid, false); } else { sp_object_setAttribute(cc->clickeditem, - "inkscape:connection-end",shape_label, false); + "inkscape:connection-end", shape_label, false); + sp_object_setAttribute(cc->clickeditem, + "inkscape:connection-end-point", cpid, false); } g_free(shape_label); } @@ -1416,17 +1438,23 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc) cc->isOrthogonal ? "orthogonal" : "polyline", false); sp_object_setAttribute(cc->newconn, "inkscape:connector-curvature", Glib::Ascii::dtostr(cc->curvature).c_str(), false); - if (cc->sid) + if (cc->shref) { sp_object_setAttribute(cc->newconn, "inkscape:connection-start", - cc->sid, false); + cc->shref, false); + if (cc->scpid) + sp_object_setAttribute(cc->newconn, "inkscape:connection-start-point", + cc->scpid, false); connection = true; } - if (cc->eid) + if (cc->ehref) { sp_object_setAttribute(cc->newconn, "inkscape:connection-end", - cc->eid, false); + cc->ehref, false); + if (cc->ecpid) + sp_object_setAttribute(cc->newconn, "inkscape:connection-end-point", + cc->ecpid, false); connection = true; } // Process pending updates. diff --git a/src/connector-context.h b/src/connector-context.h index 640a03aae..bd3805e96 100644 --- a/src/connector-context.h +++ b/src/connector-context.h @@ -47,8 +47,6 @@ enum { SP_CONNECTOR_CONTEXT_DRAWING_MODE, SP_CONNECTOR_CONTEXT_EDITING_MODE }; -static char* cc_knot_tips[] = { _("<b>Connection point</b>: click or drag to create a new connector"), - _("<b>Connection point</b>: click to select, drag to move") }; typedef std::map<SPKnot *, ConnectionPoint> ConnectionPointMap; @@ -70,16 +68,16 @@ struct SPConnectorContext : public SPEventContext { SPCanvasItem *red_bpath; SPCurve *red_curve; guint32 red_color; - + // Green curve SPCurve *green_curve; - + // The new connector SPItem *newconn; Avoid::ConnRef *newConnRef; gdouble curvature; bool isOrthogonal; - + // The active shape SPItem *active_shape; Inkscape::XML::Node *active_shape_repr; @@ -90,7 +88,7 @@ struct SPConnectorContext : public SPEventContext { Inkscape::XML::Node *active_conn_repr; sigc::connection sel_changed_connection; - + // The activehandle SPKnot *active_handle; @@ -99,12 +97,14 @@ struct SPConnectorContext : public SPEventContext { SPItem *clickeditem; SPKnot *clickedhandle; - + ConnectionPointMap connpthandles; SPKnot *endpt_handle[2]; guint endpt_handler_id[2]; - gchar *sid; - gchar *eid; + gchar *shref; + gchar *scpid; + gchar *ehref; + gchar *ecpid; SPCanvasItem *c0, *c1, *cl0, *cl1; }; diff --git a/src/context-fns.cpp b/src/context-fns.cpp index 8e4b6384c..0ff7bd120 100644 --- a/src/context-fns.cpp +++ b/src/context-fns.cpp @@ -131,12 +131,12 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item Inkscape::SnappedPoint s[2]; /* Try to snap p[0] (the opposite corner) along the constraint vector */ - s[0] = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[0]), Inkscape::SNAPSOURCE_HANDLE, - Inkscape::Snapper::ConstraintLine(p[0] - p[1]), false); + s[0] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE), + Inkscape::Snapper::ConstraintLine(p[0] - p[1])); /* Try to snap p[1] (the dragged corner) along the constraint vector */ - s[1] = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[1]), Inkscape::SNAPSOURCE_HANDLE, - Inkscape::Snapper::ConstraintLine(p[1] - p[0]), false); + s[1] = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), + Inkscape::Snapper::ConstraintLine(p[1] - p[0])); /* Choose the best snap and update points accordingly */ if (s[0].getSnapDistance() < s[1].getSnapDistance()) { @@ -156,8 +156,8 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item /* Our origin is the opposite corner. Snap the drag point along the constraint vector */ p[0] = center; - snappoint = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[1]), Inkscape::SNAPSOURCE_HANDLE, - Inkscape::Snapper::ConstraintLine(p[1] - p[0]), false); + snappoint = m.constrainedSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE), + Inkscape::Snapper::ConstraintLine(p[1] - p[0])); if (snappoint.getSnapped()) { p[1] = snappoint.getPoint(); } @@ -174,8 +174,8 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item Inkscape::SnappedPoint s[2]; - s[0] = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[0]), Inkscape::SNAPSOURCE_HANDLE); - s[1] = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p[1]), Inkscape::SNAPSOURCE_HANDLE); + s[0] = m.freeSnap(Inkscape::SnapCandidatePoint(p[0], Inkscape::SNAPSOURCE_NODE_HANDLE)); + s[1] = m.freeSnap(Inkscape::SnapCandidatePoint(p[1], Inkscape::SNAPSOURCE_NODE_HANDLE)); if (s[0].getSnapDistance() < s[1].getSnapDistance()) { if (s[0].getSnapped()) { @@ -196,7 +196,7 @@ Geom::Rect Inkscape::snap_rectangular_box(SPDesktop const *desktop, SPItem *item /* There's no constraint on the corner point, so just snap it to anything */ p[0] = center; p[1] = pt; - snappoint = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(pt), Inkscape::SNAPSOURCE_HANDLE); + snappoint = m.freeSnap(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_HANDLE)); if (snappoint.getSnapped()) { p[1] = snappoint.getPoint(); } diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index cea478f85..810f501d7 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -5,6 +5,7 @@ * Lauris Kaplinski <lauris@kaplinski.com> * * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 1999-2010 Others * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -87,9 +88,6 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge if (event->button.button == 1) { dragging = true; - // FIXME: The snap delay mechanism won't work here, because it has been implemented for the event context. Dragging - // guides off the ruler will send event to the ruler and not to the context, which bypasses sp_event_context_snap_delay_handler - Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win)); Geom::Point const event_dt(desktop->w2d(event_w)); @@ -145,12 +143,14 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win)); Geom::Point event_dt(desktop->w2d(event_w)); - SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop); - // We only have a temporary guide which is not stored in our document yet. - // Because the guide snapper only looks in the document for guides to snap to, - // we don't have to worry about a guide snapping to itself here - m.guideFreeSnap(event_dt, normal, SP_DRAG_MOVE_ORIGIN); + if (!(event->motion.state & GDK_SHIFT_MASK)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + // We only have a temporary guide which is not stored in our document yet. + // Because the guide snapper only looks in the document for guides to snap to, + // we don't have to worry about a guide snapping to itself here + m.guideFreeSnap(event_dt, normal, SP_DRAG_MOVE_ORIGIN); + } sp_guideline_set_position(SP_GUIDELINE(guide), from_2geom(event_dt)); desktop->set_coordinate_status(to_2geom(event_dt)); @@ -159,21 +159,23 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge break; case GDK_BUTTON_RELEASE: if (dragging && event->button.button == 1) { + sp_event_context_discard_delayed_snap_event(desktop->event_context); + gdk_pointer_ungrab(event->button.time); Geom::Point const event_w(sp_canvas_window_to_world(dtw->canvas, event_win)); Geom::Point event_dt(desktop->w2d(event_w)); - SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop); - // We only have a temporary guide which is not stored in our document yet. - // Because the guide snapper only looks in the document for guides to snap to, - // we don't have to worry about a guide snapping to itself here - m.guideFreeSnap(event_dt, normal, SP_DRAG_MOVE_ORIGIN); + if (!(event->button.state & GDK_SHIFT_MASK)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + // We only have a temporary guide which is not stored in our document yet. + // Because the guide snapper only looks in the document for guides to snap to, + // we don't have to worry about a guide snapping to itself here + m.guideFreeSnap(event_dt, normal, SP_DRAG_MOVE_ORIGIN); + } dragging = false; - sp_event_context_discard_delayed_snap_event(desktop->event_context); - gtk_object_destroy(GTK_OBJECT(guide)); guide = NULL; if ((horiz ? wy : wx) >= 0) { @@ -187,16 +189,8 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge _("Create guide")); } desktop->set_coordinate_status(from_2geom(event_dt)); - - // A dt_ruler_event might be emitted when dragging a guide of the rulers - // while drawing a Bezier curve. In such a situation, we're already in that - // specific context and the snap delay is already active. We should interfere - // with that context and we should therefore leave the snap delay status - // as it is. So although it might have been set to active above on - // GDK_BUTTON_PRESS, we should not set it back to inactive here. That must be - // done by the context. } - default: + default: break; } @@ -205,11 +199,17 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge int sp_dt_hruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw) { + if (event->type == GDK_MOTION_NOTIFY) { + sp_event_context_snap_delay_handler(dtw->desktop->event_context, (gpointer) widget, (gpointer) dtw, (GdkEventMotion *)event, DelayedSnapEvent::GUIDE_HRULER); + } return sp_dt_ruler_event(widget, event, dtw, true); } int sp_dt_vruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidget *dtw) { + if (event->type == GDK_MOTION_NOTIFY) { + sp_event_context_snap_delay_handler(dtw->desktop->event_context, (gpointer) widget, (gpointer) dtw, (GdkEventMotion *)event, DelayedSnapEvent::GUIDE_VRULER); + } return sp_dt_ruler_event(widget, event, dtw, false); } @@ -230,7 +230,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) SPDesktop *desktop = static_cast<SPDesktop*>(gtk_object_get_data(GTK_OBJECT(item->canvas), "SPDesktop")); switch (event->type) { - case GDK_2BUTTON_PRESS: + case GDK_2BUTTON_PRESS: if (event->button.button == 1) { drag_type = SP_DRAG_NONE; sp_event_context_discard_delayed_snap_event(desktop->event_context); @@ -239,7 +239,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) ret = TRUE; } break; - case GDK_BUTTON_PRESS: + case GDK_BUTTON_PRESS: if (event->button.button == 1) { Geom::Point const event_w(event->button.x, event->button.y); Geom::Point const event_dt(desktop->w2d(event_w)); @@ -279,6 +279,8 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) event->motion.y); Geom::Point motion_dt(desktop->w2d(motion_w)); + sp_event_context_snap_delay_handler(desktop->event_context, (gpointer) item, data, (GdkEventMotion *)event, DelayedSnapEvent::GUIDE_HANDLER); + // This is for snapping while dragging existing guidelines. New guidelines, // which are dragged off the ruler, are being snapped in sp_dt_ruler_event SnapManager &m = desktop->namedview->snap_manager; @@ -288,13 +290,16 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) // be forced to be on the guide. If we don't snap however, then // the origin should still be constrained to the guide. So let's do // that explicitly first: - Geom::Line line(guide->point_on_line, guide->angle()); Geom::Coord t = line.nearestPoint(motion_dt); motion_dt = line.pointAt(t); - m.guideConstrainedSnap(motion_dt, *guide); - } else { - m.guideFreeSnap(motion_dt, guide->normal_to_line, drag_type); + if (!(event->motion.state & GDK_SHIFT_MASK)) { + m.guideConstrainedSnap(motion_dt, *guide); + } + } else if (!(event->motion.state & GDK_SHIFT_MASK)) { + if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) { + m.guideFreeSnap(motion_dt, guide->normal_to_line, drag_type); + } } switch (drag_type) { @@ -307,7 +312,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) { Geom::Point pt = motion_dt - guide->point_on_line; double angle = std::atan2(pt[Geom::Y], pt[Geom::X]); - if (event->motion.state & GDK_CONTROL_MASK) { + if (event->motion.state & GDK_CONTROL_MASK) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); if (snaps) { @@ -321,7 +326,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) case SP_DRAG_MOVE_ORIGIN: { sp_guide_moveto(*guide, motion_dt, false); - break; + break; } case SP_DRAG_NONE: g_assert_not_reached(); @@ -336,6 +341,8 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) break; case GDK_BUTTON_RELEASE: if (drag_type != SP_DRAG_NONE && event->button.button == 1) { + sp_event_context_discard_delayed_snap_event(desktop->event_context); + if (moved) { Geom::Point const event_w(event->button.x, event->button.y); @@ -344,16 +351,20 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, NULL, NULL, guide); if (drag_type == SP_DRAG_MOVE_ORIGIN) { - // If we snap in guideConstrainedSnap() below, then motion_dt will + // If we snap in guideConstrainedSnap() below, then motion_dt will // be forced to be on the guide. If we don't snap however, then // the origin should still be constrained to the guide. So let's // do that explicitly first: - Geom::Line line(guide->point_on_line, guide->angle()); + Geom::Line line(guide->point_on_line, guide->angle()); Geom::Coord t = line.nearestPoint(event_dt); event_dt = line.pointAt(t); - m.guideConstrainedSnap(event_dt, *guide); - } else { - m.guideFreeSnap(event_dt, guide->normal_to_line, drag_type); + if (!(event->button.state & GDK_SHIFT_MASK)) { + m.guideConstrainedSnap(event_dt, *guide); + } + } else if (!(event->button.state & GDK_SHIFT_MASK)) { + if (!((drag_type == SP_DRAG_ROTATE) && (event->motion.state & GDK_CONTROL_MASK))) { + m.guideFreeSnap(event_dt, guide->normal_to_line, drag_type); + } } if (sp_canvas_world_pt_inside_window(item->canvas, event_w)) { @@ -380,8 +391,8 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) } case SP_DRAG_MOVE_ORIGIN: { - sp_guide_moveto(*guide, event_dt, true); - break; + sp_guide_moveto(*guide, event_dt, true); + break; } case SP_DRAG_NONE: g_assert_not_reached(); @@ -402,7 +413,6 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) desktop->setPosition (from_2geom(event_dt)); } drag_type = SP_DRAG_NONE; - sp_event_context_discard_delayed_snap_event(desktop->event_context); sp_canvas_item_ungrab(item, event->button.time); ret=TRUE; } @@ -414,7 +424,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) Geom::Point const event_w(event->crossing.x, event->crossing.y); Geom::Point const event_dt(desktop->w2d(event_w)); - if (event->crossing.state & GDK_SHIFT_MASK) { + if ((event->crossing.state & GDK_SHIFT_MASK) && (drag_type != SP_DRAG_MOVE_ORIGIN)) { GdkCursor *guide_cursor; guide_cursor = gdk_cursor_new (GDK_EXCHANGE); gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, guide_cursor); @@ -444,15 +454,20 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data) sp_guide_remove(guide); sp_document_done(doc, SP_VERB_NONE, _("Delete guide")); ret = TRUE; + sp_event_context_discard_delayed_snap_event(desktop->event_context); break; } case GDK_Shift_L: case GDK_Shift_R: - GdkCursor *guide_cursor; - guide_cursor = gdk_cursor_new (GDK_EXCHANGE); - gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, guide_cursor); - gdk_cursor_unref(guide_cursor); - ret = TRUE; + if (drag_type != SP_DRAG_MOVE_ORIGIN) { + GdkCursor *guide_cursor; + guide_cursor = gdk_cursor_new (GDK_EXCHANGE); + gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, guide_cursor); + gdk_cursor_unref(guide_cursor); + ret = TRUE; + break; + } + default: // do nothing; break; diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 2afcd6109..f3b1b833d 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -475,8 +475,8 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill if (!SP_IS_LINEARGRADIENT(server)) return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server - SPGradient *vector = sp_gradient_get_vector ( SP_GRADIENT (server), FALSE ); - SPGradient *vector_res = sp_gradient_get_vector ( SP_GRADIENT (server_res), FALSE ); + SPGradient *vector = SP_GRADIENT(server)->getVector(); + SPGradient *vector_res = SP_GRADIENT(server_res)->getVector(); if (vector_res != vector) return QUERY_STYLE_MULTIPLE_DIFFERENT; // different gradient vectors @@ -485,8 +485,8 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill if (!SP_IS_RADIALGRADIENT(server)) return QUERY_STYLE_MULTIPLE_DIFFERENT; // different kind of server - SPGradient *vector = sp_gradient_get_vector ( SP_GRADIENT (server), FALSE ); - SPGradient *vector_res = sp_gradient_get_vector ( SP_GRADIENT (server_res), FALSE ); + SPGradient *vector = SP_GRADIENT(server)->getVector(); + SPGradient *vector_res = SP_GRADIENT(server_res)->getVector(); if (vector_res != vector) return QUERY_STYLE_MULTIPLE_DIFFERENT; // different gradient vectors diff --git a/src/desktop.cpp b/src/desktop.cpp index 0e4d4caf3..52f172577 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -91,6 +91,10 @@ #include "widgets/desktop-widget.h" #include "box3d-context.h" +// TODO those includes are only for node tool quick zoom. Remove them after fixing it. +#include "ui/tool/node-tool.h" +#include "ui/tool/control-point-selection.h" + #include "display/sp-canvas.h" namespace Inkscape { namespace XML { class Node; }} @@ -605,8 +609,10 @@ SPDesktop::change_document (SPDocument *theDocument) Gtk::Window *parent = this->getToplevel(); g_assert(parent != NULL); SPDesktopWidget *dtw = (SPDesktopWidget *) parent->get_data("desktopwidget"); - if (dtw) dtw->desktop = this; - sp_desktop_widget_update_namedview(dtw); + if (dtw) { + dtw->desktop = this; + } + dtw->updateNamedview(); _namedview_modified (namedview, SP_OBJECT_MODIFIED_FLAG, this); _document_replaced_signal.emit (this, theDocument); @@ -622,9 +628,12 @@ SPDesktop::set_event_context (GtkType type, const gchar *config) while (event_context) { ec = event_context; sp_event_context_deactivate (ec); - event_context = ec->next; + // we have to keep event_context valid during destruction - otherwise writing + // destructors is next to impossible + SPEventContext *next = ec->next; sp_event_context_finish (ec); g_object_unref (G_OBJECT (ec)); + event_context = next; } ec = sp_event_context_new (type, this, config, SP_EVENT_CONTEXT_STATIC); @@ -783,11 +792,12 @@ SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double int clear = FALSE; if (!NR_DF_TEST_CLOSE (newscale, scale, 1e-4 * scale)) { - /* Set zoom factors */ + // zoom changed - set new zoom factors _d2w = Geom::Scale(newscale, -newscale); _w2d = Geom::Scale(1/newscale, 1/-newscale); sp_canvas_item_affine_absolute(SP_CANVAS_ITEM(main), _d2w); clear = TRUE; + signal_zoom_changed.emit(_d2w.descrim()); } /* Calculate top left corner (in document pixels) */ @@ -873,11 +883,6 @@ SPDesktop::next_zoom() zooms_future = g_list_remove (zooms_future, ((NRRect *) zooms_future->data)); } -#include "tools-switch.h" -#include "node-context.h" -#include "shape-editor.h" -#include "nodepath.h" - /** \brief Performs a quick zoom into what the user is working on \param enable Whether we're going in or out of quick zoom @@ -893,67 +898,26 @@ SPDesktop::zoom_quick (bool enable) _quick_zoom_stored_area = get_display_area(); bool zoomed = false; - if (!zoomed) { - SPItem * singleItem = selection->singleItem(); - if (singleItem != NULL && tools_isactive(this, TOOLS_NODES)) { - - Inkscape::NodePath::Path * nodepath = event_context->shape_editor->get_nodepath(); - // printf("I've got a nodepath, crazy\n"); - - if (nodepath) { - Geom::Rect nodes; - bool firstnode = true; - - if (nodepath->selected) { - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (node->selected) { - // printf("\tSelected node\n"); - if (firstnode) { - nodes = Geom::Rect(node->pos, node->pos); - firstnode = false; - } else { - nodes.expandTo(node->pos); - } - - if (node->p.other != NULL) { - /* Include previous node pos */ - nodes.expandTo(node->p.other->pos); - - /* Include previous handle */ - if (!sp_node_side_is_line(node, &node->p)) { - nodes.expandTo(node->p.pos); - } - } - - if (node->n.other != NULL) { - /* Include previous node pos */ - nodes.expandTo(node->n.other->pos); - - /* Include previous handle */ - if (!sp_node_side_is_line(node, &node->n)) { - nodes.expandTo(node->n.pos); - } - } - } - } - } - - if (!firstnode && nodes.area() * 2.0 < _quick_zoom_stored_area.area()) { - set_display_area(nodes, 10); - zoomed = true; - } - } - } + // TODO This needs to migrate into the node tool, but currently the design + // of this method is sufficiently wrong to prevent this. + if (!zoomed && INK_IS_NODE_TOOL(event_context)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(event_context); + if (!nt->_selected_nodes->empty()) { + Geom::Rect nodes = *nt->_selected_nodes->bounds(); + double area = nodes.area(); + // do not zoom if a single cusp node is selected aand the bounds + // have zero area. + if (!Geom::are_near(area, 0) && area * 2.0 < _quick_zoom_stored_area.area()) { + set_display_area(nodes, true); + zoomed = true; + } } } if (!zoomed) { Geom::OptRect const d = selection->bounds(); if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) { - set_display_area(*d, 10); + set_display_area(*d, true); zoomed = true; } } @@ -963,7 +927,7 @@ SPDesktop::zoom_quick (bool enable) zoomed = true; } } else { - set_display_area(_quick_zoom_stored_area, 0); + set_display_area(_quick_zoom_stored_area, false); } _quick_zoom_enabled = enable; @@ -1671,7 +1635,7 @@ static void _reconstruction_start (SPDesktop * desktop) { // printf("Desktop, starting reconstruction\n"); - desktop->_reconstruction_old_layer_id = g_strdup(SP_OBJECT_ID(desktop->currentLayer())); + desktop->_reconstruction_old_layer_id = g_strdup(desktop->currentLayer()->getId()); desktop->_layer_hierarchy->setBottom(desktop->currentRoot()); /* @@ -1804,7 +1768,7 @@ Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const } -/** +/* * Pop event context from desktop's context stack. Never used. */ // void @@ -1846,4 +1810,4 @@ Geom::Point SPDesktop::dt2doc(Geom::Point const &p) const fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/desktop.h b/src/desktop.h index a02a31034..00f6cfdd5 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -38,10 +38,7 @@ #include "display/rendermode.h" #include "display/snap-indicator.h" -class NRRect; class SPCSSAttr; -struct _GtkWidget; -typedef struct _GtkWidget GtkWidget; struct SPCanvas; struct SPCanvasItem; struct SPCanvasGroup; @@ -143,6 +140,11 @@ struct SPDesktop : public Inkscape::UI::View::View sigc::signal<void, SPObject *> _layer_changed_signal; sigc::signal<bool, const SPCSSAttr *>::accumulated<StopOnTrue> _set_style_signal; sigc::signal<int, SPStyle *, int>::accumulated<StopOnNonZero> _query_style_signal; + + /// Emitted when the zoom factor changes (not emitted when scrolling). + /// The parameter is the new zoom factor + sigc::signal<void, double> signal_zoom_changed; + sigc::connection connectDocumentReplaced (const sigc::slot<void,SPDesktop*,SPDocument*> & slot) { return _document_replaced_signal.connect (slot); diff --git a/src/dialogs/export.cpp b/src/dialogs/export.cpp index 2c04135bc..9e8a71c35 100644 --- a/src/dialogs/export.cpp +++ b/src/dialogs/export.cpp @@ -62,7 +62,7 @@ #ifdef WIN32 #include <windows.h> -#include <COMMDLG.h> +#include <commdlg.h> #include <gdk/gdkwin32.h> #endif @@ -1111,7 +1111,7 @@ sp_export_export_clicked (GtkButton */*button*/, GtkObject *base) const gchar *filename = SP_OBJECT_REPR(item)->attribute("inkscape:export-filename"); gchar *path = 0; if (!filename) { - path = create_filepath_from_id (SP_OBJECT_ID(item), NULL); + path = create_filepath_from_id(item->getId(), NULL); } else { path = absolutize_path_from_document_location(doc, filename); } @@ -1356,51 +1356,51 @@ sp_export_browse_clicked (GtkButton */*button*/, gpointer /*userdata*/) gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (fs), filename); #ifdef WIN32 - // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp - OPENFILENAMEW opf; - WCHAR filter_string[20]; - wcsncpy(filter_string, L"PNG#*.png##", 11); - filter_string[3] = L'\0'; - filter_string[9] = L'\0'; - filter_string[10] = L'\0'; - WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL); - WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL); - // Copy the selected file name, converting from UTF-8 to UTF-16 - WCHAR _filename[_MAX_PATH + 1]; + // code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp + OPENFILENAMEW opf; + WCHAR filter_string[20]; + wcsncpy(filter_string, L"PNG#*.png##", 11); + filter_string[3] = L'\0'; + filter_string[9] = L'\0'; + filter_string[10] = L'\0'; + WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL); + WCHAR* extension_string = (WCHAR*)g_utf8_to_utf16("*.png", -1, NULL, NULL, NULL); + // Copy the selected file name, converting from UTF-8 to UTF-16 + WCHAR _filename[_MAX_PATH + 1]; memset(_filename, 0, sizeof(_filename)); gunichar2* utf16_path_string = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL); wcsncpy(_filename, (wchar_t*)utf16_path_string, _MAX_PATH); g_free(utf16_path_string); - opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window)); - opf.lpstrFilter = filter_string; - opf.lpstrCustomFilter = 0; - opf.nMaxCustFilter = 0L; - opf.nFilterIndex = 1L; - opf.lpstrFile = _filename; - opf.nMaxFile = _MAX_PATH; - opf.lpstrFileTitle = NULL; - opf.nMaxFileTitle=0; - opf.lpstrInitialDir = 0; - opf.lpstrTitle = title_string; - opf.nFileOffset = 0; - opf.nFileExtension = 2; - opf.lpstrDefExt = extension_string; - opf.lpfnHook = NULL; - opf.lCustData = 0; - opf.Flags = OFN_PATHMUSTEXIST; - opf.lStructSize = sizeof(OPENFILENAMEW); - if (GetSaveFileNameW(&opf) != 0) - { - // Copy the selected file name, converting from UTF-16 to UTF-8 - gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL); - gtk_entry_set_text (GTK_ENTRY (fe), utf8string); + opf.hwndOwner = (HWND)(GDK_WINDOW_HWND(GTK_WIDGET(dlg)->window)); + opf.lpstrFilter = filter_string; + opf.lpstrCustomFilter = 0; + opf.nMaxCustFilter = 0L; + opf.nFilterIndex = 1L; + opf.lpstrFile = _filename; + opf.nMaxFile = _MAX_PATH; + opf.lpstrFileTitle = NULL; + opf.nMaxFileTitle=0; + opf.lpstrInitialDir = 0; + opf.lpstrTitle = title_string; + opf.nFileOffset = 0; + opf.nFileExtension = 2; + opf.lpstrDefExt = extension_string; + opf.lpfnHook = NULL; + opf.lCustData = 0; + opf.Flags = OFN_PATHMUSTEXIST; + opf.lStructSize = sizeof(OPENFILENAMEW); + if (GetSaveFileNameW(&opf) != 0) + { + // Copy the selected file name, converting from UTF-16 to UTF-8 + gchar *utf8string = g_utf16_to_utf8((const gunichar2*)opf.lpstrFile, _MAX_PATH, NULL, NULL, NULL); + gtk_entry_set_text (GTK_ENTRY (fe), utf8string); g_object_set_data (G_OBJECT (dlg), "filename", fe); - g_free(utf8string); + g_free(utf8string); - } - g_free(extension_string); - g_free(title_string); + } + g_free(extension_string); + g_free(title_string); #else if (gtk_dialog_run (GTK_DIALOG (fs)) == GTK_RESPONSE_ACCEPT) diff --git a/src/dialogs/input.cpp b/src/dialogs/input.cpp index 0b4587f55..55e9704c3 100644 --- a/src/dialogs/input.cpp +++ b/src/dialogs/input.cpp @@ -17,6 +17,8 @@ #include <gtk/gtksignal.h> #include <gtk/gtkinputdialog.h> #include <glibmm/ustring.h> +#include <list> +#include <set> #include "macros.h" #include "verbs.h" @@ -36,6 +38,74 @@ static win_data wd; static gint x = -1000, y = -1000, w = 0, h = 0; static Glib::ustring const prefs_path = "/dialogs/input/"; +#define noTEST_WITH_GOOD_TABLET 1 +#define noTEST_WITH_BAD_TABLET 1 + +#if defined(TEST_WITH_GOOD_TABLET) || defined(TEST_WITH_BAD_TABLET) +static int testDeviceCount = 0; +static GdkDevice* testDevices = 0; + +// Defined at the end of the file to keep debugging out of the way. +static void initTestDevices(); +#endif + +static std::list<GdkDevice *> getInputDevices() +{ + std::list<GdkDevice*> devices; + +#if defined(TEST_WITH_GOOD_TABLET) || defined(TEST_WITH_BAD_TABLET) + initTestDevices(); + for (int i = 0; i < testDeviceCount; i++) { + devices.push_back(&testDevices[i]); + } +#else + for (GList *ptr = gdk_devices_list(); ptr; ptr = ptr->next) { + GdkDevice *device = static_cast<GdkDevice *>(ptr->data); + devices.push_back(device); + } +#endif + + return devices; +} + +// wrap these GDK calls to be able to intercept for testing. + +static bool setDeviceMode( GdkDevice *device, GdkInputMode mode ) +{ +#if defined(TEST_WITH_GOOD_TABLET) || defined(TEST_WITH_BAD_TABLET) + (void)device; + (void)mode; + bool retVal = true; // Can't let the Gdk call be called with bad data +#else + bool retVal = gdk_device_set_mode(device, mode); +#endif + return retVal; +} + +static void setDeviceAxisUse( GdkDevice *device, guint index, GdkAxisUse use ) +{ +#if defined(TEST_WITH_GOOD_TABLET) && !defined(TEST_WITH_BAD_TABLET) + (void)device; + (void)index; + (void)use; +#else + gdk_device_set_axis_use(device, index, use); +#endif +} + +static void setDeviceKey( GdkDevice* device, guint index, guint keyval, GdkModifierType modifiers ) +{ +#if defined(TEST_WITH_GOOD_TABLET) && !defined(TEST_WITH_BAD_TABLET) + (void)device; + (void)index; + (void)keyval; + (void)modifiers; +#else + gdk_device_set_key(device, index, keyval, modifiers); +#endif +} + + static void sp_input_dialog_destroy (GtkObject */*object*/, gpointer /*data*/) { @@ -63,85 +133,156 @@ sp_input_dialog_delete (GtkObject */*object*/, GdkEvent */*event*/, gpointer /*d } -static const gchar *axis_use_strings[GDK_AXIS_LAST] = { +static gchar const *axis_use_strings[GDK_AXIS_LAST] = { "ignore", "x", "y", "pressure", "xtilt", "ytilt", "wheel" }; -void -sp_input_load_from_preferences (void) +static const int RUNAWAY_MAX = 1000; + +static Glib::ustring getBaseDeviceName(GdkInputSource source) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Glib::ustring name; + switch (source) { + case GDK_SOURCE_MOUSE: + name ="pointer"; + break; + case GDK_SOURCE_PEN: + name ="pen"; + break; + case GDK_SOURCE_ERASER: + name ="eraser"; + break; + case GDK_SOURCE_CURSOR: + name ="cursor"; + break; + default: + name = "tablet"; + } + return name; +} - for (GList *list_ptr = gdk_devices_list(); list_ptr != NULL; list_ptr = list_ptr->next) { - GdkDevice *device = static_cast<GdkDevice *>(list_ptr->data); - //repr = sp_repr_lookup_child(devices, "id", device->name); - Glib::ustring device_path = Glib::ustring("/devices/") + device->name; - if (/*repr != NULL*/ 1) { - GdkInputMode mode; - Glib::ustring device_mode = prefs->getString(device_path + "/mode"); - - if (device_mode.empty()) - mode = GDK_MODE_DISABLED; - else if (device_mode == "screen") - mode = GDK_MODE_SCREEN; - else if (device_mode == "window") - mode = GDK_MODE_WINDOW; - else - mode = GDK_MODE_DISABLED; - - if (device->mode != mode) { - gdk_device_set_mode(device, mode); +static Glib::ustring createSanitizedPath(GdkDevice* device, std::set<Glib::ustring> &seenPaths) +{ + // LP #334800: tablet device names on Windows sometimes contain funny junk like + // \x03, \xf2, etc. Moreover this junk changes between runs. + // If the tablet name contains unprintable or non-ASCII characters, + // we use some default name. + // This might break if someone has two tablets with broken names, but it's + // not possible to do anything 100% correct then. + bool broken = false; + + if (!device->name || (*(device->name) == 0)) { + broken = true; + } else { + for (gchar const *s = device->name; *s; ++s) { + if ((*s < 0x20) || (*s >= 0x7f)) { + broken = true; + break; } + } + } + + Glib::ustring device_path; + if (broken) { + Glib::ustring base = Glib::ustring("/devices/") + getBaseDeviceName(device->source); + int num = 1; + device_path = base; + while ((seenPaths.find(device_path) != seenPaths.end()) && (num < RUNAWAY_MAX)) { + device_path = Glib::ustring::compose("%1%2", base, ++num); + } + } else { + device_path += Glib::ustring("/devices/") + device->name; + } + + seenPaths.insert(device_path); + + return device_path; +} + +void sp_input_load_from_preferences(void) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + std::list<GdkDevice *> devices = getInputDevices(); + std::set<Glib::ustring> seenPaths; + for (std::list<GdkDevice *>::iterator it = devices.begin(); it != devices.end(); ++it) { + GdkDevice *device = *it; + +// g_message(" s:%d m:%d hc:%d a:%d k:%d [%s]", device->source, device->mode, device->has_cursor, device->num_axes, device->num_keys, device->name); +// for (int i = 0; i < device->num_axes; i++) { +// GdkDeviceAxis &axis = device->axes[i]; +// g_message(" axis[%d] u:%d min:%f max:%f", i, axis.use, axis.min, axis.max); +// } + + Glib::ustring device_path = createSanitizedPath(device, seenPaths); +// if (device_path != (Glib::ustring("/devices/") + device->name)) { +// g_message(" re-name [%s]", device_path.c_str()); +// } + + Glib::ustring device_mode = prefs->getString(device_path + "/mode"); + + GdkInputMode mode = GDK_MODE_DISABLED; + if (device_mode == "screen") { + mode = GDK_MODE_SCREEN; + } else if (device_mode == "window") { + mode = GDK_MODE_WINDOW; + } - Glib::ustring::size_type pos0, pos1; - GdkAxisUse axis_use; - - //temp_ptr = repr->attribute("axes"); - Glib::ustring const axes_str = prefs->getString(device_path + "/axes"); - pos0 = pos1 = 0; - for (gint i=0; i < device->num_axes; i++) { - pos1 = axes_str.find(';', pos0); - if (pos1 == Glib::ustring::npos) - break; // Too few axis specifications - - axis_use = GDK_AXIS_IGNORE; - for (gint j=0; j < GDK_AXIS_LAST; j++) - if (!strcmp(axes_str.substr(pos0, pos1-pos0).c_str(), axis_use_strings[j])) { - axis_use = static_cast<GdkAxisUse>(j); - break; - } - gdk_device_set_axis_use(device, i, axis_use); - pos0 = pos1 + 1; + if (device->mode != mode) { + setDeviceMode(device, mode); + } + + Glib::ustring::size_type pos0, pos1; + GdkAxisUse axis_use; + + //temp_ptr = repr->attribute("axes"); + Glib::ustring const axes_str = prefs->getString(device_path + "/axes"); + pos0 = pos1 = 0; + for (gint i=0; i < device->num_axes; i++) { + pos1 = axes_str.find(';', pos0); + if (pos1 == Glib::ustring::npos) { + break; // Too few axis specifications } - guint keyval; - GdkModifierType modifier; + axis_use = GDK_AXIS_IGNORE; + for (gint j=0; j < GDK_AXIS_LAST; j++) { + if (!strcmp(axes_str.substr(pos0, pos1-pos0).c_str(), axis_use_strings[j])) { + axis_use = static_cast<GdkAxisUse>(j); + break; + } + } + setDeviceAxisUse(device, i, axis_use); + pos0 = pos1 + 1; + } - Glib::ustring const keys_str = prefs->getString(device_path + "/keys"); - pos0 = pos1 = 0; - for (gint i=0; i < device->num_keys; i++) { - pos1 = keys_str.find(';', pos0); - if (pos1 == Glib::ustring::npos) - break; // Too few key specifications + guint keyval; + GdkModifierType modifier; - gtk_accelerator_parse(keys_str.substr(pos0, pos1-pos0).c_str(), &keyval, &modifier); - gdk_device_set_key(device, i, keyval, modifier); - pos0 = pos1 + 1; + Glib::ustring const keys_str = prefs->getString(device_path + "/keys"); + pos0 = pos1 = 0; + for (gint i=0; i < device->num_keys; i++) { + pos1 = keys_str.find(';', pos0); + if (pos1 == Glib::ustring::npos) { + break; // Too few key specifications } + + gtk_accelerator_parse(keys_str.substr(pos0, pos1-pos0).c_str(), &keyval, &modifier); + setDeviceKey(device, i, keyval, modifier); + pos0 = pos1 + 1; } } } -void -sp_input_save_to_preferences (void) +void sp_input_save_to_preferences(void) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - for (GList *list_ptr = gdk_devices_list(); list_ptr != NULL; list_ptr = list_ptr->next) { - GdkDevice *device = static_cast<GdkDevice *>(list_ptr->data); + std::list<GdkDevice *> devices = getInputDevices(); + std::set<Glib::ustring> seenPaths; + for (std::list<GdkDevice *>::iterator it = devices.begin(); it != devices.end(); ++it) { + GdkDevice *device = *it; - //repr = sp_repr_lookup_child(devices, "id", device->name); - Glib::ustring device_path = Glib::ustring("/devices/") + device->name; + Glib::ustring device_path = createSanitizedPath(device, seenPaths); switch (device->mode) { default: @@ -240,6 +381,219 @@ sp_input_dialog (void) gtk_window_present ((GtkWindow *) dlg); } +// ///////////////////////////////// +// For debugging: +// ///////////////////////////////// + + +#if defined(TEST_WITH_GOOD_TABLET) +static void initTestDevices() +{ + static bool init = false; + if (!init) { + static GdkDevice devs[5] = {}; + int i = 0; // use variable instead of constant to allow devices to be moved around, commented out, etc. + + { + // Laptop trackpad + devs[i].name = g_strdup("pointer"); + devs[i].source = GDK_SOURCE_MOUSE; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Tablet stylus + devs[i].name = g_strdup("pen"); + devs[i].source = GDK_SOURCE_PEN; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}, + {GDK_AXIS_PRESSURE, 0, 1}, + {GDK_AXIS_XTILT, -1, 1}, + {GDK_AXIS_YTILT, -1, 1}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Puck + devs[i].name = g_strdup("cursor"); + devs[i].source = GDK_SOURCE_CURSOR; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}, + {GDK_AXIS_PRESSURE, 0, 1}, + {GDK_AXIS_XTILT, -1, 1}, + {GDK_AXIS_YTILT, -1, 1}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Back of tablet stylus + devs[i].name = g_strdup("eraser"); + devs[i].source = GDK_SOURCE_ERASER; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}, + {GDK_AXIS_PRESSURE, 0, 1}, + {GDK_AXIS_XTILT, -1, 1}, + {GDK_AXIS_YTILT, -1, 1}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Main (composit) mouse device + devs[i].name = g_strdup("Core Pointer"); + devs[i].source = GDK_SOURCE_MOUSE; + devs[i].mode = GDK_MODE_SCREEN; + devs[i].has_cursor = 1; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + testDeviceCount = i; + testDevices = devs; + init = true; + } +} +#elif defined(TEST_WITH_BAD_TABLET) + +/** + * Uses the current time in seconds to change a name to be unique from one + * run of the program to the next. + */ +void perturbName(gchar *str) +{ + if (str) { + GTimeVal when = {0,0}; + g_get_current_time(&when); + gchar *tmp = g_strdup_printf("%ld", when.tv_sec); + + size_t partLen = strlen(tmp); + size_t len = strlen(str); + if (len > (partLen + 4)) { + size_t pos = (len - partLen) / 2; + for (size_t i = 0; i < partLen; i++) { + str[pos + i] = tmp[i]; + } + } + g_free(tmp); + } +} + +static void initTestDevices() +{ + static bool init = false; + if (!init) { + static GdkDevice devs[5] = {}; + int i = 0; // use variable instead of constant to allow devices to be moved around, commented out, etc. + + { + // Main (composit) mouse device + devs[i].name = g_strdup("Core Pointer"); + devs[i].source = GDK_SOURCE_MOUSE; + devs[i].mode = GDK_MODE_SCREEN; + devs[i].has_cursor = 1; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Back of tablet stylus + devs[i].name = g_strdup("\346\205\227\347\221\254\347\201\257\345\220\240\346\211\241\346\225\254t\303\265\006 \347\211\220\347\215\245\347\225\263\346\225\262\345\214\240\347\245\264\347\225\254s\357\227\230#\354\234\274C\356\232\210\307\255\350\271\214\310\201\350\222\200\310\201\356\202\250\310\200\350\223\260\310\201\356\202\250\310\200"); + perturbName(devs[i].name); + devs[i].source = GDK_SOURCE_ERASER; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}, + {GDK_AXIS_PRESSURE, 0, 1}, + {GDK_AXIS_XTILT, -1, 1}, + {GDK_AXIS_YTILT, -1, 1}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Tablet stylus + devs[i].name = g_strdup("\346\205\227\347\221\254\347\201\257\345\220\240\346\211\241\346\225\254t\303\265\006 \347\211\220\347\215\245\347\225\263\346\225\262\345\214\240\347\245\264\347\225\254s\357\227\230#\354\234\274C\341\221\230\307\255\343\277\214\310\202\343\230\200\310\202\331\270\310\202\343\231\260\310\202\331\270\310\202"); + perturbName(devs[i].name); + devs[i].source = GDK_SOURCE_PEN; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}, + {GDK_AXIS_PRESSURE, 0, 1}, + {GDK_AXIS_XTILT, -1, 1}, + {GDK_AXIS_YTILT, -1, 1}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + { + // Tablet stylus + devs[i].name = g_strdup("\346\205\227\347\221\254\347\201\257\345\220\240\346\211\241\346\225\254t\303\265\006 \347\211\220\347\215\245\347\225\263\346\225\262\345\214\240\347\245\264\347\225\254s\357\227\230#\354\234\274C\341\221\230\307\255\343\277\214\310\202\343\230\200\310\202\331\270\310\202\343\231\260\310\202\331\270\310\202"); + perturbName(devs[i].name); + devs[i].source = GDK_SOURCE_PEN; + devs[i].mode = GDK_MODE_DISABLED; + devs[i].has_cursor = 0; + static GdkDeviceAxis tmp[] = {{GDK_AXIS_X, 0, 0}, + {GDK_AXIS_Y, 0, 0}, + {GDK_AXIS_PRESSURE, 0, 1}, + {GDK_AXIS_XTILT, -1, 1}, + {GDK_AXIS_YTILT, -1, 1}}; + devs[i].num_axes = G_N_ELEMENTS(tmp); + devs[i].axes = tmp; + devs[i].num_keys = 0; + devs[i].keys = 0; + i++; + } + + testDeviceCount = i; + testDevices = devs; + init = true; + } +} +#endif + /* Local Variables: diff --git a/src/dialogs/item-properties.cpp b/src/dialogs/item-properties.cpp index 211b800dc..abc45b44b 100644 --- a/src/dialogs/item-properties.cpp +++ b/src/dialogs/item-properties.cpp @@ -328,7 +328,7 @@ sp_item_widget_setup ( SPWidget *spw, Inkscape::Selection *selection ) /* ID */ w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id")); - gtk_entry_set_text (GTK_ENTRY (w), obj->id); + gtk_entry_set_text (GTK_ENTRY (w), obj->getId()); gtk_widget_set_sensitive (w, TRUE); w = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label")); gtk_label_set_markup_with_mnemonic (GTK_LABEL (w), _("_Id")); @@ -439,7 +439,7 @@ sp_item_widget_label_changed( GtkWidget */*widget*/, SPWidget *spw ) gchar *id = (gchar *) gtk_entry_get_text (GTK_ENTRY (id_entry)); g_strcanon (id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_'); GtkWidget *id_label = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (spw), "id_label")); - if (!strcmp (id, SP_OBJECT_ID(item))) { + if (!strcmp (id, item->getId())) { gtk_label_set_markup_with_mnemonic (GTK_LABEL (id_label), _("_Id")); } else if (!*id || !isalnum (*id)) { gtk_label_set_text (GTK_LABEL (id_label), _("Id invalid! ")); diff --git a/src/display/canvas-text.cpp b/src/display/canvas-text.cpp index 1c715291f..d32bc20c3 100644 --- a/src/display/canvas-text.cpp +++ b/src/display/canvas-text.cpp @@ -91,7 +91,9 @@ sp_canvastext_destroy (GtkObject *object) SPCanvasText *canvastext = SP_CANVASTEXT (object); - canvastext->item=NULL; + g_free(canvastext->text); + canvastext->text = NULL; + canvastext->item = NULL; if (GTK_OBJECT_CLASS (parent_class_ct)->destroy) (* GTK_OBJECT_CLASS (parent_class_ct)->destroy) (object); diff --git a/src/display/curve.cpp b/src/display/curve.cpp index 7d7dbc987..1b54c981c 100644 --- a/src/display/curve.cpp +++ b/src/display/curve.cpp @@ -51,10 +51,10 @@ SPCurve::new_from_rect(Geom::Rect const &rect) Geom::Point p = rect.corner(0); c->moveto(p); - for (int i=3; i>=0; i--) { + for (int i=3; i>=1; i--) { c->lineto(rect.corner(i)); } - c->closepath_current(); + c->closepath(); return c; } @@ -285,7 +285,11 @@ SPCurve::closepath() void SPCurve::closepath_current() { - _pathv.back().setFinal(_pathv.back().initialPoint()); + if (_pathv.back().size() > 0 && dynamic_cast<Geom::LineSegment const *>(&_pathv.back().back_open())) { + _pathv.back().erase_last(); + } else { + _pathv.back().setFinal(_pathv.back().initialPoint()); + } _pathv.back().close(true); } diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index fdea9cbbf..1e4ca12a8 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -6,7 +6,7 @@ * Diederik van Lierop * * Copyright (C) Johan Engelen 2009 <j.b.c.engelen@utwente.nl> - * Copyright (C) Diederik van Lierop 2009 <mail@diedenrezi.nl> + * Copyright (C) Diederik van Lierop 2010 <mail@diedenrezi.nl> * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -16,7 +16,9 @@ #include "desktop.h" #include "desktop-handles.h" #include "display/sodipodi-ctrl.h" +#include "display/sodipodi-ctrlrect.h" #include "display/canvas-text.h" +#include "display/sp-canvas-util.h" #include "knot.h" #include "preferences.h" #include <glibmm/i18n.h> @@ -27,7 +29,9 @@ namespace Display { SnapIndicator::SnapIndicator(SPDesktop * desktop) : _snaptarget(NULL), _snaptarget_tooltip(NULL), + _snaptarget_bbox(NULL), _snapsource(NULL), + _snaptarget_is_presnap(false), _desktop(desktop) { } @@ -40,7 +44,7 @@ SnapIndicator::~SnapIndicator() } void -SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) +SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap) { remove_snaptarget(); //only display one snaptarget at a time @@ -97,9 +101,6 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) case SNAPTARGET_BBOX_EDGE: target_name = _("bounding box side"); break; - case SNAPTARGET_GRADIENTS_PARENT_BBOX: - target_name = _("bounding box"); - break; case SNAPTARGET_PAGE_BORDER: target_name = _("page border"); break; @@ -139,6 +140,12 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) case SNAPTARGET_TEXT_BASELINE: target_name = _("text baseline"); break; + case SNAPTARGET_CONSTRAINED_ANGLE: + target_name = _("constrained angle"); + break; + case SNAPTARGET_CONSTRAINT: + target_name = _("constraint"); + break; default: g_warning("Snap target has not yet been defined!"); break; @@ -173,7 +180,8 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) case SNAPSOURCE_ROTATION_CENTER: source_name = _("Object rotation center"); break; - case SNAPSOURCE_HANDLE: + case SNAPSOURCE_NODE_HANDLE: + case SNAPSOURCE_OTHER_HANDLE: source_name = _("Handle"); break; case SNAPSOURCE_PATH_INTERSECTION: @@ -200,6 +208,9 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) case SNAPSOURCE_TEXT_BASELINE: source_name = _("Text baseline"); break; + case SNAPSOURCE_GRID_PITCH: + source_name = _("Multiple of grid spacing"); + break; default: g_warning("Snap source has not yet been defined!"); break; @@ -216,7 +227,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) "anchor", GTK_ANCHOR_CENTER, "size", 10.0, "stroked", TRUE, - "stroke_color", 0xf000f0ff, + "stroke_color", pre_snap ? 0x7f7f7fff : 0xff0000ff, "mode", SP_KNOT_MODE_XOR, "shape", SP_KNOT_SHAPE_DIAMOND, NULL ); @@ -226,7 +237,7 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) "anchor", GTK_ANCHOR_CENTER, "size", 10.0, "stroked", TRUE, - "stroke_color", 0xf000f0ff, + "stroke_color", pre_snap ? 0x7f7f7fff : 0xff0000ff, "mode", SP_KNOT_MODE_XOR, "shape", SP_KNOT_SHAPE_CROSS, NULL ); @@ -236,24 +247,53 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) SP_CTRL(canvasitem)->moveto(p.getPoint()); _snaptarget = _desktop->add_temporary_canvasitem(canvasitem, timeout_val); + _snaptarget_is_presnap = pre_snap; - gchar *tooltip_str = g_strconcat(source_name, _(" to "), target_name, NULL); + // Display the tooltip, which reveals the type of snap source and the type of snap target + gchar *tooltip_str = NULL; + if (p.getSource() != SNAPSOURCE_GRID_PITCH) { + tooltip_str = g_strconcat(source_name, _(" to "), target_name, NULL); + } else { + tooltip_str = g_strdup(source_name); + } Geom::Point tooltip_pos = p.getPoint() + _desktop->w2d(Geom::Point(15, -15)); SPCanvasItem *canvas_tooltip = sp_canvastext_new(sp_desktop_tempgroup(_desktop), _desktop, tooltip_pos, tooltip_str); + if (pre_snap) { + SP_CANVASTEXT(canvas_tooltip)->rgba = 0x7f7f7fff; + } g_free(tooltip_str); sp_canvastext_set_anchor((SPCanvasText* )canvas_tooltip, -1, 1); _snaptarget_tooltip = _desktop->add_temporary_canvasitem(canvas_tooltip, timeout_val); + + // Display the bounding box, if we snapped to one + Geom::OptRect const bbox = p.getTargetBBox(); + if (bbox) { + SPCanvasItem* box = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), + SP_TYPE_CTRLRECT, + NULL); + + SP_CTRLRECT(box)->setRectangle(*bbox); + SP_CTRLRECT(box)->setColor(pre_snap ? 0x7f7f7fff : 0xff0000ff, 0, 0); + SP_CTRLRECT(box)->setDashed(true); + sp_canvas_item_move_to_z(box, 0); + _snaptarget_bbox = _desktop->add_temporary_canvasitem(box, timeout_val); + } } } void -SnapIndicator::remove_snaptarget() +SnapIndicator::remove_snaptarget(bool only_if_presnap) { + if (only_if_presnap && !_snaptarget_is_presnap) { + return; + } + if (_snaptarget) { _desktop->remove_temporary_canvasitem(_snaptarget); _snaptarget = NULL; + _snaptarget_is_presnap = false; } if (_snaptarget_tooltip) { @@ -261,10 +301,15 @@ SnapIndicator::remove_snaptarget() _snaptarget_tooltip = NULL; } + if (_snaptarget_bbox) { + _desktop->remove_temporary_canvasitem(_snaptarget_bbox); + _snaptarget_bbox = NULL; + } + } void -SnapIndicator::set_new_snapsource(std::pair<Geom::Point, int> const p) +SnapIndicator::set_new_snapsource(Inkscape::SnapCandidatePoint const &p) { remove_snapsource(); @@ -279,12 +324,12 @@ SnapIndicator::set_new_snapsource(std::pair<Geom::Point, int> const p) "anchor", GTK_ANCHOR_CENTER, "size", 6.0, "stroked", TRUE, - "stroke_color", 0xf000f0ff, + "stroke_color", 0xff0000ff, "mode", SP_KNOT_MODE_XOR, "shape", SP_KNOT_SHAPE_CIRCLE, NULL ); - SP_CTRL(canvasitem)->moveto(p.first); + SP_CTRL(canvasitem)->moveto(p.getPoint()); _snapsource = _desktop->add_temporary_canvasitem(canvasitem, 1000); } } diff --git a/src/display/snap-indicator.h b/src/display/snap-indicator.h index 4391ca6d6..5475f9f60 100644 --- a/src/display/snap-indicator.h +++ b/src/display/snap-indicator.h @@ -9,7 +9,7 @@ * Diederik van Lierop * * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl> - * Copyright (C) Diederik van Lierop 2008 <mail@diedenrezi.nl> + * Copyright (C) Diederik van Lierop 2010 <mail@diedenrezi.nl> * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -26,16 +26,18 @@ public: SnapIndicator(SPDesktop *desktop); virtual ~SnapIndicator(); - void set_new_snaptarget(Inkscape::SnappedPoint const p); - void remove_snaptarget(); + void set_new_snaptarget(Inkscape::SnappedPoint const &p, bool pre_snap = false); + void remove_snaptarget(bool only_if_presnap = false); - void set_new_snapsource(std::pair<Geom::Point, int> const p); + void set_new_snapsource(Inkscape::SnapCandidatePoint const &p); void remove_snapsource(); protected: TemporaryItem *_snaptarget; TemporaryItem *_snaptarget_tooltip; + TemporaryItem *_snaptarget_bbox; TemporaryItem *_snapsource; + bool _snaptarget_is_presnap; SPDesktop *_desktop; private: diff --git a/src/display/sp-canvas-util.cpp b/src/display/sp-canvas-util.cpp index 30cd0dfa0..a23b157df 100644 --- a/src/display/sp-canvas-util.cpp +++ b/src/display/sp-canvas-util.cpp @@ -112,10 +112,11 @@ void sp_canvas_item_move_to_z (SPCanvasItem * item, gint z) if (z == current_z) return; - if (z > current_z) + if (z > current_z) { sp_canvas_item_raise (item, z - current_z); - - sp_canvas_item_lower (item, current_z - z); + } else { + sp_canvas_item_lower (item, current_z - z); + } } gint diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index e640911d6..e39fef45b 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -103,6 +103,7 @@ static void group_remove (SPCanvasGroup *group, SPCanvasItem *item); /* SPCanvasItem */ enum {ITEM_EVENT, ITEM_LAST_SIGNAL}; +enum {PROP_0, PROP_VISIBLE}; static void sp_canvas_request_update (SPCanvas *canvas); @@ -114,11 +115,10 @@ static void sp_canvas_item_dispose (GObject *object); static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args); static int emit_event (SPCanvas *canvas, GdkEvent *event); +static int pick_current_item (SPCanvas *canvas, GdkEvent *event); static guint item_signals[ITEM_LAST_SIGNAL] = { 0 }; -static GtkObjectClass *item_parent_class; - /** * Registers the SPCanvasItem class with Glib and returns its type number. */ @@ -151,9 +151,6 @@ sp_canvas_item_class_init (SPCanvasItemClass *klass) { GObjectClass *object_class = (GObjectClass *) klass; - /* fixme: Derive from GObject */ - item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT); - item_signals[ITEM_EVENT] = g_signal_new ("event", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, @@ -172,6 +169,9 @@ sp_canvas_item_class_init (SPCanvasItemClass *klass) static void sp_canvas_item_init (SPCanvasItem *item) { + // TODO items should not be visible on creation - this causes kludges with items + // that should be initially invisible; examples of such items: node handles, the CtrlRect + // used for rubberbanding, path outline, etc. item->flags |= SP_CANVAS_ITEM_VISIBLE; item->xform = Geom::Matrix(Geom::identity()); } @@ -277,7 +277,7 @@ sp_canvas_item_dispose (GObject *object) group_remove (SP_CANVAS_GROUP (item->parent), item); } - G_OBJECT_CLASS (item_parent_class)->dispose (object); + G_OBJECT_CLASS (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->dispose (object); } /** @@ -477,6 +477,13 @@ sp_canvas_item_lower (SPCanvasItem *item, int positions) item->canvas->need_repick = TRUE; } +bool +sp_canvas_item_is_visible (SPCanvasItem *item) +{ + return item->flags & SP_CANVAS_ITEM_VISIBLE; +} + + /** * Sets visible flag on item and requests a redraw. */ @@ -542,8 +549,13 @@ sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, gu if (item->canvas->grabbed_item) return -1; - if (!(item->flags & SP_CANVAS_ITEM_VISIBLE)) - return -1; + // This test disallows grabbing events by an invisible item, which may be useful + // sometimes. An example is the hidden control point used for the selector component, + // where it is used for object selection and rubberbanding. There seems to be nothing + // preventing this except this test, so I removed it. + // -- Krzysztof Kosiński, 2009.08.12 + //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE)) + // return -1; if (HAS_BROKEN_MOTION_HINTS) { event_mask &= ~GDK_POINTER_MOTION_HINT_MASK; @@ -1316,6 +1328,20 @@ emit_event (SPCanvas *canvas, GdkEvent *event) if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) { item = canvas->grabbed_item; } else { + // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then + // sp_canvas_item_dispose has been called and there is no current_item specified. We need + // that though because otherwise we don't know where to send this event to, leading to a + // lost event. We can't wait for idle events to have current_item updated, we need it now! + // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example) + // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8 + if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) { + // To avoid side effects, we'll only do this for scroll events, because this is the + // only thing we want to fix here. An example of a reported side effect is that + // otherwise selection of nodes in the node editor by dragging a rectangle using a + // tablet will break + canvas->need_repick = FALSE; + pick_current_item (canvas, (GdkEvent *) event); + } item = canvas->current_item; } @@ -1481,6 +1507,8 @@ pick_current_item (SPCanvas *canvas, GdkEvent *event) retval = emit_event (canvas, &new_event); } + + return retval; } @@ -1578,7 +1606,7 @@ static inline void request_motions(GdkWindow *w, GdkEventMotion *event) { static int sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) { - int status; + int status; SPCanvas *canvas = SP_CANVAS (widget); track_latency((GdkEvent *)event); @@ -1590,11 +1618,11 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event) return FALSE; canvas->state = event->state; - pick_current_item (canvas, (GdkEvent *) event); - status = emit_event (canvas, (GdkEvent *) event); - if (event->is_hint) { - request_motions(widget->window, event); - } + pick_current_item (canvas, (GdkEvent *) event); + status = emit_event (canvas, (GdkEvent *) event); + if (event->is_hint) { + request_motions(widget->window, event); + } return status; } @@ -2085,7 +2113,7 @@ paint (SPCanvas *canvas) static int do_update (SPCanvas *canvas) { - if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display! + if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display! return TRUE; if (canvas->drawing_disabled) @@ -2189,6 +2217,7 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear, } else { // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw } + } /** diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index a17e2e086..5cd201618 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -101,6 +101,7 @@ void sp_canvas_item_affine_absolute(SPCanvasItem *item, Geom::Matrix const &aff) void sp_canvas_item_raise(SPCanvasItem *item, int positions); void sp_canvas_item_lower(SPCanvasItem *item, int positions); +bool sp_canvas_item_is_visible(SPCanvasItem *item); void sp_canvas_item_show(SPCanvasItem *item); void sp_canvas_item_hide(SPCanvasItem *item); int sp_canvas_item_grab(SPCanvasItem *item, unsigned int event_mask, GdkCursor *cursor, guint32 etime); diff --git a/src/document-subset.cpp b/src/document-subset.cpp index aa4046d86..eb7265b2d 100644 --- a/src/document-subset.cpp +++ b/src/document-subset.cpp @@ -184,9 +184,6 @@ private: void _doRemove(SPObject *obj) { Record &record=records[obj]; - record.release_connection.disconnect(); - record.position_changed_connection.disconnect(); - records.erase(obj); if ( record.parent == NULL ) { Record &root = records[NULL]; @@ -198,6 +195,9 @@ private: } } + record.release_connection.disconnect(); + record.position_changed_connection.disconnect(); + records.erase(obj); removed_signal.emit(obj); sp_object_unref(obj); } diff --git a/src/document.cpp b/src/document.cpp index 3104ade28..101c54e30 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -615,26 +615,101 @@ Geom::Point sp_document_dimensions(SPDocument *doc) } /** + * Gets page fitting margin information from the namedview node in the XML. + * \param nv_repr reference to this document's namedview + * \param key the same key used by the RegisteredScalarUnit in + * ui/widget/page-sizer.cpp + * \param margin_units units for the margin + * \param return_units units to return the result in + * \param width width in px (for percentage margins) + * \param height height in px (for percentage margins) + * \param use_width true if the this key is left or right margins, false + * otherwise. Used for percentage margins. + * \return the margin size in px, else 0.0 if anything is invalid. + */ +static double getMarginLength(Inkscape::XML::Node * const nv_repr, + gchar const * const key, + SPUnit const * const margin_units, + SPUnit const * const return_units, + double const width, + double const height, + bool const use_width) +{ + double value; + if (!sp_repr_get_double (nv_repr, key, &value)) { + return 0.0; + } + if (margin_units == &sp_unit_get_by_id (SP_UNIT_PERCENT)) { + return (use_width)? width * value : height * value; + } + if (!sp_convert_distance (&value, margin_units, return_units)) { + return 0.0; + } + return value; +} + +/** * Given a Geom::Rect that may, for example, correspond to the bbox of an object, * this function fits the canvas to that rect by resizing the canvas * and translating the document root into position. + * \param rect fit document size to this + * \param with_margins add margins to rect, by taking margins from this + * document's namedview (<sodipodi:namedview> "fit-margin-..." + * attributes, and "units") */ -void SPDocument::fitToRect(Geom::Rect const &rect) +void SPDocument::fitToRect(Geom::Rect const &rect, bool with_margins) { double const w = rect.width(); double const h = rect.height(); double const old_height = sp_document_height(this); SPUnit const &px(sp_unit_get_by_id(SP_UNIT_PX)); - sp_document_set_width(this, w, &px); - sp_document_set_height(this, h, &px); + + /* in px */ + double margin_top = 0.0; + double margin_left = 0.0; + double margin_right = 0.0; + double margin_bottom = 0.0; + + SPNamedView *nv = sp_document_namedview(this, 0); + + if (with_margins && nv) { + Inkscape::XML::Node *nv_repr = SP_OBJECT_REPR (nv); + if (nv_repr != NULL) { + gchar const * const units_abbr = nv_repr->attribute("units"); + SPUnit const *margin_units = NULL; + if (units_abbr != NULL) { + margin_units = sp_unit_get_by_abbreviation(units_abbr); + } + if (margin_units == NULL) { + margin_units = &sp_unit_get_by_id(SP_UNIT_PX); + } + margin_top = getMarginLength(nv_repr, "fit-margin-top", + margin_units, &px, w, h, false); + margin_left = getMarginLength(nv_repr, "fit-margin-left", + margin_units, &px, w, h, true); + margin_right = getMarginLength(nv_repr, "fit-margin-right", + margin_units, &px, w, h, true); + margin_bottom = getMarginLength(nv_repr, "fit-margin-bottom", + margin_units, &px, w, h, false); + } + } + + Geom::Rect const rect_with_margins( + rect.min() - Geom::Point(margin_left, margin_bottom), + rect.max() + Geom::Point(margin_right, margin_top)); + + + sp_document_set_width(this, rect_with_margins.width(), &px); + sp_document_set_height(this, rect_with_margins.height(), &px); - Geom::Translate const tr(Geom::Point(0, (old_height - h)) - - to_2geom(rect.min())); + Geom::Translate const tr( + Geom::Point(0, old_height - rect_with_margins.height()) + - to_2geom(rect_with_margins.min())); SP_GROUP(root)->translateChildItems(tr); - SPNamedView *nv = sp_document_namedview(this, 0); + if(nv) { - Geom::Translate tr2(-rect.min()); + Geom::Translate tr2(-rect_with_margins.min()); nv->translateGuides(tr2); // update the viewport so the drawing appears to stay where it was diff --git a/src/document.h b/src/document.h index e83c37a96..e70582006 100644 --- a/src/document.h +++ b/src/document.h @@ -179,7 +179,7 @@ public: sigc::connection _selection_changed_connection; sigc::connection _desktop_activated_connection; - void fitToRect(Geom::Rect const &rect); + void fitToRect(Geom::Rect const &rect, bool with_margins = false); }; SPDocument *sp_document_new(const gchar *uri, unsigned int keepalive, bool make_new = false); diff --git a/src/dom/io/httpclient.cpp b/src/dom/io/httpclient.cpp index 4245d71f2..97c8575cf 100644 --- a/src/dom/io/httpclient.cpp +++ b/src/dom/io/httpclient.cpp @@ -73,7 +73,7 @@ bool HttpClient::openGet(const URI &uri) socket.enableSSL(true); else { - printf("Bad proto scheme:%d\n", uri.getScheme()); + //printf("Bad proto scheme:%d\n", uri.getScheme()); return false; } @@ -106,7 +106,7 @@ bool HttpClient::openGet(const URI &uri) { if (!socket.readLine(msg)) return false; - printf("header:'%s'\n", msg.c_str()); + //printf("header:'%s'\n", msg.c_str()); if (msg.size() < 1) break; } diff --git a/src/dom/odf/SvgOdg.cpp b/src/dom/odf/SvgOdg.cpp index 2b1161310..e69de29bb 100644 --- a/src/dom/odf/SvgOdg.cpp +++ b/src/dom/odf/SvgOdg.cpp @@ -1,1551 +0,0 @@ -/** - * - * This is a small experimental class for converting between - * SVG and OpenDocument .odg files. This code is not intended - * to be a permanent solution for SVG-to-ODG conversion. Rather, - * it is a quick-and-easy test bed for ideas which will be later - * recoded into C++. - * - * --------------------------------------------------------------------- - * - * SvgOdg - A program to experiment with conversions between SVG and ODG - * Copyright (C) 2006 Bob Jamison - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * For more information, please write to rwjj@earthlink.net - * - */ - - -/** - * - */ -public class SvgOdg -{ - - - -/** - * Namespace declarations - */ -public static final String SVG_NS = - "http://www.w3.org/2000/svg"; -public static final String XLINK_NS = - "http://www.w3.org/1999/xlink"; -public static final String ODF_NS = - "urn:oasis:names:tc:opendocument:xmlns:office:1.0"; -public static final String ODG_NS = - "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"; -public static final String ODSVG_NS = - "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"; - - -DecimalFormat nrfmt; -//static final double pxToCm = 0.0339; -static final double pxToCm = 0.0275; -static final double piToRad = 0.0174532925; -BufferedWriter out; -BufferedReader in; -int imageNr; -int styleNr; - -//######################################################################## -//# M E S S A G E S -//######################################################################## - -/** - * - */ -void err(String msg) -{ - System.out.println("SvgOdg ERROR:" + msg); -} - -/** - * - */ -void trace(String msg) -{ - System.out.println("SvgOdg:" + msg); -} - - - - -//######################################################################## -//# I N P U T / O U T P U T -//######################################################################## - -boolean po(String s) -{ - try - { - out.write(s); - } - catch(IOException e) - { - return false; - } - return true; -} - -//######################################################################## -//# U T I L I T Y -//######################################################################## - -public void dumpDocument(Document doc) -{ - String s = ""; - try - { - TransformerFactory factory = TransformerFactory.newInstance(); - Transformer trans = factory.newTransformer(); - DOMSource source = new DOMSource(doc); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - StreamResult result = new StreamResult(bos); - trans.transform(source, result); - byte buf[] = bos.toByteArray(); - s = new String(buf); - } - catch (javax.xml.transform.TransformerException e) - { - } - trace("doc:" + s); -} - - -//######################################################################## -//# I N N E R C L A S S ImageInfo -//######################################################################## -public class ImageInfo -{ -String name; -String newName; -byte buf[]; - -public String getName() -{ - return name; -} - -public String getNewName() -{ - return newName; -} - - -public byte[] getBuf() -{ - return buf; -} - -public ImageInfo(String name, String newName, byte buf[]) -{ - this.name = name; - this.name = newName; - this.buf = buf; -} - -} -//######################################################################## -//# I N N E R C L A S S StyleInfo -//######################################################################## -public class StyleInfo -{ - -String name; -public String getName() -{ - return name; -} - -String cssStyle; -public String getCssStyle() -{ - return cssStyle; -} - -String stroke; -public String getStroke() -{ - return stroke; -} - -String strokeColor; -public String getStrokeColor() -{ - return strokeColor; -} - -String strokeWidth; -public String getStrokeWidth() -{ - return strokeWidth; -} - -String fill; -public String getFill() -{ - return fill; -} - -String fillColor; -public String getFillColor() -{ - return fillColor; -} - -public StyleInfo(String name, String cssStyle) -{ - this.name = name; - this.cssStyle = cssStyle; - fill = "none"; - stroke = "none"; -} - -} -//######################################################################## -//# E N D I N N E R C L A S S E S -//######################################################################## - - - - -//######################################################################## -//# V A R I A B L E S -//######################################################################## - -/** - * ODF content.xml file - */ -Document content; -public Document getContent() -{ - return content; -} - -/** - * ODF meta.xml file - */ -Document meta; -public Document getMeta() -{ - return meta; -} - -/** - * SVG file - */ -Document svg; -public Document getSvg() -{ - return svg; -} - -/** - * Loaded ODF or SVG images - */ -ArrayList<ImageInfo> images; -public ArrayList<ImageInfo> getImages() -{ - return images; -} - -/** - * CSS styles - */ -HashMap<String, StyleInfo> styles; -public HashMap<String, StyleInfo> getStyles() -{ - return styles; -} - - - - - -//######################################################################## -//# S V G T O O D F -//######################################################################## - -class PathData -{ -String cmd; -double nr[]; -PathData(String s, double buf[]) -{ - cmd=s; nr = buf; -} -} - -double getPathNum(StringTokenizer st) -{ - if (!st.hasMoreTokens()) - return 0.0; - String s = st.nextToken(); - double nr = Double.parseDouble(s); - return nr; -} - -String parsePathData(String pathData, double bounds[]) -{ - double minx = Double.MAX_VALUE; - double maxx = Double.MIN_VALUE; - double miny = Double.MAX_VALUE; - double maxy = Double.MIN_VALUE; - //trace("#### pathData:" + pathData); - ArrayList<PathData> data = new ArrayList<PathData>(); - StringTokenizer st = new StringTokenizer(pathData, " ,"); - while (true) - { - String s = st.nextToken(); - if ( s.equals("z") || s.equals("Z") ) - { - PathData pd = new PathData(s, new double[0]); - data.add(pd); - break; - } - else if ( s.equals("h") || s.equals("H") ) - { - double d[] = new double[1]; - d[0] = getPathNum(st) * pxToCm; - if (d[0] < minx) minx = d[0]; - else if (d[0] > maxx) maxx = d[0]; - PathData pd = new PathData(s, d); - data.add(pd); - } - else if ( s.equals("v") || s.equals("V") ) - { - double d[] = new double[1]; - d[0] = getPathNum(st) * pxToCm; - if (d[0] < miny) miny = d[0]; - else if (d[0] > maxy) maxy = d[0]; - PathData pd = new PathData(s, d); - data.add(pd); - } - else if ( s.equals("m") || s.equals("M") || - s.equals("l") || s.equals("L") || - s.equals("t") || s.equals("T") ) - { - double d[] = new double[2]; - d[0] = getPathNum(st) * pxToCm; - d[1] = getPathNum(st) * pxToCm; - if (d[0] < minx) minx = d[0]; - else if (d[0] > maxx) maxx = d[0]; - if (d[1] < miny) miny = d[1]; - else if (d[1] > maxy) maxy = d[1]; - PathData pd = new PathData(s, d); - data.add(pd); - } - else if ( s.equals("q") || s.equals("Q") || - s.equals("s") || s.equals("S") ) - { - double d[] = new double[4]; - d[0] = getPathNum(st) * pxToCm; - d[1] = getPathNum(st) * pxToCm; - if (d[0] < minx) minx = d[0]; - else if (d[0] > maxx) maxx = d[0]; - if (d[1] < miny) miny = d[1]; - else if (d[1] > maxy) maxy = d[1]; - d[2] = getPathNum(st) * pxToCm; - d[3] = getPathNum(st) * pxToCm; - if (d[2] < minx) minx = d[2]; - else if (d[2] > maxx) maxx = d[2]; - if (d[3] < miny) miny = d[3]; - else if (d[3] > maxy) maxy = d[3]; - PathData pd = new PathData(s, d); - data.add(pd); - } - else if ( s.equals("c") || s.equals("C") ) - { - double d[] = new double[6]; - d[0] = getPathNum(st) * pxToCm; - d[1] = getPathNum(st) * pxToCm; - if (d[0] < minx) minx = d[0]; - else if (d[0] > maxx) maxx = d[0]; - if (d[1] < miny) miny = d[1]; - else if (d[1] > maxy) maxy = d[1]; - d[2] = getPathNum(st) * pxToCm; - d[3] = getPathNum(st) * pxToCm; - if (d[2] < minx) minx = d[2]; - else if (d[2] > maxx) maxx = d[2]; - if (d[3] < miny) miny = d[3]; - else if (d[3] > maxy) maxy = d[3]; - d[4] = getPathNum(st) * pxToCm; - d[5] = getPathNum(st) * pxToCm; - if (d[4] < minx) minx = d[4]; - else if (d[4] > maxx) maxx = d[4]; - if (d[5] < miny) miny = d[5]; - else if (d[5] > maxy) maxy = d[5]; - PathData pd = new PathData(s, d); - data.add(pd); - } - else if ( s.equals("a") || s.equals("A") ) - { - double d[] = new double[6]; - d[0] = getPathNum(st) * pxToCm; - d[1] = getPathNum(st) * pxToCm; - if (d[0] < minx) minx = d[0]; - else if (d[0] > maxx) maxx = d[0]; - if (d[1] < miny) miny = d[1]; - else if (d[1] > maxy) maxy = d[1]; - d[2] = getPathNum(st) * piToRad;//angle - d[3] = getPathNum(st) * piToRad;//angle - d[4] = getPathNum(st) * pxToCm; - d[5] = getPathNum(st) * pxToCm; - if (d[4] < minx) minx = d[4]; - else if (d[4] > maxx) maxx = d[4]; - if (d[5] < miny) miny = d[5]; - else if (d[5] > maxy) maxy = d[5]; - PathData pd = new PathData(s, d); - data.add(pd); - } - //trace("x:" + x + " y:" + y); - } - - trace("minx:" + minx + " maxx:" + maxx + - " miny:" + miny + " maxy:" + maxy); - - StringBuffer buf = new StringBuffer(); - for (PathData pd : data) - { - buf.append(pd.cmd); - buf.append(" "); - for (double d:pd.nr) - { - buf.append(nrfmt.format(d * 1000.0)); - buf.append(" "); - } - } - - bounds[0] = minx; - bounds[1] = miny; - bounds[2] = maxx; - bounds[3] = maxy; - - return buf.toString(); -} - - - -boolean parseTransform(String transStr, AffineTransform trans) -{ - trace("== transform:"+ transStr); - StringTokenizer st = new StringTokenizer(transStr, ")"); - while (st.hasMoreTokens()) - { - String chunk = st.nextToken(); - StringTokenizer st2 = new StringTokenizer(chunk, " ,("); - if (!st2.hasMoreTokens()) - continue; - String name = st2.nextToken(); - trace(" ++name:"+ name); - if (name.equals("matrix")) - { - double v[] = new double[6]; - for (int i=0 ; i<6 ; i++) - { - if (!st2.hasMoreTokens()) - break; - v[i] = Double.parseDouble(st2.nextToken()) * pxToCm; - } - AffineTransform mat = new AffineTransform(v); - trans.concatenate(mat); - } - else if (name.equals("translate")) - { - double dx = 0.0; - double dy = 0.0; - if (!st2.hasMoreTokens()) - continue; - dx = Double.parseDouble(st2.nextToken()) * pxToCm; - if (st2.hasMoreTokens()) - dy = Double.parseDouble(st2.nextToken()) * pxToCm; - trans.translate(dx, dy); - } - else if (name.equals("scale")) - { - double sx = 1.0; - double sy = 1.0; - if (!st2.hasMoreTokens()) - continue; - sx = sy = Double.parseDouble(st2.nextToken()); - if (st2.hasMoreTokens()) - sy = Double.parseDouble(st2.nextToken()); - trans.scale(sx, sy); - } - else if (name.equals("rotate")) - { - double r = 0.0; - double cx = 0.0; - double cy = 0.0; - if (!st2.hasMoreTokens()) - continue; - r = Double.parseDouble(st2.nextToken()) * piToRad; - if (st2.hasMoreTokens()) - { - cx = Double.parseDouble(st2.nextToken()) * pxToCm; - if (!st2.hasMoreTokens()) - continue; - cy = Double.parseDouble(st2.nextToken()) * pxToCm; - trans.rotate(r, cx, cy); - } - else - { - trans.rotate(r); - } - } - else if (name.equals("skewX")) - { - double angle = 0.0; - if (!st2.hasMoreTokens()) - continue; - angle = Double.parseDouble(st2.nextToken()); - trans.shear(angle, 0.0); - } - else if (name.equals("skewY")) - { - double angle = 0.0; - if (!st2.hasMoreTokens()) - continue; - angle = Double.parseDouble(st2.nextToken()); - trans.shear(0.0, angle); - } - } - return true; -} - - - -String coordToOdg(String sval) -{ - double nr = Double.parseDouble(sval); - nr = nr * pxToCm; - String s = nrfmt.format(nr) + "cm"; - return s; -} - - -boolean writeSvgAttributes(Element elem, AffineTransform trans) -{ - NamedNodeMap attrs = elem.getAttributes(); - String ename = elem.getLocalName(); - for (int i=0 ; i<attrs.getLength() ; i++) - { - Attr attr = (Attr)attrs.item(i); - String aname = attr.getName(); - String aval = attr.getValue(); - if (aname.startsWith("xmlns")) - continue; - else if (aname.equals("d"))//already handled - continue; - else if (aname.startsWith("transform")) - { - parseTransform(aval, trans); - continue; - } - else if (aname.equals("style")) - { - StyleInfo style = styles.get(aval); - if (style != null) - { - po(" draw:style-name=\""); - po(style.getName()); - po("\""); - } - continue; - } - if (aname.equals("x") || aname.equals("y") || - aname.equals("width") || aname.equals("height")) - { - aval = coordToOdg(aval); - } - if ("id".equals(aname)) - po(" "); - else if ("transform".equals(aname)) - po(" draw:"); - else - po(" svg:"); - po(aname); - po("=\""); - po(aval); - po("\""); - } - - //Output the current transform - if (!trans.isIdentity() && - !( - ename.equals("g") || - ename.equals("defs") || - ename.equals("metadata") - ) - ) - { - double v[] = new double[6]; - trans.getMatrix(v); - po(" draw:transform=\"matrix(" + - nrfmt.format(v[0]) + "," + - nrfmt.format(v[1]) + "," + - nrfmt.format(v[2]) + "," + - nrfmt.format(v[3]) + "," + - nrfmt.format(v[4]) + "," + - nrfmt.format(v[5]) + ")\""); - } - return true; -} - -public boolean writeOdfContent(Element elem, AffineTransform trans) -{ - String ns = elem.getNamespaceURI(); - String tagName = elem.getLocalName(); - //trace("ns:" + ns + " tagName:" + tagName); - if (!ns.equals(SVG_NS)) - return true; - if (tagName.equals("svg")) - { - NodeList children = elem.getChildNodes(); - for (int i=0 ; i<children.getLength() ; i++) - { - Node n = children.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - if (!writeOdfContent((Element)n, - (AffineTransform)trans.clone())) - return false; - } - } - else if (tagName.equals("g")) - { - //String transform = elem.getAttribute("transform"); - po("<draw:g"); - writeSvgAttributes(elem, trans); po(">\n"); - NodeList children = elem.getChildNodes(); - for (int i=0 ; i<children.getLength() ; i++) - { - Node n = children.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - if (!writeOdfContent((Element)n, - (AffineTransform)trans.clone())) - return false; - } - po("</draw:g>\n"); - } - else if (tagName.equals("text")) - { - String x = coordToOdg(elem.getAttribute("x")); - String y = coordToOdg(elem.getAttribute("y")); - String width = "5cm"; - String height = "2cm"; - String txt = elem.getTextContent(); - po("<draw:frame draw:style-name=\"grx1\" draw:layer=\"layout\" "); - po("svg:x=\"" + x + "\" svg:y=\"" + y + "\" "); - po("svg:width=\"" + width + "\" svg:height=\"" + height + "\""); - po(">\n"); - po(" <draw:text-box draw:auto-grow-height=\"true\" draw:auto-grow-width=\"true\">\n"); - po(" <text:p text:style-name=\"P1\"> " + txt + "</text:p>\n"); - po(" </draw:text-box>\n"); - po("</draw:frame>\n"); - return true; - } - else if (tagName.equals("image")) - { - String x = coordToOdg(elem.getAttribute("x")); - String y = coordToOdg(elem.getAttribute("y")); - String width = coordToOdg(elem.getAttribute("width")); - String height = coordToOdg(elem.getAttribute("height")); - String imageName = elem.getAttributeNS(XLINK_NS, "href"); - po("<draw:frame draw:style-name=\"grx1\" draw:layer=\"layout\" "); - po("svg:x=\"" + x + "\" svg:y=\"" + y + "\" "); - po("svg:width=\"" + width + "\" svg:height=\"" + height + "\">\n"); - po(" <draw:image xlink:href=\"Pictures/" + imageName + "\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"><text:p/></draw:image>\n"); - po("</draw:frame>\n"); - return true; - } - else if (tagName.equals("path")) - { - double bounds[] = new double[4]; - String d = elem.getAttribute("d"); - String newd = parsePathData(d, bounds); - double x = bounds[0]; - double y = bounds[1]; - double width = (bounds[2]-bounds[0]); - double height = (bounds[3]-bounds[1]); - po("<draw:path draw:layer=\"layout\" \n"); - po(" svg:x=\"" + nrfmt.format(x) + "cm\" "); - po("svg:y=\"" + nrfmt.format(y) + "cm\" "); - po("svg:width=\"" + nrfmt.format(width) + "cm\" "); - po("svg:height=\"" + nrfmt.format(height) + "cm\" "); - po("svg:viewBox=\"0.0 0.0 " + - nrfmt.format(bounds[2] * 1000.0) + " " + - nrfmt.format(bounds[3] * 1000.0) + "\"\n"); - po(" svg:d=\"" + newd + "\"\n "); - - writeSvgAttributes(elem, trans); po("/>\n"); - //po(" svg:d=\"" + d + "\"/>\n"); - return true; - } - - else - { - //Verbatim tab mapping - po("<draw:"); po(tagName); - writeSvgAttributes(elem, trans); po(">\n"); - po("</draw:"); po(tagName); po(">\n"); - } - - return true; -} - - -boolean writeOdfContent(ZipOutputStream outs) -{ - try - { - ZipEntry ze = new ZipEntry("content.xml"); - outs.putNextEntry(ze); - out = new BufferedWriter(new OutputStreamWriter(outs)); - } - catch (IOException e) - { - return false; - } - - NodeList res = svg.getElementsByTagNameNS(SVG_NS, "svg"); - if (res.getLength() < 1) - { - err("saveOdf: no <svg> root in .svg file"); - return false; - } - Element root = (Element)res.item(0); - - - po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - po("<office:document-content\n"); - po(" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n"); - po(" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n"); - po(" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n"); - po(" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n"); - po(" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n"); - po(" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n"); - po(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"); - po(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"); - po(" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n"); - po(" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n"); - po(" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n"); - po(" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n"); - po(" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n"); - po(" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n"); - po(" xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n"); - po(" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n"); - po(" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n"); - po(" xmlns:ooo=\"http://openoffice.org/2004/office\"\n"); - po(" xmlns:ooow=\"http://openoffice.org/2004/writer\"\n"); - po(" xmlns:oooc=\"http://openoffice.org/2004/calc\"\n"); - po(" xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n"); - po(" xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n"); - po(" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n"); - po(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); - po(" xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n"); - po(" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n"); - po(" office:version=\"1.0\">\n"); - po("\n"); - po("\n"); - po("<office:scripts/>\n"); - po("<office:automatic-styles>\n"); - po("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n"); - po("<style:style style:name=\"grx1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n"); - po(" <style:graphic-properties draw:stroke=\"none\" draw:fill=\"solid\" draw:textarea-horizontal-align=\"center\" draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\" draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\" draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\" draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n"); - po("</style:style>\n"); - po("<style:style style:name=\"P1\" style:family=\"paragraph\">\n"); - po(" <style:paragraph-properties fo:text-align=\"center\"/>\n"); - po("</style:style>\n"); - - //## Dump our style table - for (StyleInfo s : styles.values()) - { - po("<style:style style:name=\"" + s.getName() + "\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n"); - po(" <style:graphic-properties"); - po(" draw:fill=\"" + s.getFill() + "\""); - if (!s.getFill().equals("none")) - po(" draw:fill-color=\"" + s.getFillColor() + "\""); - po(" draw:stroke=\"" + s.getStroke() + "\""); - if (!s.getStroke().equals("none")) - { - po(" svg:stroke-width=\"" + s.getStrokeWidth() + "\""); - po(" svg:stroke-color=\"" + s.getStrokeColor() + "\""); - } - po("/>\n"); - po("</style:style>\n"); - } - po("</office:automatic-styles>\n"); - po("\n"); - po("\n"); - po("<office:body>\n"); - po("<office:drawing>\n"); - po("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\" draw:master-page-name=\"Default\">\n"); - po("\n\n\n"); - AffineTransform trans = new AffineTransform(); - //trans.scale(12.0, 12.0); - po("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n"); - writeOdfContent(root, trans); - po("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n"); - po("\n\n\n"); - - po("</draw:page>\n"); - po("</office:drawing>\n"); - po("</office:body>\n"); - po("</office:document-content>\n"); - - - try - { - out.flush(); - outs.closeEntry(); - } - catch (IOException e) - { - err("writeOdfContent:" + e); - return false; - } - return true; -} - -boolean writeOdfMeta(ZipOutputStream outs) -{ - try - { - ZipEntry ze = new ZipEntry("meta.xml"); - outs.putNextEntry(ze); - out = new BufferedWriter(new OutputStreamWriter(outs)); - } - catch (IOException e) - { - return false; - } - - po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - po("<office:document-meta\n"); - po(" xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n"); - po(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"); - po(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"); - po(" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n"); - po(" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n"); - po(" xmlns:ooo=\"http://openoffice.org/2004/office\"\n"); - po(" xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n"); - po(" xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n"); - po(" office:version=\"1.0\">\n"); - po("<office:meta>\n"); - po(" <meta:generator>Inkscape-0.43</meta:generator>\n"); - po(" <meta:initial-creator>clark kent</meta:initial-creator>\n"); - po(" <meta:creation-date>2005-12-10T10:55:13</meta:creation-date>\n"); - po(" <dc:creator>clark kent</dc:creator>\n"); - po(" <dc:date>2005-12-10T10:56:20</dc:date>\n"); - po(" <dc:language>en-US</dc:language>\n"); - po(" <meta:editing-cycles>2</meta:editing-cycles>\n"); - po(" <meta:editing-duration>PT1M13S</meta:editing-duration>\n"); - po(" <meta:user-defined meta:name=\"Info 1\"/>\n"); - po(" <meta:user-defined meta:name=\"Info 2\"/>\n"); - po(" <meta:user-defined meta:name=\"Info 3\"/>\n"); - po(" <meta:user-defined meta:name=\"Info 4\"/>\n"); - po(" <meta:document-statistic meta:object-count=\"2\"/>\n"); - po("</office:meta>\n"); - po("</office:document-meta>\n"); - - - try - { - out.flush(); - outs.closeEntry(); - } - catch (IOException e) - { - err("writeOdfContent:" + e); - return false; - } - return true; -} - - -boolean writeOdfManifest(ZipOutputStream outs) -{ - try - { - ZipEntry ze = new ZipEntry("META-INF/manifest.xml"); - outs.putNextEntry(ze); - out = new BufferedWriter(new OutputStreamWriter(outs)); - } - catch (IOException e) - { - return false; - } - - po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - po("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n"); - po("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n"); - po(" <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n"); - po(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n"); - po(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n"); - po(" <!--List our images here-->\n"); - for (int i=0 ; i<images.size() ; i++) - { - ImageInfo ie = images.get(i); - String fname = ie.getName(); - if (fname.length() < 5) - { - err("image file name too short:" + fname); - return false; - } - String ext = fname.substring(fname.length() - 4); - po(" <manifest:file-entry manifest:media-type=\""); - if (ext.equals(".gif")) - po("image/gif"); - else if (ext.equals(".png")) - po("image/png"); - else if (ext.equals(".jpg") || ext.equals(".jpeg")) - po("image/jpeg"); - po("\" manifest:full-path=\""); - po(fname); - po("\"/>\n"); - } - po("</manifest:manifest>\n"); - - try - { - out.flush(); - outs.closeEntry(); - } - catch (IOException e) - { - err("writeOdfContent:" + e); - return false; - } - return true; -} - -boolean writeOdfImages(ZipOutputStream outs) -{ - for (int i=0 ; i<images.size() ; i++) - { - ImageInfo ie = images.get(i); - try - { - String iname = "Pictures/" + ie.getName(); - ZipEntry ze = new ZipEntry(iname); - outs.putNextEntry(ze); - outs.write(ie.getBuf()); - outs.closeEntry(); - } - catch (IOException e) - { - err("writing images:" + e); - return false; - } - } - return true; -} - -/** - * - */ -public boolean writeOdf(OutputStream outs) -{ - try - { - ZipOutputStream zos = new ZipOutputStream(outs); - if (!writeOdfContent(zos)) - return false; - if (!writeOdfManifest(zos)) - return false; - if (!writeOdfMeta(zos)) - return false; - if (!writeOdfImages(zos)) - return false; - //if (!writeOdfStyles(zos)) - // return false; - zos.close(); - } - catch (IOException e) - { - err("closing ODF zip output stream:" + e); - return false; - } - return true; -} - - - -/** - * - */ -public boolean saveOdf(String fileName) -{ - boolean ret = true; - try - { - FileOutputStream fos = new FileOutputStream(fileName); - ret = writeOdf(fos); - fos.close(); - } - catch (IOException e) - { - err("writing odf " + fileName + " : " + e); - return false; - } - return ret; -} - - - -boolean parseCss(String css) -{ - trace("##### STYLE ### :" + css); - String name = "gr" + styleNr; - styleNr++; - StyleInfo si = new StyleInfo(name, css); - StringTokenizer st = new StringTokenizer(css, ";"); - while (st.hasMoreTokens()) - { - String style = st.nextToken(); - //trace(" " + style); - int pos = style.indexOf(':'); - if (pos < 1 || pos > style.length()-2) - continue; - String attrName = style.substring(0, pos); - String attrVal = style.substring(pos+1); - trace(" =" + attrName + ':' + attrVal); - if ("stroke".equals(attrName)) - { - si.stroke = "solid"; - si.strokeColor = attrVal; - } - else if ("stroke-width".equals(attrName)) - { - si.strokeWidth = attrVal; - } - else if ("fill".equals(attrName)) - { - si.fill = "solid"; - si.fillColor = attrVal; - } - } - styles.put(css, si); - return true; -} - -boolean readSvg(InputStream ins) -{ - //### LOAD XML - try - { - DocumentBuilderFactory factory = - DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = - factory.newDocumentBuilder(); - builder.setEntityResolver(new EntityResolver() - { - public InputSource resolveEntity(String publicId, String systemId) - { - return new InputSource(new ByteArrayInputStream(new byte[0])); - } - }); - Document doc = builder.parse(ins); - svg = doc; - } - catch (javax.xml.parsers.ParserConfigurationException e) - { - err("making DOM parser:"+e); - return false; - } - catch (org.xml.sax.SAXException e) - { - err("parsing svg document:"+e); - return false; - } - catch (IOException e) - { - err("parsing svg document:"+e); - return false; - } - //dumpDocument(svg); - - //### LOAD IMAGES - imageNr = 0; - images = new ArrayList<ImageInfo>(); - NodeList res = svg.getElementsByTagNameNS(SVG_NS, "image"); - for (int i=0 ; i<res.getLength() ; i++) - { - Element elem = (Element) res.item(i); - String fileName = elem.getAttributeNS(XLINK_NS, "href"); - if (fileName == null) - { - err("No xlink:href pointer to image data for image"); - return false; - } - File f = new File(fileName); - if (!f.exists()) - { - err("image '" + fileName + "' does not exist"); - return false; - } - int bufSize = (int)f.length(); - byte buf[] = new byte[bufSize]; - int pos = 0; - try - { - FileInputStream fis = new FileInputStream(fileName); - while (pos < bufSize) - { - int len = fis.read(buf, pos, bufSize - pos); - if (len < 0) - break; - pos += len; - } - fis.close(); - } - catch (IOException e) - { - err("reading image '" + fileName + "' :" + e); - return false; - } - if (pos != bufSize) - { - err("reading image entry. expected " + - bufSize + ", found " + pos + " bytes."); - return false; - } - ImageInfo ie = new ImageInfo(fileName, fileName, buf); - images.add(ie); - } - - //### Parse styles - styleNr = 0; - styles = new HashMap<String, StyleInfo>(); - res = svg.getElementsByTagName("*"); - for (int i=0 ; i<res.getLength() ; i++) - { - Element elem = (Element) res.item(i); - trace("elem:"+ elem.getNodeName()); - String style = elem.getAttribute("style"); - if (style != null && style.length() > 0) - parseCss(style); - } - - return true; -} - -boolean readSvg(String fileName) -{ - try - { - FileInputStream fis = new FileInputStream(fileName); - if (!readSvg(fis)) - return false; - fis.close(); - } - catch (IOException e) - { - err("opening svg file:"+e); - return false; - } - return true; -} - - -//######################################################################## -//# O D F T O S V G -//######################################################################## - -/** - * - */ -public boolean readOdfEntry(ZipInputStream zis, ZipEntry ent) -{ - String fileName = ent.getName(); - trace("fileName:" + fileName); - if (fileName.length() < 4) - return true; - String ext = fileName.substring(fileName.length() - 4); - trace("ext:" + ext); - ArrayList<Byte> arr = new ArrayList<Byte>(); - try - { - while (true) - { - int inb = zis.read(); - if (inb < 0) - break; - arr.add((byte)inb); - } - } - catch (IOException e) - { - return false; - } - byte buf[] = new byte[arr.size()]; - for (int i=0 ; i<buf.length ; i++) - buf[i] = arr.get(i); - trace("bufsize:" + buf.length); - - if (ext.equals(".xml")) - { - try - { - DocumentBuilderFactory factory = - DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - DocumentBuilder builder = - factory.newDocumentBuilder(); - builder.setEntityResolver(new EntityResolver() - { - public InputSource resolveEntity(String publicId, String systemId) - { - return new InputSource(new ByteArrayInputStream(new byte[0])); - } - }); - //trace("doc:"+new String(buf)); - Document doc = builder.parse(new ByteArrayInputStream(buf)); - if (fileName.equals("content.xml")) - content = doc; - else if (fileName.equals("meta.xml")) - meta = doc; - else if (fileName.equals("styles.xml")) - { - //styles = doc; - } - } - catch (javax.xml.parsers.ParserConfigurationException e) - { - err("making DOM parser:"+e); - return false; - } - catch (org.xml.sax.SAXException e) - { - err("parsing document:"+e); - return false; - } - catch (IOException e) - { - err("parsing document:"+e); - return false; - } - } - else if (ext.equals(".png") || - ext.equals(".gif") || - ext.equals(".jpg") || - ext.equals(".jpeg") ) - { - String imageName = "image" + imageNr + ext; - imageNr++; - ImageInfo ie = new ImageInfo(fileName, imageName, buf); - trace("added image '" + imageName + "'. " + buf.length +" bytes."); - images.add(ie); - - } - dumpDocument(content); - return true; -} - - -/** - * - */ -public boolean readOdf(InputStream ins) -{ - imageNr = 0; - images = new ArrayList<ImageInfo>(); - styleNr = 0; - styles = new HashMap<String, StyleInfo>(); - ZipInputStream zis = new ZipInputStream(ins); - while (true) - { - try - { - ZipEntry ent = zis.getNextEntry(); - if (ent == null) - break; - if (!readOdfEntry(zis, ent)) - return false; - zis.closeEntry(); - } - catch (IOException e) - { - err("reading zip entry"); - return false; - } - } - - return true; -} - - -/** - * - */ -public boolean readOdf(String fileName) -{ - boolean ret = true; - try - { - FileInputStream fis = new FileInputStream(fileName); - ret = readOdf(fis); - fis.close(); - } - catch (IOException e) - { - err("reading " + fileName + " : " + e); - ret = false; - } - return true; -} - - - - -public boolean writeSvgElement(Element elem) -{ - String ns = elem.getNamespaceURI(); - String tagName = elem.getLocalName(); - trace("tag:" + tagName + " : " + ns); - if (ns.equals(ODSVG_NS)) - { - po("<"); po(tagName); - NamedNodeMap attrs = elem.getAttributes(); - for (int i=0 ; i<attrs.getLength() ; i++) - { - Attr attr = (Attr)attrs.item(i); - String aname = attr.getName(); - String aval = attr.getValue(); - //Replace image name - if ("xlink:href".equals(aname) && "image".equals(tagName)) - { - for (int j=0 ; j<images.size() ; j++) - { - ImageInfo ie = images.get(i); - if (aval.equals(ie.getName())) - aval = ie.getNewName(); - } - } - po(" "); - po(aname); - po("=\""); - po(aval); - po("\""); - } - po(">\n"); - } - NodeList children = elem.getChildNodes(); - for (int i=0 ; i<children.getLength() ; i++) - { - Node n = children.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - if (!writeSvgElement((Element)n)) - return false; - } - if (ns.equals(ODSVG_NS)) - { - po("</"); po(tagName); po(">\n"); - } - return true; -} - - -public boolean saveSvg(String svgFileName) -{ - trace("====== Saving images ==========="); - try - { - for (int i=0 ; i<images.size() ; i++) - { - ImageInfo entry = images.get(i); - trace("saving:" + entry.name); - FileOutputStream fos = new FileOutputStream(entry.name); - fos.write(entry.buf); - fos.close(); - } - } - catch (IOException e) - { - err("saveAsSVG:" + e); - return false; - } - - try - { - out = new BufferedWriter(new FileWriter(svgFileName)); - } - catch (IOException e) - { - err("save:" + e); - return false; - } - - if (content == null) - { - err("no content in odf"); - return false; - } - - NodeList res = content.getElementsByTagNameNS(ODF_NS, "drawing"); - if (res.getLength() < 1) - { - err("save: no drawing in document"); - return false; - } - Element root = (Element)res.item(0); - trace("NS:"+root.getNamespaceURI()); - - res = root.getElementsByTagNameNS(ODG_NS, "page"); - if (res.getLength() < 1) - { - err("save: no page in drawing"); - return false; - } - Element page = (Element)res.item(0); - - po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); - po("<svg\n"); - po(" xmlns:svg=\"http://www.w3.org/2000/svg\"\n"); - po(" xmlns=\"http://www.w3.org/2000/svg\"\n"); - po(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"); - po(" version=\"1.0\"\n"); - po(" >\n"); - - writeSvgElement(page); - - po("</svg>\n"); - - try - { - out.close(); - } - catch (IOException e) - { - err("save:close:" + e); - return false; - } - return true; -} - - - -//######################################################################## -//# C O N S T R U C T O R -//######################################################################## - -SvgOdg() -{ - //init, if necessary - nrfmt = new DecimalFormat("#.####"); -} - - -//######################################################################## -//# C O M M A N D L I N E -//######################################################################## - -public boolean odfToSvg(String odfName, String svgName) -{ - if (!readOdf(odfName)) - return false; - if (!saveSvg(svgName)) - return false; - return true; -} - -public boolean svgToOdf(String svgName, String odfName) -{ - if (!readSvg(svgName)) - return false; - System.out.println("ok"); - if (!saveOdf(odfName)) - return false; - return true; -} - -void usage() -{ - System.out.println("usage: SvgOdf input_file.odg output_file.svg"); - System.out.println(" SvgOdf input_file.svg output_file.odg"); -} - - -boolean parseArguments(String argv[]) -{ - if (argv.length != 2) - { - usage(); - return false; - } - - String fileName1 = argv[0]; - String fileName2 = argv[1]; - - if (fileName1.length()<5 || fileName2.length()<5) - { - System.out.println("one or more file names is too short"); - usage(); - return false; - } - - String ext1 = fileName1.substring(fileName1.length()-4); - String ext2 = fileName2.substring(fileName2.length()-4); - //System.out.println("ext1:"+ext1+" ext2:"+ext2); - - //##### ODG -> SVG ##### - if ((ext1.equals(".odg") || ext1.equals(".odf") || ext1.equals(".zip") ) && - ext2.equals(".svg")) - { - if (!odfToSvg(argv[0], argv[1])) - { - System.out.println("Conversion from ODG to SVG failed"); - return false; - } - } - - //##### SVG -> ODG ##### - else if (ext1.equals(".svg") && - ( ext2.equals(".odg") || ext2.equals(".odf") || ext2.equals(".zip") ) ) - { - if (!svgToOdf(fileName1, fileName2)) - { - System.out.println("Conversion from SVG to ODG failed"); - return false; - } - } - - //##### none of the above ##### - else - { - usage(); - return false; - } - return true; -} - - -public static void main(String argv[]) -{ - SvgOdg svgodg = new SvgOdg(); - svgodg.parseArguments(argv); -} - - -} - -//######################################################################## -//# E N D O F F I L E -//######################################################################## - diff --git a/src/doxygen-main.cpp b/src/doxygen-main.cpp index fd8f4bb1a..c6a6bb3ab 100644 --- a/src/doxygen-main.cpp +++ b/src/doxygen-main.cpp @@ -224,7 +224,7 @@ namespace XML {} * - SPEllipse * - SPLine [\ref sp-line.cpp, \ref sp-line.h] * - SPOffset [\ref sp-offset.cpp, \ref sp-offset.h] - * - SPPath [\ref sp-path.cpp, \ref sp-path.h, \ref path-chemistry.cpp, \ref nodepath.cpp, \ref nodepath.h, \ref splivarot.cpp] + * - SPPath [\ref sp-path.cpp, \ref sp-path.h, \ref path-chemistry.cpp, \ref splivarot.cpp] * - SPPolygon [\ref sp-polygon.cpp, \ref sp-polygon.h] * - SPStar [\ref sp-star.cpp, \ref sp-star.h] * - SPPolyLine [\ref sp-polyline.cpp, \ref sp-polyline.h] diff --git a/src/draw-context.cpp b/src/draw-context.cpp index de9a7c7e5..da22c8a7a 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -511,9 +511,7 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p, /* Snap it along best vector */ SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; m.setup(SP_EVENT_CONTEXT_DESKTOP(ec)); - Geom::Point pt2g = to_2geom(p); - m.constrainedSnapReturnByRef( Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE, Inkscape::Snapper::ConstraintLine(best), false); - p = from_2geom(pt2g); + m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE, Inkscape::Snapper::ConstraintLine(best)); } } } @@ -522,16 +520,14 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p, void spdc_endpoint_snap_free(SPEventContext const * const ec, Geom::Point& p, guint const /*state*/) { SPDesktop *dt = SP_EVENT_CONTEXT_DESKTOP(ec); - SnapManager &m = dt->namedview->snap_manager; - Inkscape::Selection *selection = sp_desktop_selection (dt); + SnapManager &m = dt->namedview->snap_manager; + Inkscape::Selection *selection = sp_desktop_selection (dt); - // selection->singleItem() is the item that is currently being drawn. This item will not be snapped to (to avoid self-snapping) - // TODO: Allow snapping to the stationary parts of the item, and only ignore the last segment + // selection->singleItem() is the item that is currently being drawn. This item will not be snapped to (to avoid self-snapping) + // TODO: Allow snapping to the stationary parts of the item, and only ignore the last segment - m.setup(dt, true, selection->singleItem()); - Geom::Point pt2g = to_2geom(p); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); - p = from_2geom(pt2g); + m.setup(dt, true, selection->singleItem()); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); } static SPCurve * diff --git a/src/enums.h b/src/enums.h index 4d3c75c20..8b9dcc71f 100644 --- a/src/enums.h +++ b/src/enums.h @@ -81,5 +81,13 @@ enum PrefsSelectionContext { PREFS_SELECTION_LAYER_RECURSIVE = 2, }; +/* clip/mask group enclosing behavior preference values */ + +enum PrefsMaskobjectGrouping { + PREFS_MASKOBJECT_GROUPING_NONE = 0, + PREFS_MASKOBJECT_GROUPING_SEPARATE = 1, + PREFS_MASKOBJECT_GROUPING_ALL = 2, +}; + #endif diff --git a/src/event-context.cpp b/src/event-context.cpp index 918e3d419..6184fb4c7 100644 --- a/src/event-context.cpp +++ b/src/event-context.cpp @@ -8,7 +8,7 @@ * Frank Felfe <innerspace@iname.com> * bulia byak <buliabyak@users.sf.net> * - * Copyright (C) 1999-2005 authors + * Copyright (C) 1999-2010 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information @@ -43,6 +43,8 @@ #include "shortcuts.h" #include "desktop.h" #include "desktop-handles.h" +#include "desktop-events.h" +#include "widgets/desktop-widget.h" #include "sp-namedview.h" #include "selection.h" #include "file.h" @@ -56,16 +58,20 @@ #include "attributes.h" #include "rubberband.h" #include "selcue.h" -#include "node-context.h" #include "lpe-tool-context.h" +#include "ui/tool/control-point.h" +#include "shape-editor.h" +#include "sp-guide.h" static void sp_event_context_class_init(SPEventContextClass *klass); static void sp_event_context_init(SPEventContext *event_context); static void sp_event_context_dispose(GObject *object); static void sp_event_context_private_setup(SPEventContext *ec); -static gint sp_event_context_private_root_handler(SPEventContext *event_context, GdkEvent *event); -static gint sp_event_context_private_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); +static gint sp_event_context_private_root_handler( + SPEventContext *event_context, GdkEvent *event); +static gint sp_event_context_private_item_handler( + SPEventContext *event_context, SPItem *item, GdkEvent *event); static void set_event_location(SPDesktop * desktop, GdkEvent * event); @@ -91,22 +97,16 @@ static guint scroll_keyval = 0; /** * Registers the SPEventContext class with Glib and returns its type number. */ -GType -sp_event_context_get_type(void) -{ +GType sp_event_context_get_type(void) { static GType type = 0; if (!type) { - GTypeInfo info = { - sizeof(SPEventContextClass), - NULL, NULL, - (GClassInitFunc) sp_event_context_class_init, - NULL, NULL, - sizeof(SPEventContext), - 4, - (GInstanceInitFunc) sp_event_context_init, - NULL, /* value_table */ + GTypeInfo info = { sizeof(SPEventContextClass), NULL, NULL, + (GClassInitFunc) sp_event_context_class_init, NULL, NULL, + sizeof(SPEventContext), 4, + (GInstanceInitFunc) sp_event_context_init, NULL, /* value_table */ }; - type = g_type_register_static(G_TYPE_OBJECT, "SPEventContext", &info, (GTypeFlags)0); + type = g_type_register_static(G_TYPE_OBJECT, "SPEventContext", &info, + (GTypeFlags) 0); } return type; } @@ -114,14 +114,12 @@ sp_event_context_get_type(void) /** * Callback to set up the SPEventContext vtable. */ -static void -sp_event_context_class_init(SPEventContextClass *klass) -{ +static void sp_event_context_class_init(SPEventContextClass *klass) { GObjectClass *object_class; object_class = (GObjectClass *) klass; - parent_class = (GObjectClass*)g_type_class_peek_parent(klass); + parent_class = (GObjectClass*) g_type_class_peek_parent(klass); object_class->dispose = sp_event_context_dispose; @@ -133,9 +131,7 @@ sp_event_context_class_init(SPEventContextClass *klass) /** * Clears all SPEventContext object members. */ -static void -sp_event_context_init(SPEventContext *event_context) -{ +static void sp_event_context_init(SPEventContext *event_context) { event_context->desktop = NULL; event_context->cursor = NULL; event_context->_message_context = NULL; @@ -144,14 +140,13 @@ sp_event_context_init(SPEventContext *event_context) event_context->space_panning = false; event_context->shape_editor = NULL; event_context->_delayed_snap_event = NULL; + event_context->_dse_callback_in_process = false; } /** * Callback to free and null member variables of SPEventContext object. */ -static void -sp_event_context_dispose(GObject *object) -{ +static void sp_event_context_dispose(GObject *object) { SPEventContext *ec; ec = SP_EVENT_CONTEXT(object); @@ -183,9 +178,7 @@ sp_event_context_dispose(GObject *object) /** * Recreates and draws cursor on desktop related to SPEventContext. */ -void -sp_event_context_update_cursor(SPEventContext *ec) -{ +void sp_event_context_update_cursor(SPEventContext *ec) { GtkWidget *w = GTK_WIDGET(sp_desktop_canvas(ec->desktop)); if (w->window) { /* fixme: */ @@ -195,13 +188,12 @@ sp_event_context_update_cursor(SPEventContext *ec) sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, ec->cursor_shape); if ((bitmap != NULL) && (mask != NULL)) { if (ec->cursor) - gdk_cursor_unref (ec->cursor); + gdk_cursor_unref(ec->cursor); ec->cursor = gdk_cursor_new_from_pixmap(bitmap, mask, - &w->style->black, - &w->style->white, - ec->hot_x, ec->hot_y); - g_object_unref (bitmap); - g_object_unref (mask); + &w->style->black, &w->style->white, ec->hot_x, + ec->hot_y); + g_object_unref(bitmap); + g_object_unref(mask); } } gdk_window_set_cursor(w->window, ec->cursor); @@ -214,58 +206,56 @@ sp_event_context_update_cursor(SPEventContext *ec) * Callback that gets called on initialization of SPEventContext object. * Redraws mouse cursor, at the moment. */ -static void -sp_event_context_private_setup(SPEventContext *ec) -{ +static void sp_event_context_private_setup(SPEventContext *ec) { sp_event_context_update_cursor(ec); } /** * \brief Gobbles next key events on the queue with the same keyval and mask. Returns the number of events consumed. */ -gint gobble_key_events(guint keyval, gint mask) -{ +gint gobble_key_events(guint keyval, gint mask) { GdkEvent *event_next; gint i = 0; event_next = gdk_event_get(); // while the next event is also a key notify with the same keyval and mask, - while (event_next && (event_next->type == GDK_KEY_PRESS || event_next->type == GDK_KEY_RELEASE) - && event_next->key.keyval == keyval - && (!mask || (event_next->key.state & mask))) { + while (event_next && (event_next->type == GDK_KEY_PRESS || event_next->type + == GDK_KEY_RELEASE) && event_next->key.keyval == keyval && (!mask + || (event_next->key.state & mask))) { if (event_next->type == GDK_KEY_PRESS) - i ++; + i++; // kill it gdk_event_free(event_next); // get next event_next = gdk_event_get(); } // otherwise, put it back onto the queue - if (event_next) gdk_event_put(event_next); + if (event_next) + gdk_event_put(event_next); return i; } /** * \brief Gobbles next motion notify events on the queue with the same mask. Returns the number of events consumed. -*/ -gint gobble_motion_events(gint mask) -{ + */ +gint gobble_motion_events(gint mask) { GdkEvent *event_next; gint i = 0; event_next = gdk_event_get(); // while the next event is also a key notify with the same keyval and mask, while (event_next && event_next->type == GDK_MOTION_NOTIFY - && (event_next->motion.state & mask)) { + && (event_next->motion.state & mask)) { // kill it gdk_event_free(event_next); // get next event_next = gdk_event_get(); - i ++; + i++; } // otherwise, put it back onto the queue - if (event_next) gdk_event_put(event_next); + if (event_next) + gdk_event_put(event_next); return i; } @@ -274,20 +264,21 @@ gint gobble_motion_events(gint mask) * Toggles current tool between active tool and selector tool. * Subroutine of sp_event_context_private_root_handler(). */ -static void -sp_toggle_selector(SPDesktop *dt) -{ - if (!dt->event_context) return; +static void sp_toggle_selector(SPDesktop *dt) { + if (!dt->event_context) + return; if (tools_isactive(dt, TOOLS_SELECT)) { if (selector_toggled) { - if (switch_selector_to) tools_switch (dt, switch_selector_to); + if (switch_selector_to) + tools_switch(dt, switch_selector_to); selector_toggled = FALSE; - } else return; + } else + return; } else { selector_toggled = TRUE; switch_selector_to = tools_active(dt); - tools_switch (dt, TOOLS_SELECT); + tools_switch(dt, TOOLS_SELECT); } } @@ -295,20 +286,21 @@ sp_toggle_selector(SPDesktop *dt) * Toggles current tool between active tool and dropper tool. * Subroutine of sp_event_context_private_root_handler(). */ -static void -sp_toggle_dropper(SPDesktop *dt) -{ - if (!dt->event_context) return; +static void sp_toggle_dropper(SPDesktop *dt) { + if (!dt->event_context) + return; if (tools_isactive(dt, TOOLS_DROPPER)) { if (dropper_toggled) { - if (switch_dropper_to) tools_switch (dt, switch_dropper_to); + if (switch_dropper_to) + tools_switch(dt, switch_dropper_to); dropper_toggled = FALSE; - } else return; + } else + return; } else { dropper_toggled = TRUE; switch_dropper_to = tools_active(dt); - tools_switch (dt, TOOLS_DROPPER); + tools_switch(dt, TOOLS_DROPPER); } } @@ -316,8 +308,8 @@ sp_toggle_dropper(SPDesktop *dt) * Calculates and keeps track of scroll acceleration. * Subroutine of sp_event_context_private_root_handler(). */ -static gdouble accelerate_scroll(GdkEvent *event, gdouble acceleration, SPCanvas */*canvas*/) -{ +static gdouble accelerate_scroll(GdkEvent *event, gdouble acceleration, + SPCanvas */*canvas*/) { guint32 time_diff = ((GdkEventKey *) event)->time - scroll_event_time; /* key pressed within 500ms ? (1/2 second) */ @@ -336,8 +328,8 @@ static gdouble accelerate_scroll(GdkEvent *event, gdouble acceleration, SPCanvas /** * Main event dispatch, gets called from Gdk. */ -static gint sp_event_context_private_root_handler(SPEventContext *event_context, GdkEvent *event) -{ +static gint sp_event_context_private_root_handler( + SPEventContext *event_context, GdkEvent *event) { static Geom::Point button_w; static unsigned int panning = 0; static unsigned int zoom_rb = 0; @@ -346,380 +338,386 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, Inkscape::Preferences *prefs = Inkscape::Preferences::get(); /// @todo REmove redundant /value in preference keys - tolerance = prefs->getIntLimited( - "/options/dragtolerance/value", 0, 0, 100); + tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); gint ret = FALSE; switch (event->type) { - case GDK_2BUTTON_PRESS: - if (panning) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); + case GDK_2BUTTON_PRESS: + if (panning) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); + ret = TRUE; + } else { + /* sp_desktop_dialog(); */ + } + break; + case GDK_BUTTON_PRESS: + + // save drag origin + xp = (gint) event->button.x; + yp = (gint) event->button.y; + within_tolerance = true; + + button_w = Geom::Point(event->button.x, event->button.y); + + switch (event->button.button) { + case 1: + if (event_context->space_panning) { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 1; + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time - 1); ret = TRUE; - } else { - /* sp_desktop_dialog(); */ } break; - case GDK_BUTTON_PRESS: - - // save drag origin - xp = (gint) event->button.x; - yp = (gint) event->button.y; - within_tolerance = true; - - button_w = Geom::Point(event->button.x, event->button.y); - - switch (event->button.button) { - case 1: - if (event_context->space_panning) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 1; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, - NULL, event->button.time-1); - ret = TRUE; - } - break; - case 2: - if (event->button.state & GDK_SHIFT_MASK) { - zoom_rb = 2; - } else { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 2; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, - NULL, event->button.time-1); - } - ret = TRUE; - break; - case 3: - if (event->button.state & GDK_SHIFT_MASK - || event->button.state & GDK_CONTROL_MASK) { - // When starting panning, make sure there are no snap events pending because these might disable the panning again - sp_event_context_discard_delayed_snap_event(event_context); - panning = 3; - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK, - NULL, event->button.time); - ret = TRUE; - } else { - sp_event_root_menu_popup(desktop, NULL, event); - } - break; - default: - break; + case 2: + if (event->button.state & GDK_SHIFT_MASK) { + zoom_rb = 2; + } else { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 2; + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time - 1); } + ret = TRUE; break; - case GDK_MOTION_NOTIFY: - if (panning) { - if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK)) - || (panning == 1 && !(event->motion.state & GDK_BUTTON1_MASK)) - || (panning == 3 && !(event->motion.state & GDK_BUTTON3_MASK)) - ) { - /* Gdk seems to lose button release for us sometimes :-( */ - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - ret = TRUE; - } else { - if ( within_tolerance - && ( abs( (gint) event->motion.x - xp ) < tolerance ) - && ( abs( (gint) event->motion.y - yp ) < tolerance )) - { - // do not drag if we're within tolerance from origin - break; - } - // Once the user has moved farther than tolerance from - // the original location (indicating they intend to move - // the object, not click), then always process the motion - // notify coordinates as given (no snapping back to origin) - within_tolerance = false; - - // gobble subsequent motion events to prevent "sticking" - // when scrolling is slow - gobble_motion_events(panning == 2 ? - GDK_BUTTON2_MASK : - (panning == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK)); - - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const moved_w( motion_w - button_w ); - event_context->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw - ret = TRUE; - } - } else if (zoom_rb) { - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const motion_dt(desktop->w2d(motion_w)); - - if ( within_tolerance - && ( abs( (gint) event->motion.x - xp ) < tolerance ) - && ( abs( (gint) event->motion.y - yp ) < tolerance ) ) { - break; // do not drag if we're within tolerance from origin - } - // Once the user has moved farther than tolerance from the original location - // (indicating they intend to move the object, not click), then always process the - // motion notify coordinates as given (no snapping back to origin) - within_tolerance = false; - - if (Inkscape::Rubberband::get(desktop)->is_started()) { - Inkscape::Rubberband::get(desktop)->move(motion_dt); - } else { - Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt); - } - if (zoom_rb == 2) - gobble_motion_events(GDK_BUTTON2_MASK); + case 3: + if (event->button.state & GDK_SHIFT_MASK || event->button.state + & GDK_CONTROL_MASK) { + // When starting panning, make sure there are no snap events pending because these might disable the panning again + sp_event_context_discard_delayed_snap_event(event_context); + panning = 3; + sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), + GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK, NULL, + event->button.time); + ret = TRUE; + } else { + sp_event_root_menu_popup(desktop, NULL, event); } break; - case GDK_BUTTON_RELEASE: - xp = yp = 0; - if (within_tolerance && (panning || zoom_rb)) { - zoom_rb = 0; - if (panning) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - } - Geom::Point const event_w(event->button.x, event->button.y); - Geom::Point const event_dt(desktop->w2d(event_w)); - double const zoom_inc = prefs->getDoubleLimited("/options/zoomincrement/value", M_SQRT2, 1.01, 10); - desktop->zoom_relative_keep_point(event_dt, - (event->button.state & GDK_SHIFT_MASK) ? 1/zoom_inc : zoom_inc); - desktop->updateNow(); - ret = TRUE; - } else if (panning == event->button.button) { + default: + break; + } + break; + case GDK_MOTION_NOTIFY: + if (panning) { + if ((panning == 2 && !(event->motion.state & GDK_BUTTON2_MASK)) + || (panning == 1 && !(event->motion.state + & GDK_BUTTON1_MASK)) || (panning == 3 + && !(event->motion.state & GDK_BUTTON3_MASK))) { + /* Gdk seems to lose button release for us sometimes :-( */ panning = 0; sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->button.time); - - // in slow complex drawings, some of the motion events are lost; - // to make up for this, we scroll it once again to the button-up event coordinates - // (i.e. canvas will always get scrolled all the way to the mouse release point, - // even if few intermediate steps were visible) - Geom::Point const motion_w(event->button.x, event->button.y); - Geom::Point const moved_w( motion_w - button_w ); - event_context->desktop->scroll_world(moved_w); - desktop->updateNow(); + event->button.time); ret = TRUE; - } else if (zoom_rb == event->button.button) { - zoom_rb = 0; - Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); - Inkscape::Rubberband::get(desktop)->stop(); - if (b && !within_tolerance) { - desktop->set_display_area(*b, 10); + } else { + if (within_tolerance && (abs((gint) event->motion.x - xp) + < tolerance) && (abs((gint) event->motion.y - yp) + < tolerance)) { + // do not drag if we're within tolerance from origin + break; } + // Once the user has moved farther than tolerance from + // the original location (indicating they intend to move + // the object, not click), then always process the motion + // notify coordinates as given (no snapping back to origin) + within_tolerance = false; + + // gobble subsequent motion events to prevent "sticking" + // when scrolling is slow + gobble_motion_events(panning == 2 ? GDK_BUTTON2_MASK : (panning + == 1 ? GDK_BUTTON1_MASK : GDK_BUTTON3_MASK)); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const moved_w(motion_w - button_w); + event_context->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw ret = TRUE; } - break; - case GDK_KEY_PRESS: - { - double const acceleration = prefs->getDoubleLimited("/options/scrollingacceleration/value", 0, 0, 6); - int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", 10, 0, 1000); - - switch (get_group0_keyval(&event->key)) { - // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets - // in the editing window). So we resteal them back and run our regular shortcut - // invoker on them. - unsigned int shortcut; - case GDK_Tab: - case GDK_ISO_Left_Tab: - case GDK_F1: - shortcut = get_group0_keyval(&event->key); - if (event->key.state & GDK_SHIFT_MASK) - shortcut |= SP_SHORTCUT_SHIFT_MASK; - if (event->key.state & GDK_CONTROL_MASK) - shortcut |= SP_SHORTCUT_CONTROL_MASK; - if (event->key.state & GDK_MOD1_MASK) - shortcut |= SP_SHORTCUT_ALT_MASK; - ret = sp_shortcut_invoke(shortcut, desktop); - break; + } else if (zoom_rb) { + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt(desktop->w2d(motion_w)); + + if (within_tolerance && (abs((gint) event->motion.x - xp) + < tolerance) && (abs((gint) event->motion.y - yp) + < tolerance)) { + break; // do not drag if we're within tolerance from origin + } + // Once the user has moved farther than tolerance from the original location + // (indicating they intend to move the object, not click), then always process the + // motion notify coordinates as given (no snapping back to origin) + within_tolerance = false; - case GDK_D: - case GDK_d: - if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) { - sp_toggle_dropper(desktop); - ret = TRUE; - } - break; - case GDK_Q: - case GDK_q: - if (desktop->quick_zoomed()) { - ret = TRUE; - } - if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) { - desktop->zoom_quick(true); - ret = TRUE; - } - break; - case GDK_W: - case GDK_w: - case GDK_F4: - /* Close view */ - if (MOD__CTRL_ONLY) { - sp_ui_close_view(NULL); - ret = TRUE; - } - break; - case GDK_Left: // Ctrl Left - case GDK_KP_Left: - case GDK_KP_4: - if (MOD__CTRL_ONLY) { - int i = (int) floor(key_scroll * accelerate_scroll(event, acceleration, sp_desktop_canvas(desktop))); - gobble_key_events(get_group0_keyval(&event->key), - GDK_CONTROL_MASK); - event_context->desktop->scroll_world(i, 0); - ret = TRUE; - } - break; - case GDK_Up: // Ctrl Up - case GDK_KP_Up: - case GDK_KP_8: - if (MOD__CTRL_ONLY) { - int i = (int) floor(key_scroll * accelerate_scroll(event, acceleration, sp_desktop_canvas(desktop))); - gobble_key_events(get_group0_keyval(&event->key), - GDK_CONTROL_MASK); - event_context->desktop->scroll_world(0, i); - ret = TRUE; - } - break; - case GDK_Right: // Ctrl Right - case GDK_KP_Right: - case GDK_KP_6: - if (MOD__CTRL_ONLY) { - int i = (int) floor(key_scroll * accelerate_scroll(event, acceleration, sp_desktop_canvas(desktop))); - gobble_key_events(get_group0_keyval(&event->key), - GDK_CONTROL_MASK); - event_context->desktop->scroll_world(-i, 0); - ret = TRUE; - } - break; - case GDK_Down: // Ctrl Down - case GDK_KP_Down: - case GDK_KP_2: - if (MOD__CTRL_ONLY) { - int i = (int) floor(key_scroll * accelerate_scroll(event, acceleration, sp_desktop_canvas(desktop))); - gobble_key_events(get_group0_keyval(&event->key), - GDK_CONTROL_MASK); - event_context->desktop->scroll_world(0, -i); - ret = TRUE; - } - break; - case GDK_F10: - if (MOD__SHIFT_ONLY) { - sp_event_root_menu_popup(desktop, NULL, event); - ret= TRUE; - } - break; - case GDK_space: - if (prefs->getBool("/options/spacepans/value")) { - event_context->space_panning = true; - event_context->_message_context->set(Inkscape::INFORMATION_MESSAGE, _("<b>Space+mouse drag</b> to pan canvas")); - ret= TRUE; - } else { - sp_toggle_selector(desktop); - ret= TRUE; - } - break; - case GDK_z: - case GDK_Z: - if (MOD__ALT_ONLY) { - desktop->zoom_grab_focus(); - ret = TRUE; - } - break; - default: - break; + if (Inkscape::Rubberband::get(desktop)->is_started()) { + Inkscape::Rubberband::get(desktop)->move(motion_dt); + } else { + Inkscape::Rubberband::get(desktop)->start(desktop, motion_dt); } + if (zoom_rb == 2) + gobble_motion_events(GDK_BUTTON2_MASK); + } + break; + case GDK_BUTTON_RELEASE: + xp = yp = 0; + if (within_tolerance && (panning || zoom_rb)) { + zoom_rb = 0; + if (panning) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); + } + Geom::Point const event_w(event->button.x, event->button.y); + Geom::Point const event_dt(desktop->w2d(event_w)); + double const zoom_inc = prefs->getDoubleLimited( + "/options/zoomincrement/value", M_SQRT2, 1.01, 10); + desktop->zoom_relative_keep_point(event_dt, (event->button.state + & GDK_SHIFT_MASK) ? 1 / zoom_inc : zoom_inc); + desktop->updateNow(); + ret = TRUE; + } else if (panning == event->button.button) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->button.time); + + // in slow complex drawings, some of the motion events are lost; + // to make up for this, we scroll it once again to the button-up event coordinates + // (i.e. canvas will always get scrolled all the way to the mouse release point, + // even if few intermediate steps were visible) + Geom::Point const motion_w(event->button.x, event->button.y); + Geom::Point const moved_w(motion_w - button_w); + event_context->desktop->scroll_world(moved_w); + desktop->updateNow(); + ret = TRUE; + } else if (zoom_rb == event->button.button) { + zoom_rb = 0; + Geom::OptRect const b = + Inkscape::Rubberband::get(desktop)->getRectangle(); + Inkscape::Rubberband::get(desktop)->stop(); + if (b && !within_tolerance) { + desktop->set_display_area(*b, 10); + } + ret = TRUE; + } + break; + case GDK_KEY_PRESS: { + double const acceleration = prefs->getDoubleLimited( + "/options/scrollingacceleration/value", 0, 0, 6); + int const key_scroll = prefs->getIntLimited("/options/keyscroll/value", + 10, 0, 1000); + + switch (get_group0_keyval(&event->key)) { + // GDK insists on stealing these keys (F1 for no idea what, tab for cycling widgets + // in the editing window). So we resteal them back and run our regular shortcut + // invoker on them. + unsigned int shortcut; + case GDK_Tab: + case GDK_ISO_Left_Tab: + case GDK_F1: + shortcut = get_group0_keyval(&event->key); + if (event->key.state & GDK_SHIFT_MASK) + shortcut |= SP_SHORTCUT_SHIFT_MASK; + if (event->key.state & GDK_CONTROL_MASK) + shortcut |= SP_SHORTCUT_CONTROL_MASK; + if (event->key.state & GDK_MOD1_MASK) + shortcut |= SP_SHORTCUT_ALT_MASK; + ret = sp_shortcut_invoke(shortcut, desktop); + break; + + case GDK_D: + case GDK_d: + if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) { + sp_toggle_dropper(desktop); + ret = TRUE; + } + break; + case GDK_Q: + case GDK_q: + if (desktop->quick_zoomed()) { + ret = TRUE; + } + if (!MOD__SHIFT && !MOD__CTRL && !MOD__ALT) { + desktop->zoom_quick(true); + ret = TRUE; + } + break; + case GDK_W: + case GDK_w: + case GDK_F4: + /* Close view */ + if (MOD__CTRL_ONLY) { + sp_ui_close_view(NULL); + ret = TRUE; + } + break; + case GDK_Left: // Ctrl Left + case GDK_KP_Left: + case GDK_KP_4: + if (MOD__CTRL_ONLY) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, sp_desktop_canvas(desktop))); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + event_context->desktop->scroll_world(i, 0); + ret = TRUE; + } + break; + case GDK_Up: // Ctrl Up + case GDK_KP_Up: + case GDK_KP_8: + if (MOD__CTRL_ONLY) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, sp_desktop_canvas(desktop))); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + event_context->desktop->scroll_world(0, i); + ret = TRUE; + } + break; + case GDK_Right: // Ctrl Right + case GDK_KP_Right: + case GDK_KP_6: + if (MOD__CTRL_ONLY) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, sp_desktop_canvas(desktop))); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + event_context->desktop->scroll_world(-i, 0); + ret = TRUE; + } + break; + case GDK_Down: // Ctrl Down + case GDK_KP_Down: + case GDK_KP_2: + if (MOD__CTRL_ONLY) { + int i = (int) floor(key_scroll * accelerate_scroll(event, + acceleration, sp_desktop_canvas(desktop))); + gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); + event_context->desktop->scroll_world(0, -i); + ret = TRUE; + } + break; + case GDK_F10: + if (MOD__SHIFT_ONLY) { + sp_event_root_menu_popup(desktop, NULL, event); + ret = TRUE; + } + break; + case GDK_space: + if (prefs->getBool("/options/spacepans/value")) { + event_context->space_panning = true; + event_context->_message_context->set(Inkscape::INFORMATION_MESSAGE, + _("<b>Space+mouse drag</b> to pan canvas")); + ret = TRUE; + } else { + sp_toggle_selector(desktop); + ret = TRUE; + } + break; + case GDK_z: + case GDK_Z: + if (MOD__ALT_ONLY) { + desktop->zoom_grab_focus(); + ret = TRUE; + } + break; + default: + break; + } + } + break; + case GDK_KEY_RELEASE: + switch (get_group0_keyval(&event->key)) { + case GDK_space: + if (event_context->space_panning) { + event_context->space_panning = false; + event_context->_message_context->clear(); + if (panning == 1) { + panning = 0; + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), + event->key.time); + desktop->updateNow(); + } + ret = TRUE; } break; - case GDK_KEY_RELEASE: - switch (get_group0_keyval(&event->key)) { - case GDK_space: - if (event_context->space_panning) { - event_context->space_panning = false; - event_context->_message_context->clear(); - if (panning == 1) { - panning = 0; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), - event->key.time); - desktop->updateNow(); - } - ret= TRUE; - } - break; - case GDK_Q: - case GDK_q: - if (desktop->quick_zoomed()) { - desktop->zoom_quick(false); - ret = TRUE; - } - break; - default: - break; + case GDK_Q: + case GDK_q: + if (desktop->quick_zoomed()) { + desktop->zoom_quick(false); + ret = TRUE; } break; - case GDK_SCROLL: - { - bool ctrl = (event->scroll.state & GDK_CONTROL_MASK); - bool wheelzooms = prefs->getBool("/options/wheelzooms/value"); - int const wheel_scroll = prefs->getIntLimited("/options/wheelscroll/value", 40, 0, 1000); - - /* shift + wheel, pan left--right */ - if (event->scroll.state & GDK_SHIFT_MASK) { - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - desktop->scroll_world(wheel_scroll, 0); - break; - case GDK_SCROLL_DOWN: - desktop->scroll_world(-wheel_scroll, 0); - break; - default: - break; - } + default: + break; + } + break; + case GDK_SCROLL: { + bool ctrl = (event->scroll.state & GDK_CONTROL_MASK); + bool wheelzooms = prefs->getBool("/options/wheelzooms/value"); + int const wheel_scroll = prefs->getIntLimited( + "/options/wheelscroll/value", 40, 0, 1000); + + /* shift + wheel, pan left--right */ + if (event->scroll.state & GDK_SHIFT_MASK) { + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + desktop->scroll_world(wheel_scroll, 0); + break; + case GDK_SCROLL_DOWN: + desktop->scroll_world(-wheel_scroll, 0); + break; + default: + break; + } - /* ctrl + wheel, zoom in--out */ - } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) { - double rel_zoom; - double const zoom_inc = prefs->getDoubleLimited("/options/zoomincrement/value", M_SQRT2, 1.01, 10); - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - rel_zoom = zoom_inc; - break; - case GDK_SCROLL_DOWN: - rel_zoom = 1 / zoom_inc; - break; - default: - rel_zoom = 0.0; - break; - } - if (rel_zoom != 0.0) { - Geom::Point const scroll_dt = desktop->point(); - desktop->zoom_relative_keep_point(scroll_dt, rel_zoom); - } + /* ctrl + wheel, zoom in--out */ + } else if ((ctrl && !wheelzooms) || (!ctrl && wheelzooms)) { + double rel_zoom; + double const zoom_inc = prefs->getDoubleLimited( + "/options/zoomincrement/value", M_SQRT2, 1.01, 10); + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + rel_zoom = zoom_inc; + break; + case GDK_SCROLL_DOWN: + rel_zoom = 1 / zoom_inc; + break; + default: + rel_zoom = 0.0; + break; + } + if (rel_zoom != 0.0) { + Geom::Point const scroll_dt = desktop->point(); + desktop->zoom_relative_keep_point(scroll_dt, rel_zoom); + } - /* no modifier, pan up--down (left--right on multiwheel mice?) */ - } else { - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - desktop->scroll_world(0, wheel_scroll); - break; - case GDK_SCROLL_DOWN: - desktop->scroll_world(0, -wheel_scroll); - break; - case GDK_SCROLL_LEFT: - desktop->scroll_world(wheel_scroll, 0); - break; - case GDK_SCROLL_RIGHT: - desktop->scroll_world(-wheel_scroll, 0); - break; - } + /* no modifier, pan up--down (left--right on multiwheel mice?) */ + } else { + switch (event->scroll.direction) { + case GDK_SCROLL_UP: + desktop->scroll_world(0, wheel_scroll); + break; + case GDK_SCROLL_DOWN: + desktop->scroll_world(0, -wheel_scroll); + break; + case GDK_SCROLL_LEFT: + desktop->scroll_world(wheel_scroll, 0); + break; + case GDK_SCROLL_RIGHT: + desktop->scroll_world(-wheel_scroll, 0); + break; } - break; } - default: - break; + break; + } + default: + break; } return ret; @@ -731,39 +729,50 @@ static gint sp_event_context_private_root_handler(SPEventContext *event_context, * Only reacts to right mouse button at the moment. * \todo Fixme: do context sensitive popup menu on items. */ -gint -sp_event_context_private_item_handler(SPEventContext *ec, SPItem *item, GdkEvent *event) -{ +gint sp_event_context_private_item_handler(SPEventContext *ec, SPItem *item, + GdkEvent *event) { int ret = FALSE; switch (event->type) { - case GDK_BUTTON_PRESS: - if ((event->button.button == 3) - && !(event->button.state & GDK_SHIFT_MASK || event->button.state & GDK_CONTROL_MASK)) { - sp_event_root_menu_popup(ec->desktop, item, event); - ret = TRUE; - } - break; - default: - break; + case GDK_BUTTON_PRESS: + if ((event->button.button == 3) && !(event->button.state + & GDK_SHIFT_MASK || event->button.state & GDK_CONTROL_MASK)) { + sp_event_root_menu_popup(ec->desktop, item, event); + ret = TRUE; + } + break; + default: + break; } return ret; } /** + * @brief: Returns true if we're hovering above a knot (needed because we don't want to pre-snap in that case) + */ + +bool sp_event_context_knot_mouseover(SPEventContext *ec) +{ + if (ec->shape_editor) { + return ec->shape_editor->knot_mouseover(); + } + + return false; +} + +/** * @brief An observer that relays pref changes to the derived classes */ -class ToolPrefObserver : public Inkscape::Preferences::Observer { +class ToolPrefObserver: public Inkscape::Preferences::Observer { public: ToolPrefObserver(Glib::ustring const &path, SPEventContext *ec) : - Inkscape::Preferences::Observer(path), - _ec(ec) {} - virtual void notify(Inkscape::Preferences::Entry const &val) - { + Inkscape::Preferences::Observer(path), _ec(ec) { + } + virtual void notify(Inkscape::Preferences::Entry const &val) { if (((SPEventContextClass *) G_OBJECT_GET_CLASS(_ec))->set) { ((SPEventContextClass *) G_OBJECT_GET_CLASS(_ec))->set(_ec, - const_cast<Inkscape::Preferences::Entry*>(&val)); + const_cast<Inkscape::Preferences::Entry*> (&val)); } } private: @@ -775,15 +784,16 @@ private: * @todo This is bogus. pref_path should be a private property of the inheriting objects. */ SPEventContext * -sp_event_context_new(GType type, SPDesktop *desktop, gchar const *pref_path, unsigned int key) -{ +sp_event_context_new(GType type, SPDesktop *desktop, gchar const *pref_path, + unsigned int key) { g_return_val_if_fail(g_type_is_a(type, SP_TYPE_EVENT_CONTEXT), NULL); g_return_val_if_fail(desktop != NULL, NULL); - SPEventContext *const ec = (SPEventContext*)g_object_new(type, NULL); + SPEventContext * const ec = (SPEventContext*) g_object_new(type, NULL); ec->desktop = desktop; - ec->_message_context = new Inkscape::MessageContext(desktop->messageStack()); + ec->_message_context + = new Inkscape::MessageContext(desktop->messageStack()); ec->key = key; ec->pref_observer = NULL; @@ -803,9 +813,7 @@ sp_event_context_new(GType type, SPDesktop *desktop, gchar const *pref_path, uns /** * Finishes SPEventContext. */ -void -sp_event_context_finish(SPEventContext *ec) -{ +void sp_event_context_finish(SPEventContext *ec) { g_return_if_fail(ec != NULL); g_return_if_fail(SP_IS_EVENT_CONTEXT(ec)); @@ -854,9 +862,7 @@ void SPEventContext::enableGrDrag(bool enable) { /** * Calls virtual set() function of SPEventContext. */ -void -sp_event_context_read(SPEventContext *ec, gchar const *key) -{ +void sp_event_context_read(SPEventContext *ec, gchar const *key) { g_return_if_fail(ec != NULL); g_return_if_fail(SP_IS_EVENT_CONTEXT(ec)); g_return_if_fail(key != NULL); @@ -864,7 +870,7 @@ sp_event_context_read(SPEventContext *ec, gchar const *key) if (((SPEventContextClass *) G_OBJECT_GET_CLASS(ec))->set) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::Preferences::Entry val = prefs->getEntry( - ec->pref_observer->observed_path + '/' + key ); + ec->pref_observer->observed_path + '/' + key); ((SPEventContextClass *) G_OBJECT_GET_CLASS(ec))->set(ec, &val); } } @@ -872,9 +878,7 @@ sp_event_context_read(SPEventContext *ec, gchar const *key) /** * Calls virtual activate() function of SPEventContext. */ -void -sp_event_context_activate(SPEventContext *ec) -{ +void sp_event_context_activate(SPEventContext *ec) { g_return_if_fail(ec != NULL); g_return_if_fail(SP_IS_EVENT_CONTEXT(ec)); @@ -890,9 +894,7 @@ sp_event_context_activate(SPEventContext *ec) /** * Calls virtual deactivate() function of SPEventContext. */ -void -sp_event_context_deactivate(SPEventContext *ec) -{ +void sp_event_context_deactivate(SPEventContext *ec) { g_return_if_fail(ec != NULL); g_return_if_fail(SP_IS_EVENT_CONTEXT(ec)); @@ -903,36 +905,38 @@ sp_event_context_deactivate(SPEventContext *ec) /** * Calls virtual root_handler(), the main event handling function. */ -gint -sp_event_context_root_handler(SPEventContext * event_context, GdkEvent * event) +gint sp_event_context_root_handler(SPEventContext * event_context, + GdkEvent * event) { switch (event->type) { - case GDK_MOTION_NOTIFY: - sp_event_context_snap_delay_handler(event_context, NULL, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER); - break; - case GDK_BUTTON_RELEASE: - if (event_context->_delayed_snap_event) { - // If we have any pending snapping action, then invoke it now - sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); - } - break; - case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: - // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. - event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - break; - default: - break; + case GDK_MOTION_NOTIFY: + sp_event_context_snap_delay_handler(event_context, NULL, NULL, + (GdkEventMotion *) event, + DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER); + break; + case GDK_BUTTON_RELEASE: + if (event_context->_delayed_snap_event) { + // If we have any pending snapping action, then invoke it now + sp_event_context_snap_watchdog_callback( + event_context->_delayed_snap_event); + } + break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + // Snapping will be on hold if we're moving the mouse at high speeds. When starting + // drawing a new shape we really should snap though. + event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally( + false); + break; + default: + break; } return sp_event_context_virtual_root_handler(event_context, event); } -gint -sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * event) -{ +gint sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * event) { gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(event_context, event); set_event_location(event_context->desktop, event); return ret; @@ -941,40 +945,36 @@ sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * /** * Calls virtual item_handler(), the item event handling function. */ -gint -sp_event_context_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) -{ +gint sp_event_context_item_handler(SPEventContext * event_context, + SPItem * item, GdkEvent * event) { switch (event->type) { - case GDK_MOTION_NOTIFY: - sp_event_context_snap_delay_handler(event_context, item, NULL, (GdkEventMotion *)event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); - break; - case GDK_BUTTON_RELEASE: - if (event_context->_delayed_snap_event) { - // If we have any pending snapping action, then invoke it now - sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); - } - break; - /*case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: - // Snapping will be on hold if we're moving the mouse at high speeds. When starting - // drawing a new shape we really should snap though. - event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); - break; - */ - default: - break; + case GDK_MOTION_NOTIFY: + sp_event_context_snap_delay_handler(event_context, (gpointer) item, NULL, (GdkEventMotion *) event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER); + break; + case GDK_BUTTON_RELEASE: + if (event_context->_delayed_snap_event) { + // If we have any pending snapping action, then invoke it now + sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event); + } + break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + // Snapping will be on hold if we're moving the mouse at high speeds. When starting + // drawing a new shape we really should snap though. + event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + break; + default: + break; } return sp_event_context_virtual_item_handler(event_context, item, event); } -gint -sp_event_context_virtual_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) -{ +gint sp_event_context_virtual_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) { gint ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler(event_context, item, event); - if (! ret) { + if (!ret) { ret = sp_event_context_virtual_root_handler(event_context, event); } else { set_event_location(event_context->desktop, event); @@ -986,8 +986,7 @@ sp_event_context_virtual_item_handler(SPEventContext * event_context, SPItem * i /** * Emits 'position_set' signal on desktop and shows coordinates on status bar. */ -static void set_event_location(SPDesktop *desktop, GdkEvent *event) -{ +static void set_event_location(SPDesktop *desktop, GdkEvent *event) { if (event->type != GDK_MOTION_NOTIFY) { return; } @@ -1002,9 +1001,7 @@ static void set_event_location(SPDesktop *desktop, GdkEvent *event) /** * Create popup menu and tell Gtk to show it. */ -void -sp_event_root_menu_popup(SPDesktop *desktop, SPItem *item, GdkEvent *event) -{ +void sp_event_root_menu_popup(SPDesktop *desktop, SPItem *item, GdkEvent *event) { GtkWidget *menu; /* fixme: This is not what I want but works for now (Lauris) */ @@ -1015,45 +1012,36 @@ sp_event_root_menu_popup(SPDesktop *desktop, SPItem *item, GdkEvent *event) gtk_widget_show(menu); switch (event->type) { - case GDK_BUTTON_PRESS: - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, 0, NULL, event->button.button, event->button.time); - break; - case GDK_KEY_PRESS: - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, 0, NULL, 0, event->key.time); - break; - default: - break; + case GDK_BUTTON_PRESS: + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, 0, NULL, + event->button.button, event->button.time); + break; + case GDK_KEY_PRESS: + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, 0, NULL, 0, event->key.time); + break; + default: + break; } } /** * Show tool context specific modifier tip. */ -void -sp_event_show_modifier_tip(Inkscape::MessageContext *message_context, +void sp_event_show_modifier_tip(Inkscape::MessageContext *message_context, GdkEvent *event, gchar const *ctrl_tip, gchar const *shift_tip, - gchar const *alt_tip) -{ + gchar const *alt_tip) { guint keyval = get_group0_keyval(&event->key); - bool ctrl = ctrl_tip && (MOD__CTRL - || (keyval == GDK_Control_L) - || (keyval == GDK_Control_R)); - bool shift = shift_tip - && (MOD__SHIFT || (keyval == GDK_Shift_L) || (keyval == GDK_Shift_R)); - bool alt = alt_tip - && (MOD__ALT - || (keyval == GDK_Alt_L) - || (keyval == GDK_Alt_R) - || (keyval == GDK_Meta_L) - || (keyval == GDK_Meta_R)); - - gchar *tip = g_strdup_printf("%s%s%s%s%s", - ( ctrl ? ctrl_tip : "" ), - ( ctrl && (shift || alt) ? "; " : "" ), - ( shift ? shift_tip : "" ), - ( (ctrl || shift) && alt ? "; " : "" ), - ( alt ? alt_tip : "" )); + bool ctrl = ctrl_tip && (MOD__CTRL || (keyval == GDK_Control_L) || (keyval + == GDK_Control_R)); + bool shift = shift_tip && (MOD__SHIFT || (keyval == GDK_Shift_L) || (keyval + == GDK_Shift_R)); + bool alt = alt_tip && (MOD__ALT || (keyval == GDK_Alt_L) || (keyval + == GDK_Alt_R) || (keyval == GDK_Meta_L) || (keyval == GDK_Meta_R)); + + gchar *tip = g_strdup_printf("%s%s%s%s%s", (ctrl ? ctrl_tip : ""), (ctrl + && (shift || alt) ? "; " : ""), (shift ? shift_tip : ""), ((ctrl + || shift) && alt ? "; " : ""), (alt ? alt_tip : "")); if (strlen(tip) > 0) { message_context->flash(Inkscape::INFORMATION_MESSAGE, tip); @@ -1069,16 +1057,12 @@ sp_event_show_modifier_tip(Inkscape::MessageContext *message_context, * Use this instead of simply event->keyval, so that your keyboard shortcuts * work regardless of layouts (e.g., in Cyrillic). */ -guint -get_group0_keyval(GdkEventKey *event) -{ +guint get_group0_keyval(GdkEventKey *event) { guint keyval = 0; - gdk_keymap_translate_keyboard_state( - gdk_keymap_get_for_display(gdk_display_get_default()), - event->hardware_keycode, - (GdkModifierType) event->state, - 0 /*event->key.group*/, - &keyval, NULL, NULL, NULL); + gdk_keymap_translate_keyboard_state(gdk_keymap_get_for_display( + gdk_display_get_default()), event->hardware_keycode, + (GdkModifierType) event->state, 0 /*event->key.group*/, &keyval, + NULL, NULL, NULL); return keyval; } @@ -1089,14 +1073,13 @@ get_group0_keyval(GdkEventKey *event) * into_groups. */ SPItem * -sp_event_context_find_item (SPDesktop *desktop, Geom::Point const &p, - bool select_under, bool into_groups) -{ +sp_event_context_find_item(SPDesktop *desktop, Geom::Point const &p, + bool select_under, bool into_groups) { SPItem *item; if (select_under) { - SPItem *selected_at_point = - desktop->item_from_list_at_point_bottom (desktop->selection->itemList(), p); + SPItem *selected_at_point = desktop->item_from_list_at_point_bottom( + desktop->selection->itemList(), p); item = desktop->item_at_point(p, into_groups, selected_at_point); if (item == NULL) { // we may have reached bottom, flip over to the top item = desktop->item_at_point(p, into_groups, NULL); @@ -1113,97 +1096,113 @@ sp_event_context_find_item (SPDesktop *desktop, Geom::Point const &p, * Honors into_groups. */ SPItem * -sp_event_context_over_item (SPDesktop *desktop, SPItem *item, Geom::Point const &p) -{ +sp_event_context_over_item(SPDesktop *desktop, SPItem *item, + Geom::Point const &p) { GSList *temp = NULL; - temp = g_slist_prepend (temp, item); - SPItem *item_at_point = desktop->item_from_list_at_point_bottom (temp, p); - g_slist_free (temp); + temp = g_slist_prepend(temp, item); + SPItem *item_at_point = desktop->item_from_list_at_point_bottom(temp, p); + g_slist_free(temp); return item_at_point; } ShapeEditor * -sp_event_context_get_shape_editor (SPEventContext *ec) -{ +sp_event_context_get_shape_editor(SPEventContext *ec) { return ec->shape_editor; } -void -event_context_print_event_info(GdkEvent *event, bool print_return) { +void event_context_print_event_info(GdkEvent *event, bool print_return) { switch (event->type) { - case GDK_BUTTON_PRESS: - g_print ("GDK_BUTTON_PRESS"); - break; - case GDK_2BUTTON_PRESS: - g_print ("GDK_2BUTTON_PRESS"); - break; - case GDK_3BUTTON_PRESS: - g_print ("GDK_3BUTTON_PRESS"); - break; - - case GDK_MOTION_NOTIFY: - g_print ("GDK_MOTION_NOTIFY"); - break; - case GDK_ENTER_NOTIFY: - g_print ("GDK_ENTER_NOTIFY"); - break; - - case GDK_LEAVE_NOTIFY: - g_print ("GDK_LEAVE_NOTIFY"); - break; - case GDK_BUTTON_RELEASE: - g_print ("GDK_BUTTON_RELEASE"); - break; - - case GDK_KEY_PRESS: - g_print ("GDK_KEY_PRESS: %d", get_group0_keyval(&event->key)); - break; - case GDK_KEY_RELEASE: - g_print ("GDK_KEY_RELEASE: %d", get_group0_keyval(&event->key)); - break; - default: - //g_print ("even type not recognized"); - break; + case GDK_BUTTON_PRESS: + g_print("GDK_BUTTON_PRESS"); + break; + case GDK_2BUTTON_PRESS: + g_print("GDK_2BUTTON_PRESS"); + break; + case GDK_3BUTTON_PRESS: + g_print("GDK_3BUTTON_PRESS"); + break; + + case GDK_MOTION_NOTIFY: + g_print("GDK_MOTION_NOTIFY"); + break; + case GDK_ENTER_NOTIFY: + g_print("GDK_ENTER_NOTIFY"); + break; + + case GDK_LEAVE_NOTIFY: + g_print("GDK_LEAVE_NOTIFY"); + break; + case GDK_BUTTON_RELEASE: + g_print("GDK_BUTTON_RELEASE"); + break; + + case GDK_KEY_PRESS: + g_print("GDK_KEY_PRESS: %d", get_group0_keyval(&event->key)); + break; + case GDK_KEY_RELEASE: + g_print("GDK_KEY_RELEASE: %d", get_group0_keyval(&event->key)); + break; + default: + //g_print ("even type not recognized"); + break; } if (print_return) { - g_print ("\n"); + g_print("\n"); } } -void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, SPKnot* const knot, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin) +/** + * \brief Analyses the current event, calculates the mouse speed, turns snapping off (temporarily) if the + * mouse speed is above a threshold, and stores the current event such that it can be re-triggered when needed + * (re-triggering is controlled by a watchdog timer) + * + * \param ec Pointer to the event context + * \param dse_item Pointer that store a reference to a canvas or to an item + * \param dse_item2 Another pointer, storing a reference to a knot or controlpoint + * \param event Pointer to the motion event + * \param origin Identifier (enum) specifying where the delay (and the call to this method) were initiated + */ +void sp_event_context_snap_delay_handler(SPEventContext *ec, + gpointer const dse_item, gpointer const dse_item2, GdkEventMotion *event, + DelayedSnapEvent::DelayedSnapEventOrigin origin) { static guint32 prev_time; static boost::optional<Geom::Point> prev_pos; + if (ec->_dse_callback_in_process) { + return; + } + // Snapping occurs when dragging with the left mouse button down, or when hovering e.g. in the pen tool with left mouse button up bool const c1 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been bool const c2 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then - // Inkscape will get stuck in an unresponsive state + // Inkscape will get stuck in an unresponsive state bool const c3 = tools_isactive(ec->desktop, TOOLS_CALLIGRAPHIC); - // The snap delay will repeat the last motion event, which will lead to - // erroneous points in the calligraphy context. And because we don't snap - // in this context, we might just as well disable the snap delay all together - + // The snap delay will repeat the last motion event, which will lead to + // erroneous points in the calligraphy context. And because we don't snap + // in this context, we might just as well disable the snap delay all together + if (c1 || c2 || c3) { // Make sure that we don't send any pending snap events to a context if we know in advance // that we're not going to snap any way (e.g. while scrolling with middle mouse button) // Any motion event might affect the state of the context, leading to unexpected behavior sp_event_context_discard_delayed_snap_event(ec); - } else if (ec->desktop && ec->desktop->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) { + } else if (ec->desktop + && ec->desktop->namedview->snap_manager.snapprefs.getSnapEnabledGlobally()) { // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred for some period. // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never // be fully at stand still and might keep spitting out motion events. ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold Geom::Point event_pos(event->x, event->y); - guint32 event_t = gdk_event_get_time ( (GdkEvent *) event ); + guint32 event_t = gdk_event_get_time((GdkEvent *) event); if (prev_pos) { Geom::Coord dist = Geom::L2(event_pos - *prev_pos); guint32 delta_t = event_t - prev_time; - gdouble speed = delta_t > 0 ? dist/delta_t : 1000; + gdouble speed = delta_t > 0 ? dist / delta_t : 1000; //std::cout << "Mouse speed = " << speed << " px/msec " << std::endl; if (speed > 0.02) { // Jitter threshold, might be needed for tablets // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We @@ -1212,7 +1211,8 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, // now, just in case there's no future motion event that drops under the speed limit (when // stopping abruptly) delete ec->_delayed_snap_event; - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); // watchdog is reset, i.e. pushed forward in time + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, + event, origin); // watchdog is reset, i.e. pushed forward in time // If the watchdog expires before a new motion event is received, we will snap (as explained // above). This means however that when the timer is too short, we will always snap and that the // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will @@ -1223,13 +1223,15 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, // snap, and set a new watchdog again. if (ec->_delayed_snap_event == NULL) { // no watchdog has been set // it might have already expired, so we'll set a new one; the snapping frequency will be limited this way - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, + dse_item2, event, origin); } // else: watchdog has been set before and we'll wait for it to expire } } else { // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog g_assert(ec->_delayed_snap_event == NULL); - ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot, event, origin); + ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2, + event, origin); } prev_pos = event_pos; @@ -1237,10 +1239,13 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, } } -gboolean sp_event_context_snap_watchdog_callback(gpointer data) -{ +/** + * \brief When the snap delay watchdog timer barks, this method will be called and will re-inject the last motion + * event in an appropriate place, with snapping being turned on again + */ +gboolean sp_event_context_snap_watchdog_callback(gpointer data) { // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated - DelayedSnapEvent *dse = reinterpret_cast<DelayedSnapEvent*>(data); + DelayedSnapEvent *dse = reinterpret_cast<DelayedSnapEvent*> (data); if (dse == NULL) { // This might occur when this method is called directly, i.e. not through the timer @@ -1252,57 +1257,89 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) if (ec == NULL || ec->desktop == NULL) { return false; } + ec->_dse_callback_in_process = true; SPDesktop *dt = ec->desktop; dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); + // Depending on where the delayed snap event originated from, we will inject it back at it's origin + // The switch below takes care of that and prepares the relevant parameters switch (dse->getOrigin()) { - case DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER: - sp_event_context_virtual_root_handler(ec, dse->getEvent()); - break; - case DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER: - { - SPItem* item = NULL; - item = dse->getItem(); - if (item && SP_IS_ITEM(item)) { - sp_event_context_virtual_item_handler(ec, item, dse->getEvent()); - } - } - break; - case DelayedSnapEvent::KNOT_HANDLER: - { - SPKnot* knot = dse->getKnot(); - if (knot && SP_IS_KNOT(knot)) { - sp_knot_handler_request_position(dse->getEvent(), knot); - } + case DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER: + sp_event_context_virtual_root_handler(ec, dse->getEvent()); + break; + case DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER: { + SPItem* item = NULL; + item = SP_ITEM(dse->getItem()); + if (item && SP_IS_ITEM(item)) { + sp_event_context_virtual_item_handler(ec, item, dse->getEvent()); + } + } + break; + case DelayedSnapEvent::KNOT_HANDLER: { + SPKnot* knot = SP_KNOT(dse->getItem2()); + if (knot && SP_IS_KNOT(knot)) { + sp_knot_handler_request_position(dse->getEvent(), knot); + } + } + break; + case DelayedSnapEvent::CONTROL_POINT_HANDLER: { + using Inkscape::UI::ControlPoint; + ControlPoint *point = reinterpret_cast<ControlPoint*> (dse->getItem2()); + point->_eventHandler(dse->getEvent()); + } + break; + case DelayedSnapEvent::GUIDE_HANDLER: { + gpointer item = dse->getItem(); + gpointer item2 = dse->getItem2(); + if (item && item2) { + g_assert(SP_IS_CANVAS_ITEM(item)); + g_assert(SP_IS_GUIDE(item2)); + sp_dt_guide_event(SP_CANVAS_ITEM(item), dse->getEvent(), item2); + } + } + break; + case DelayedSnapEvent::GUIDE_HRULER: + case DelayedSnapEvent::GUIDE_VRULER: { + gpointer item = dse->getItem(); + gpointer item2 = dse->getItem2(); + if (item && item2) { + g_assert(GTK_IS_WIDGET(item)); + g_assert(SP_IS_DESKTOP_WIDGET(item2)); + if (dse->getOrigin() == DelayedSnapEvent::GUIDE_HRULER) { + sp_dt_hruler_event(GTK_WIDGET(item), dse->getEvent(), SP_DESKTOP_WIDGET(item2)); + } else { + sp_dt_vruler_event(GTK_WIDGET(item), dse->getEvent(), SP_DESKTOP_WIDGET(item2)); } - break; - default: - g_warning("Origin of snap-delay event has not been defined!;"); - break; + } + } + break; + default: + g_warning("Origin of snap-delay event has not been defined!;"); + break; } ec->_delayed_snap_event = NULL; delete dse; + ec->_dse_callback_in_process = false; + return FALSE; //Kills the timer and stops it from executing this callback over and over again. } -void sp_event_context_discard_delayed_snap_event(SPEventContext *ec) -{ +void sp_event_context_discard_delayed_snap_event(SPEventContext *ec) { delete ec->_delayed_snap_event; ec->_delayed_snap_event = NULL; + ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false); } - - /* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: + */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/event-context.h b/src/event-context.h index 5285bdb87..76c74e26c 100644 --- a/src/event-context.h +++ b/src/event-context.h @@ -45,44 +45,48 @@ void sp_event_context_discard_delayed_snap_event(SPEventContext *ec); class DelayedSnapEvent { public: - enum DelayedSnapEventOrigin { - UNDEFINED_HANDLER = 0, - EVENTCONTEXT_ROOT_HANDLER, - EVENTCONTEXT_ITEM_HANDLER, - KNOT_HANDLER - }; - - DelayedSnapEvent(SPEventContext *event_context, SPItem* const item, SPKnot* knot, GdkEventMotion const *event, DelayedSnapEvent::DelayedSnapEventOrigin const origin) - : _timer_id(0), _event(NULL), _item(item), _knot(knot), _origin(origin), _event_context(event_context) - { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000); - _timer_id = g_timeout_add(value, &sp_event_context_snap_watchdog_callback, this); - _event = gdk_event_copy((GdkEvent*) event); - ((GdkEventMotion *)_event)->time = GDK_CURRENT_TIME; - } - - ~DelayedSnapEvent() { - if (_timer_id > 0) g_source_remove(_timer_id); // Kill the watchdog - if (_event != NULL) gdk_event_free(_event); // Remove the copy of the original event - } - - SPEventContext* getEventContext() {return _event_context;} - DelayedSnapEventOrigin getOrigin() {return _origin;} - GdkEvent* getEvent() {return _event;} - SPItem* getItem() {return _item;} - SPKnot* getKnot() {return _knot;} + enum DelayedSnapEventOrigin { + UNDEFINED_HANDLER = 0, + EVENTCONTEXT_ROOT_HANDLER, + EVENTCONTEXT_ITEM_HANDLER, + KNOT_HANDLER, + CONTROL_POINT_HANDLER, + GUIDE_HANDLER, + GUIDE_HRULER, + GUIDE_VRULER + }; + + DelayedSnapEvent(SPEventContext *event_context, gpointer const dse_item, gpointer dse_item2, GdkEventMotion const *event, DelayedSnapEvent::DelayedSnapEventOrigin const origin) + : _timer_id(0), _event(NULL), _item(dse_item), _item2(dse_item2), _origin(origin), _event_context(event_context) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000); + _timer_id = g_timeout_add(value, &sp_event_context_snap_watchdog_callback, this); + _event = gdk_event_copy((GdkEvent*) event); + ((GdkEventMotion *)_event)->time = GDK_CURRENT_TIME; + } + + ~DelayedSnapEvent() { + if (_timer_id > 0) g_source_remove(_timer_id); // Kill the watchdog + if (_event != NULL) gdk_event_free(_event); // Remove the copy of the original event + } + + SPEventContext* getEventContext() {return _event_context;} + DelayedSnapEventOrigin getOrigin() {return _origin;} + GdkEvent* getEvent() {return _event;} + gpointer getItem() {return _item;} + gpointer getItem2() {return _item2;} private: - guint _timer_id; - GdkEvent* _event; - SPItem* _item; - SPKnot* _knot; - DelayedSnapEventOrigin _origin; - SPEventContext* _event_context; + guint _timer_id; + GdkEvent* _event; + gpointer _item; + gpointer _item2; + DelayedSnapEventOrigin _origin; + SPEventContext* _event_context; }; -void sp_event_context_snap_delay_handler(SPEventContext *ec, SPItem* const item, SPKnot* const knot, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin); +void sp_event_context_snap_delay_handler(SPEventContext *ec, gpointer const dse_item, gpointer const dse_item2, GdkEventMotion *event, DelayedSnapEvent::DelayedSnapEventOrigin origin); /** * Base class for Event processors. @@ -123,6 +127,7 @@ struct SPEventContext : public GObject { bool space_panning; DelayedSnapEvent *_delayed_snap_event; + bool _dse_callback_in_process; }; /** @@ -170,6 +175,7 @@ SPItem *sp_event_context_find_item (SPDesktop *desktop, Geom::Point const &p, bo SPItem *sp_event_context_over_item (SPDesktop *desktop, SPItem *item, Geom::Point const &p); ShapeEditor *sp_event_context_get_shape_editor (SPEventContext *ec); +bool sp_event_context_knot_mouseover(SPEventContext *ec); void ec_shape_event_attr_changed(Inkscape::XML::Node *shape_repr, gchar const *name, gchar const *old_value, gchar const *new_value, diff --git a/src/extension/dxf2svg/entities2elements.cpp b/src/extension/dxf2svg/entities2elements.cpp index ab160e265..f280bb2a8 100644 --- a/src/extension/dxf2svg/entities2elements.cpp +++ b/src/extension/dxf2svg/entities2elements.cpp @@ -20,10 +20,12 @@ SoC 2005 */ -#include"entities2elements.h" -#include"tables2svg_info.h" -#include<iostream> -#include<math.h> +#include "entities2elements.h" +#include "tables2svg_info.h" +#include <iostream> +#include <math.h> +#include <string.h> +#include <stdlib.h> // The names indicate the DXF entitiy first and the SVG element last // Common elements diff --git a/src/extension/dxf2svg/read_dxf.cpp b/src/extension/dxf2svg/read_dxf.cpp index 8a6a6d6ac..ecda343c6 100644 --- a/src/extension/dxf2svg/read_dxf.cpp +++ b/src/extension/dxf2svg/read_dxf.cpp @@ -11,11 +11,13 @@ -#include<fstream> -#include<string> -#include"read_dxf.h" +#include <fstream> +#include <string> +#include "read_dxf.h" -#include<iostream> +#include <iostream> +#include <string.h> +#include <stdlib.h> using namespace std; diff --git a/src/extension/dxf2svg/tables2svg_info.cpp b/src/extension/dxf2svg/tables2svg_info.cpp index 17bc47beb..3b27a9c38 100644 --- a/src/extension/dxf2svg/tables2svg_info.cpp +++ b/src/extension/dxf2svg/tables2svg_info.cpp @@ -10,9 +10,11 @@ */ -#include"tables2svg_info.h" -#include<math.h> -#include<iostream> +#include "tables2svg_info.h" +#include <math.h> +#include <iostream> +#include <stdlib.h> +#include <string.h> char* pattern2dasharray(ltype info, int precision, double scaling, char* out){ std::vector< double > pattern = info.ret_pattern(); diff --git a/src/extension/execution-env.cpp b/src/extension/execution-env.cpp index e8d7c4baf..7ed389195 100644 --- a/src/extension/execution-env.cpp +++ b/src/extension/execution-env.cpp @@ -58,7 +58,7 @@ ExecutionEnv::ExecutionEnv (Effect * effect, Inkscape::UI::View::View * doc, Imp sp_desktop_selection(desktop)->itemList(); while ( selected != NULL ) { Glib::ustring selected_id; - selected_id = SP_OBJECT_ID(*selected); + selected_id = (*selected)->getId(); _selected.insert(_selected.end(), selected_id); //std::cout << "Selected: " << selected_id << std::endl; ++selected; @@ -127,7 +127,7 @@ ExecutionEnv::createWorkingDialog (void) { SPDesktop *desktop = (SPDesktop *)_doc; GtkWidget *toplevel = gtk_widget_get_toplevel(&(desktop->canvas->widget)); - if (!toplevel || !GTK_WIDGET_TOPLEVEL (toplevel)) + if (!toplevel || !GTK_WIDGET_TOPLEVEL (toplevel)) return; Gtk::Window *window = Glib::wrap(GTK_WINDOW(toplevel), false); diff --git a/src/extension/extension.cpp b/src/extension/extension.cpp index 52d5f5148..b4cf06bda 100644 --- a/src/extension/extension.cpp +++ b/src/extension/extension.cpp @@ -57,7 +57,9 @@ Parameter * param_shared (const gchar * name, GSList * list); not related to the module directly. If the Repr does not include a name and an ID the module will be left in an errored state. */ -Extension::Extension (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) : _help(NULL) +Extension::Extension (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp) + : _help(NULL) + , _gui(true) { repr = in_repr; Inkscape::GC::anchor(in_repr); @@ -438,6 +440,14 @@ Extension::get_param_enum (const gchar * name, const SPDocument * doc, const Ink return param->get_enum(doc, node); } + +gchar const *Extension::get_param_optiongroup( gchar const * name, SPDocument const * doc, Inkscape::XML::Node const * node) +{ + Parameter* param = param_shared(name, parameters); + return param->get_optiongroup(doc, node); +} + + /** \return The value of the parameter identified by the name \brief Gets a parameter identified by name with the bool placed @@ -595,6 +605,13 @@ Extension::set_param_string (const gchar * name, const gchar * value, SPDocument return param->set_string(value, doc, node); } +gchar const * Extension::set_param_optiongroup(gchar const * name, gchar const * value, SPDocument * doc, Inkscape::XML::Node * node) +{ + Parameter * param = param_shared(name, parameters); + return param->set_optiongroup(value, doc, node); +} + + /** \return The passed in value \brief Sets a parameter identified by name with the string @@ -673,7 +690,7 @@ public: Gtk::Widget * Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) { - if (param_visible_count() == 0) return NULL; + if (!_gui || param_visible_count() == 0) return NULL; AutoGUI * agui = Gtk::manage(new AutoGUI()); diff --git a/src/extension/extension.h b/src/extension/extension.h index 48ca86cf7..c71ae59b4 100644 --- a/src/extension/extension.h +++ b/src/extension/extension.h @@ -99,6 +99,7 @@ private: state_t _state; /**< Which state the Extension is currently in */ std::vector<Dependency *> _deps; /**< Dependencies for this extension */ static std::ofstream error_file; /**< This is the place where errors get reported */ + bool _gui; protected: Inkscape::XML::Node *repr; /**< The XML description of the Extension */ @@ -138,8 +139,8 @@ public: g_slist_length(parameters); }; /** \brief A function to get the the number of parameters that are visible to the user that the extension has. - \return The number of visible parameters. - + \return The number of visible parameters. + \note Currently this just calls param_count as visible isn't implemented but in the future it'll do something different. Please call the appropriate function in code so that it'll work in the @@ -152,16 +153,17 @@ public: class param_wrong_type {}; class param_not_color_param {}; class param_not_enum_param {}; + class param_not_optiongroup_param {}; class param_not_string_param {}; class param_not_float_param {}; class param_not_int_param {}; class param_not_bool_param {}; - - /** An error class for when a parameter is looked for that just + + /** An error class for when a parameter is looked for that just * simply doesn't exist */ class param_not_exist {}; - - /** An error class for when a filename already exists, but the user + + /** An error class for when a filename already exists, but the user * doesn't want to overwrite it */ class no_overwrite {}; @@ -175,37 +177,56 @@ public: bool get_param_bool (const gchar * name, const SPDocument * doc = NULL, const Inkscape::XML::Node * node = NULL); + int get_param_int (const gchar * name, const SPDocument * doc = NULL, const Inkscape::XML::Node * node = NULL); + float get_param_float (const gchar * name, const SPDocument * doc = NULL, const Inkscape::XML::Node * node = NULL); + const gchar * get_param_string (const gchar * name, const SPDocument * doc = NULL, const Inkscape::XML::Node * node = NULL); + guint32 get_param_color (const gchar * name, const SPDocument * doc = NULL, const Inkscape::XML::Node * node = NULL); + const gchar * get_param_enum (const gchar * name, const SPDocument * doc = NULL, const Inkscape::XML::Node * node = NULL); + + gchar const *get_param_optiongroup( gchar const * name, + SPDocument const * doc = 0, + Inkscape::XML::Node const * node = 0); + bool set_param_bool (const gchar * name, bool value, SPDocument * doc = NULL, Inkscape::XML::Node * node = NULL); + int set_param_int (const gchar * name, int value, SPDocument * doc = NULL, Inkscape::XML::Node * node = NULL); + float set_param_float (const gchar * name, float value, SPDocument * doc = NULL, Inkscape::XML::Node * node = NULL); + const gchar * set_param_string (const gchar * name, const gchar * value, SPDocument * doc = NULL, Inkscape::XML::Node * node = NULL); + + gchar const * set_param_optiongroup(gchar const * name, + gchar const * value, + SPDocument * doc = 0, + Inkscape::XML::Node * node = 0); + guint32 set_param_color (const gchar * name, guint32 color, SPDocument * doc = NULL, @@ -219,6 +240,7 @@ public: public: Gtk::Widget * autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal = NULL); void paramListString (std::list <std::string> & retlist); + void set_gui(bool s) { _gui = s; } /* Extension editor dialog stuff */ public: diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index 5f1bef8d1..60623191f 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -5,6 +5,7 @@ * Authors: * Bryce Harrington <bryce@osdl.org> * Ted Gould <ted@gould.cx> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2002-2005,2007 Authors * @@ -64,13 +65,13 @@ namespace Implementation { /** \brief Make GTK+ events continue to come through a little bit - This just keeps coming the events through so that we'll make the GUI - update and look pretty. + This just keeps coming the events through so that we'll make the GUI + update and look pretty. */ -void -Script::pump_events (void) { - while( Gtk::Main::events_pending() ) +void Script::pump_events (void) { + while ( Gtk::Main::events_pending() ) { Gtk::Main::iteration(); + } return; } @@ -96,17 +97,15 @@ Script::interpreter_t const Script::interpreterTab[] = { /** \brief Look up an interpreter name, and translate to something that - is executable + is executable \param interpNameArg The name of the interpreter that we're looking - for, should be an entry in interpreterTab + for, should be an entry in interpreterTab */ -Glib::ustring -Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) +Glib::ustring Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) { - Glib::ustring interpName = interpNameArg; - interpreter_t const *interp; + interpreter_t const *interp = 0; bool foundInterp = false; for (interp = interpreterTab ; interp->identity ; interp++ ){ if (interpName == interp->identity) { @@ -116,8 +115,9 @@ Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) } // Do we have a supported interpreter type? - if (!foundInterp) + if (!foundInterp) { return ""; + } interpName = interp->defaultval; // 1. Check preferences @@ -137,6 +137,7 @@ Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) Glib::ustring path; Glib::ustring exeName; if (rt.getExeInfo(fullPath, path, exeName)) { +// TODO replace with proper glib/glibmm path building routines: Glib::ustring interpPath = path; interpPath.append("\\"); interpPath.append(interpNameArg); @@ -144,7 +145,7 @@ Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) interpPath.append(interpName); interpPath.append(".exe"); struct stat finfo; - if (stat(interpPath .c_str(), &finfo) ==0) { + if (stat(interpPath .c_str(), &finfo) == 0) { g_message("Found local interpreter, '%s', Size: %d", interpPath .c_str(), (int)finfo.st_size); @@ -153,12 +154,11 @@ Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg) } // 3. Try searching the path - char szExePath[MAX_PATH]; - char szCurrentDir[MAX_PATH]; + char szExePath[MAX_PATH] = {0}; + char szCurrentDir[MAX_PATH] = {0}; GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir); - unsigned int ret = (unsigned int)FindExecutable( - interpName.c_str(), szCurrentDir, szExePath); - if (ret > 32) { + HINSTANCE ret = FindExecutable(interpName.c_str(), szCurrentDir, szExePath); + if (ret > reinterpret_cast<HINSTANCE>(32)) { interpName = szExePath; return interpName; } @@ -231,9 +231,9 @@ Script::solve_reldir(Inkscape::XML::Node *reprin) { Glib::ustring filename = fname; g_free(fname); - if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) + if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) { return filename; - + } } } else { Glib::ustring str = sp_repr_children(reprin)->content(); @@ -261,8 +261,7 @@ Script::solve_reldir(Inkscape::XML::Node *reprin) { then a TRUE is returned. If we get all the way through the path then a FALSE is returned, the command could not be found. */ -bool -Script::check_existance(const Glib::ustring &command) +bool Script::check_existance(const Glib::ustring &command) { // Check the simple case first @@ -272,21 +271,23 @@ Script::check_existance(const Glib::ustring &command) //Don't search when it contains a slash. */ if (command.find(G_DIR_SEPARATOR) != command.npos) { - if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS)) + if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS)) { return true; - else + } else { return false; + } } Glib::ustring path; gchar *s = (gchar *) g_getenv("PATH"); - if (s) + if (s) { path = s; - else + } else { /* There is no `PATH' in the environment. The default search path is the current directory */ path = G_SEARCHPATH_SEPARATOR_S; + } std::string::size_type pos = 0; std::string::size_type pos2 = 0; @@ -339,11 +340,11 @@ Script::check_existance(const Glib::ustring &command) and should error out at a higher level. */ -bool -Script::load(Inkscape::Extension::Extension *module) +bool Script::load(Inkscape::Extension::Extension *module) { - if (module->loaded()) + if (module->loaded()) { return true; + } helper_extension = ""; @@ -392,8 +393,7 @@ Script::load(Inkscape::Extension::Extension *module) This function just sets the module to unloaded. It free's the command if it has been allocated. */ -void -Script::unload(Inkscape::Extension::Extension */*module*/) +void Script::unload(Inkscape::Extension::Extension */*module*/) { command.clear(); helper_extension = ""; @@ -411,11 +411,11 @@ Script::unload(Inkscape::Extension::Extension */*module*/) bool Script::check(Inkscape::Extension::Extension *module) { - int script_count = 0; + int script_count = 0; Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr()); while (child_repr != NULL) { if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) { - script_count++; + script_count++; child_repr = sp_repr_children(child_repr); while (child_repr != NULL) { if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) { @@ -443,9 +443,9 @@ Script::check(Inkscape::Extension::Extension *module) child_repr = sp_repr_next(child_repr); } - if (script_count == 0) { - return false; - } + if (script_count == 0) { + return false; + } return true; } @@ -488,8 +488,7 @@ ScriptDocCache::~ScriptDocCache ( ) unlink(_filename.c_str()); } -ImplementationDocumentCache * -Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) { +ImplementationDocumentCache *Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) { return new ScriptDocCache(view); } @@ -502,8 +501,7 @@ Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::Vie This function should really do something, right now it doesn't. */ -Gtk::Widget * -Script::prefs_input(Inkscape::Extension::Input *module, +Gtk::Widget *Script::prefs_input(Inkscape::Extension::Input *module, const gchar */*filename*/) { return module->autogui(NULL, NULL); @@ -518,8 +516,7 @@ Script::prefs_input(Inkscape::Extension::Input *module, This function should really do something, right now it doesn't. */ -Gtk::Widget * -Script::prefs_output(Inkscape::Extension::Output *module) +Gtk::Widget *Script::prefs_output(Inkscape::Extension::Output *module) { return module->autogui(NULL, NULL); } @@ -545,8 +542,7 @@ Script::prefs_output(Inkscape::Extension::Output *module) the incoming filename (so that it's not the temporary filename). That document is then returned from this function. */ -SPDocument * -Script::open(Inkscape::Extension::Input *module, +SPDocument *Script::open(Inkscape::Extension::Input *module, const gchar *filenameArg) { std::list<std::string> params; @@ -621,8 +617,7 @@ Script::open(Inkscape::Extension::Input *module, put the output of the script into the final output file. We then delete the temporary file. */ -void -Script::save(Inkscape::Extension::Output *module, +void Script::save(Inkscape::Extension::Output *module, SPDocument *doc, const gchar *filenameArg) { @@ -662,7 +657,7 @@ Script::save(Inkscape::Extension::Output *module, // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name unlink(tempfilename_in.c_str()); - if(success == false) { + if (success == false) { throw Inkscape::Extension::Output::save_failed(); } @@ -699,8 +694,7 @@ Script::save(Inkscape::Extension::Output *module, exists at the time, the other is created by that script). At that point both should be full, and the second one is loaded. */ -void -Script::effect(Inkscape::Extension::Effect *module, +void Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc, ImplementationDocumentCache * docCache) { @@ -745,7 +739,7 @@ Script::effect(Inkscape::Extension::Effect *module, while ( selected != NULL ) { Glib::ustring selected_id; selected_id += "--id="; - selected_id += SP_OBJECT_ID(*selected); + selected_id += (*selected)->getId(); params.insert(params.begin(), selected_id); ++selected; } @@ -801,8 +795,7 @@ Script::effect(Inkscape::Extension::Effect *module, elements and putting them into the old document. The copy is then complete. */ -void -Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) +void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) { std::vector<Inkscape::XML::Node *> delete_list; Inkscape::XML::Node * oldroot_namedview = NULL; @@ -821,8 +814,9 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) delete_list.push_back(child); } } - for (unsigned int i = 0; i < delete_list.size(); i++) + for (unsigned int i = 0; i < delete_list.size(); i++) { sp_repr_unparent(delete_list[i]); + } for (Inkscape::XML::Node * child = newroot->firstChild(); child != NULL; @@ -851,8 +845,9 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) } // Delete the attributes of the old root nodes. - for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++) + for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++) { oldroot->setAttribute(*it, NULL); + } // Set the new attributes. for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) { @@ -869,8 +864,7 @@ Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot) shows it in a warning dialog to the user \param filename Filename of the stderr file */ -void -Script::checkStderr (const Glib::ustring &data, +void Script::checkStderr (const Glib::ustring &data, Gtk::MessageType type, const Glib::ustring &message) { @@ -902,8 +896,7 @@ Script::checkStderr (const Glib::ustring &data, return; } -bool -Script::cancelProcessing (void) { +bool Script::cancelProcessing (void) { _canceled = true; _main_loop->quit(); Glib::spawn_close_pid(_pid); @@ -939,8 +932,7 @@ Script::cancelProcessing (void) { At the very end (after the data has been copied) both of the files are closed, and we return to what we were doing. */ -int -Script::execute (const std::list<std::string> &in_command, +int Script::execute (const std::list<std::string> &in_command, const std::list<std::string> &in_params, const Glib::ustring &filein, file_listener &fileout) @@ -968,52 +960,51 @@ Script::execute (const std::list<std::string> &in_command, //std::cout << "first space " << first_space << std::endl; //std::cout << "first quote " << first_quote << std::endl; - if((first_quote != std::string::npos) && (first_quote == 0)) { + if ((first_quote != std::string::npos) && (first_quote == 0)) { size_t next_quote = param_str.find_first_of('"', first_quote + 1); //std::cout << "next quote " << next_quote << std::endl; - if(next_quote != std::string::npos) { + if (next_quote != std::string::npos) { //std::cout << "now split " << next_quote << std::endl; //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl; //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl; std::string part_str = param_str.substr(1, next_quote - 1); - if(part_str.size() > 0) + if (part_str.size() > 0) argv.push_back(part_str); param_str = param_str.substr(next_quote + 1); - } - else { - if(param_str.size() > 0) + } else { + if (param_str.size() > 0) argv.push_back(param_str); param_str = ""; } - } - else if(first_space != std::string::npos) { + } else if (first_space != std::string::npos) { //std::cout << "now split " << first_space << std::endl; //std::cout << "now split " << param_str.substr(0, first_space) << std::endl; //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl; std::string part_str = param_str.substr(0, first_space); - if(part_str.size() > 0) + if (part_str.size() > 0) { argv.push_back(part_str); + } param_str = param_str.substr(first_space + 1); - } - else { - if(param_str.size() > 0) + } else { + if (param_str.size() > 0) { argv.push_back(param_str); + } param_str = ""; } - } while(param_str.size() > 0); + } while (param_str.size() > 0); } for (std::list<std::string>::const_iterator i = in_params.begin(); i != in_params.end(); i++) { - //g_message("Script parameter: %s",(*i)g.c_str()); + //g_message("Script parameter: %s",(*i)g.c_str()); argv.push_back(*i); } if (!(filein.empty())) { - argv.push_back(filein); + argv.push_back(filein); } int stdout_pipe, stderr_pipe; @@ -1042,10 +1033,12 @@ Script::execute (const std::list<std::string> &in_command, _main_loop->run(); // Ensure all the data is out of the pipe - while (!fileout.isDead()) + while (!fileout.isDead()) { fileout.read(Glib::IO_IN); - while (!fileerr.isDead()) + } + while (!fileerr.isDead()) { fileerr.read(Glib::IO_IN); + } if (_canceled) { // std::cout << "Script Canceled" << std::endl; diff --git a/src/extension/implementation/script.h b/src/extension/implementation/script.h index 8e25fb351..e0b6701bf 100644 --- a/src/extension/implementation/script.h +++ b/src/extension/implementation/script.h @@ -10,8 +10,8 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#ifndef __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ -#define __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ +#ifndef INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H_SEEN +#define INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H_SEEN #include "implementation.h" #include <gtkmm/messagedialog.h> @@ -62,7 +62,7 @@ public: */ virtual bool check(Inkscape::Extension::Extension *module); - ImplementationDocumentCache * newDocCache (Inkscape::Extension::Extension * ext, Inkscape::UI::View::View * view); + ImplementationDocumentCache * newDocCache(Inkscape::Extension::Extension * ext, Inkscape::UI::View::View * view); /** * @@ -93,7 +93,7 @@ public: */ virtual void effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc, - ImplementationDocumentCache * docCache); + ImplementationDocumentCache * docCache); virtual bool cancelProcessing (void); @@ -204,18 +204,18 @@ private: const Glib::ustring &filein, file_listener &fileout); - void pump_events (void); + void pump_events(void); - /** \brief A definition of an interpreter, which can be specified - in the INX file, but we need to know what to call */ - struct interpreter_t { - gchar const *identity; /**< The ID that is in the INX file */ - gchar const *prefstring; /**< The preferences key that can override the default */ - gchar const *defaultval; /**< The default value if there are no preferences */ - }; + /** \brief A definition of an interpreter, which can be specified + in the INX file, but we need to know what to call */ + struct interpreter_t { + gchar const *identity; /**< The ID that is in the INX file */ + gchar const *prefstring; /**< The preferences key that can override the default */ + gchar const *defaultval; /**< The default value if there are no preferences */ + }; static interpreter_t const interpreterTab[]; - Glib::ustring resolveInterpreterExecutable(const Glib::ustring &interpNameArg); + Glib::ustring resolveInterpreterExecutable(const Glib::ustring &interpNameArg); }; // class Script @@ -227,7 +227,7 @@ private: } // namespace Extension } // namespace Inkscape -#endif /* __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ */ +#endif // INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H_SEEN /* Local Variables: diff --git a/src/extension/init.cpp b/src/extension/init.cpp index dc39ea430..f58c8cbe6 100644 --- a/src/extension/init.cpp +++ b/src/extension/init.cpp @@ -127,10 +127,10 @@ update_pref(Glib::ustring const &pref_path, /* gboolean missing=TRUE; for (GSList *list = extension_family; list; list = g_slist_next(list)) { - g_assert( list->data ); + g_assert( list->data ); - Inkscape::Extension *extension; - extension = reinterpret_cast<Inkscape::Extension *>(list->data); + Inkscape::Extension *extension; + extension = reinterpret_cast<Inkscape::Extension *>(list->data); if (!strcmp(extension->get_id(),pref)) missing=FALSE; } @@ -231,14 +231,14 @@ init() Internal::Bitmap::Wave::init(); #endif /* WITH_IMAGE_MAGICK */ - Internal::Filter::Filter::filters_all(); + Internal::Filter::Filter::filters_all(); /* Load search path for extensions */ if (Inkscape::Extension::Extension::search_path.size() == 0) { - Inkscape::Extension::Extension::search_path.push_back(profile_path("extensions")); - - Inkscape::Extension::Extension::search_path.push_back(g_strdup(INKSCAPE_EXTENSIONDIR)); + Inkscape::Extension::Extension::search_path.push_back(profile_path("extensions")); + + Inkscape::Extension::Extension::search_path.push_back(g_strdup(INKSCAPE_EXTENSIONDIR)); } diff --git a/src/extension/input.h b/src/extension/input.h index 55d807ce2..24cbc4896 100644 --- a/src/extension/input.h +++ b/src/extension/input.h @@ -11,6 +11,7 @@ #ifndef INKSCAPE_EXTENSION_INPUT_H__ #define INKSCAPE_EXTENSION_INPUT_H__ +#include <exception> #include <glib.h> #include "extension.h" #include "xml/repr.h" @@ -30,8 +31,14 @@ public: /* this is a hack for this release, this will be private shortly */ gchar *output_extension; /**< Setting of what output extension should be used */ public: - class open_failed {}; /**< Generic failure for an undescribed reason */ - class no_extension_found {}; /**< Failed because we couldn't find an extension to match the filename */ + struct open_failed : public std::exception { + virtual ~open_failed() throw() {} + const char *what() const throw() { return "Open failed"; } + }; + struct no_extension_found : public std::exception { + virtual ~no_extension_found() throw() {} + const char *what() const throw() { return "No suitable input extension found"; } + }; Input (Inkscape::XML::Node * in_repr, Implementation::Implementation * in_imp); diff --git a/src/extension/internal/CMakeLists.txt b/src/extension/internal/CMakeLists.txt index c9c00e05b..8b23cb0ac 100644 --- a/src/extension/internal/CMakeLists.txt +++ b/src/extension/internal/CMakeLists.txt @@ -20,6 +20,7 @@ latex-pstricks.cpp latex-pstricks-out.cpp odf.cpp pdfinput +latex-text-renderer.cpp pdf-input-cairo.cpp pov-out.cpp javafx-out.cpp diff --git a/src/extension/internal/Makefile_insert b/src/extension/internal/Makefile_insert index d2ba9b3eb..3c1ce7f43 100644 --- a/src/extension/internal/Makefile_insert +++ b/src/extension/internal/Makefile_insert @@ -109,6 +109,8 @@ ink_common_sources += \ extension/internal/javafx-out.h \ extension/internal/gdkpixbuf-input.h \ extension/internal/gdkpixbuf-input.cpp \ + extension/internal/latex-text-renderer.h \ + extension/internal/latex-text-renderer.cpp \ extension/internal/pdfinput/svg-builder.h \ extension/internal/pdfinput/svg-builder.cpp \ extension/internal/pdfinput/pdf-parser.h \ diff --git a/src/extension/internal/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp index 737bb2885..61760e9d9 100644 --- a/src/extension/internal/cairo-ps-out.cpp +++ b/src/extension/internal/cairo-ps-out.cpp @@ -21,6 +21,7 @@ #include "cairo-ps-out.h" #include "cairo-render-context.h" #include "cairo-renderer.h" +#include "latex-text-renderer.h" #include <print.h> #include "extension/system.h" #include "extension/print.h" @@ -61,7 +62,8 @@ bool CairoEpsOutput::check (Inkscape::Extension::Extension * /*module*/) } static bool -ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, bool eps = false) +ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, bool texttopath, bool omittext, + bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas, bool eps = false) { sp_document_ensure_up_to_date(doc); @@ -93,6 +95,7 @@ ps_print_document_to_file(SPDocument *doc, gchar const *filename, unsigned int l ctx->setPSLevel(level); ctx->setEPS(eps); ctx->setTextToPath(texttopath); + renderer->_omitText = omittext; ctx->setFilterToBitmap(filtertobitmap); ctx->setBitmapResolution(resolution); @@ -146,6 +149,14 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con new_textToPath = mod->get_param_bool("textToPath"); } catch(...) {} + bool new_textToLaTeX = FALSE; + try { + new_textToLaTeX = mod->get_param_bool("textToLaTeX"); + } + catch(...) { + g_warning("Parameter <textToLaTeX> might not exist"); + } + bool new_blurToBitmap = FALSE; try { new_blurToBitmap = mod->get_param_bool("blurToBitmap"); @@ -171,13 +182,29 @@ CairoPsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar con new_exportId = mod->get_param_string("exportId"); } catch(...) {} - gchar * final_name; - final_name = g_strdup_printf("> %s", filename); - ret = ps_print_document_to_file(doc, final_name, level, new_textToPath, new_blurToBitmap, new_bitmapResolution, new_exportId, new_areaDrawing, new_areaPage); - g_free(final_name); + // Create PS + { + gchar * final_name; + final_name = g_strdup_printf("> %s", filename); + ret = ps_print_document_to_file(doc, final_name, level, new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, new_exportId, new_areaDrawing, new_areaPage); + g_free(final_name); - if (!ret) - throw Inkscape::Extension::Output::save_failed(); + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } + + // Create LaTeX file (if requested) + if (new_textToLaTeX) { + gchar * tex_filename; + //strip filename of ".ps", do not add ".tex" here. + gsize n = g_str_has_suffix(filename, ".ps") ? strlen(filename)-3 : strlen(filename); + tex_filename = g_strndup(filename, n); + ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_areaDrawing, new_areaPage, false); + g_free(tex_filename); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } } @@ -210,6 +237,14 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co new_textToPath = mod->get_param_bool("textToPath"); } catch(...) {} + bool new_textToLaTeX = FALSE; + try { + new_textToLaTeX = mod->get_param_bool("textToLaTeX"); + } + catch(...) { + g_warning("Parameter <textToLaTeX> might not exist"); + } + bool new_blurToBitmap = FALSE; try { new_blurToBitmap = mod->get_param_bool("blurToBitmap"); @@ -235,13 +270,29 @@ CairoEpsOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar co new_exportId = mod->get_param_string("exportId"); } catch(...) {} - gchar * final_name; - final_name = g_strdup_printf("> %s", filename); - ret = ps_print_document_to_file(doc, final_name, level, new_textToPath, new_blurToBitmap, new_bitmapResolution, new_exportId, new_areaDrawing, new_areaPage, true); - g_free(final_name); + // Create EPS + { + gchar * final_name; + final_name = g_strdup_printf("> %s", filename); + ret = ps_print_document_to_file(doc, final_name, level, new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, new_exportId, new_areaDrawing, new_areaPage, true); + g_free(final_name); - if (!ret) - throw Inkscape::Extension::Output::save_failed(); + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } + + // Create LaTeX file (if requested) + if (new_textToLaTeX) { + gchar * tex_filename; + //strip filename of ".eps", do not add ".tex" here. + gsize n = g_str_has_suffix(filename, ".eps") ? strlen(filename)-4 : strlen(filename); + tex_filename = g_strndup(filename, n); + ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_areaDrawing, new_areaPage, false); + g_free(tex_filename); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } } @@ -280,6 +331,7 @@ CairoPsOutput::init (void) #endif "</param>\n" "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n" + "<param name=\"textToLaTeX\" gui-text=\"" N_("PS+LaTeX: Omit text in PS, and create LaTeX file") "\" type=\"boolean\">false</param>\n" "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n" "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi)") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n" "<param name=\"areaDrawing\" gui-text=\"" N_("Export area is drawing") "\" type=\"boolean\">true</param>\n" @@ -317,6 +369,7 @@ CairoEpsOutput::init (void) #endif "</param>\n" "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n" + "<param name=\"textToLaTeX\" gui-text=\"" N_("EPS+LaTeX: Omit text in EPS, and create LaTeX file") "\" type=\"boolean\">false</param>\n" "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n" "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi)") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n" "<param name=\"areaDrawing\" gui-text=\"" N_("Export area is drawing") "\" type=\"boolean\">true</param>\n" diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index c33beab8a..877bdb952 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -80,7 +80,7 @@ #include <pango/pangofc-fontmap.h> //#define TRACE(_args) g_printf _args -#define TRACE(_args) +#define TRACE(_args) g_message _args //#define TEST(_args) _args #define TEST(_args) @@ -662,7 +662,11 @@ CairoRenderContext::popLayer(void) surface_width *= 1.25; surface_height *= 1.25; } - mask_ctx->setupSurface( surface_width, surface_height ); + if (!mask_ctx->setupSurface( surface_width, surface_height )) { + TRACE(("mask: setupSurface failed\n")); + _renderer->destroyContext(mask_ctx); + return; + } TRACE(("mask surface: %f x %f at %i dpi\n", surface_width, surface_height, _dpi )); // set rendering mode to normal @@ -815,6 +819,7 @@ CairoRenderContext::setSurfaceTarget(cairo_surface_t *surface, bool is_vector, c bool CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t *ctm) { +g_message("enter"); if(surface == NULL) { return false; } @@ -841,7 +846,7 @@ CairoRenderContext::_finishSurfaceSetup(cairo_surface_t *surface, cairo_matrix_t } _is_valid = TRUE; - +g_message("leave"); return true; } diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp index 0598c388a..808590e04 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -5,8 +5,9 @@ * Authors: * Ted Gould <ted@gould.cx> * Ulf Erikson <ulferikson@users.sf.net> + * Johan Engelen <goejendaagh@zonnet.nl> * - * Copyright (C) 2004-2006 Authors + * Copyright (C) 2004-2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -20,6 +21,7 @@ #include "cairo-renderer-pdf-out.h" #include "cairo-render-context.h" #include "cairo-renderer.h" +#include "latex-text-renderer.h" #include <print.h> #include "extension/system.h" #include "extension/print.h" @@ -33,6 +35,8 @@ #include "sp-item.h" #include "sp-root.h" +#include <2geom/matrix.h> + namespace Inkscape { namespace Extension { namespace Internal { @@ -48,7 +52,7 @@ CairoRendererPdfOutput::check (Inkscape::Extension::Extension * module) static bool pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, - bool texttopath, bool filtertobitmap, int resolution, + bool texttopath, bool omittext, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas) { sp_document_ensure_up_to_date(doc); @@ -83,6 +87,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int CairoRenderContext *ctx = renderer->createContext(); ctx->setPDFLevel(level); ctx->setTextToPath(texttopath); + renderer->_omitText = omittext; ctx->setFilterToBitmap(filtertobitmap); ctx->setBitmapResolution(resolution); @@ -106,7 +111,6 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int return ret; } - /** \brief This function calls the output module with the filename \param mod unused @@ -146,6 +150,14 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, g_warning("Parameter <textToPath> might not exist"); } + bool new_textToLaTeX = FALSE; + try { + new_textToLaTeX = mod->get_param_bool("textToLaTeX"); + } + catch(...) { + g_warning("Parameter <textToLaTeX> might not exist"); + } + bool new_blurToBitmap = FALSE; try { new_blurToBitmap = mod->get_param_bool("blurToBitmap"); @@ -186,15 +198,31 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, g_warning("Parameter <exportCanvas> might not exist"); } - gchar * final_name; - final_name = g_strdup_printf("> %s", filename); - ret = pdf_render_document_to_file(doc, final_name, level, - new_textToPath, new_blurToBitmap, new_bitmapResolution, - new_exportId, new_exportDrawing, new_exportCanvas); - g_free(final_name); + // Create PDF file + { + gchar * final_name; + final_name = g_strdup_printf("> %s", filename); + ret = pdf_render_document_to_file(doc, final_name, level, + new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, + new_exportId, new_exportDrawing, new_exportCanvas); + g_free(final_name); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } - if (!ret) - throw Inkscape::Extension::Output::save_failed(); + // Create LaTeX file (if requested) + if (new_textToLaTeX) { + gchar * tex_filename; + //strip filename of ".pdf", do not add ".tex" here. + gsize n = g_str_has_suffix(filename, ".pdf") ? strlen(filename)-4 : strlen(filename); + tex_filename = g_strndup(filename, n); + ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_exportDrawing, new_exportCanvas, true); + g_free(tex_filename); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } } #include "clear-n_.h" @@ -217,6 +245,7 @@ CairoRendererPdfOutput::init (void) "<_item value='PDF14'>" N_("PDF 1.4") "</_item>\n" "</param>\n" "<param name=\"textToPath\" gui-text=\"" N_("Convert texts to paths") "\" type=\"boolean\">false</param>\n" + "<param name=\"textToLaTeX\" gui-text=\"" N_("PDF+LaTeX: Omit text in PDF, and create LaTeX file") "\" type=\"boolean\">false</param>\n" "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"boolean\">true</param>\n" "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi)") "\" type=\"int\" min=\"1\" max=\"10000\">90</param>\n" "<param name=\"areaDrawing\" gui-text=\"" N_("Export area is drawing") "\" type=\"boolean\">false</param>\n" diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 8cc386135..6e4bb3b7e 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -29,6 +29,7 @@ #include <errno.h> #include "libnr/nr-rect.h" +#include "libnrtype/Layout-TNG.h" #include <2geom/transforms.h> #include <2geom/pathvector.h> @@ -102,6 +103,7 @@ namespace Extension { namespace Internal { CairoRenderer::CairoRenderer(void) + : _omitText(false) {} CairoRenderer::~CairoRenderer(void) @@ -567,6 +569,11 @@ CairoRenderer::setStateForItem(CairoRenderContext *ctx, SPItem const *item) void CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) { + if ( _omitText && (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) ) { + // skip text if _omitText is true + return; + } + ctx->pushState(); setStateForItem(ctx, item); @@ -591,6 +598,8 @@ CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) bool CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, SPItem *base) { +// PLEASE note when making changes to the boundingbox and transform calculation, corresponding changes should be made to PDFLaTeXRenderer::setupDocument !!! + g_assert( ctx != NULL ); if (!base) diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h index ab5d4cf58..d69a60753 100644 --- a/src/extension/internal/cairo-renderer.h +++ b/src/extension/internal/cairo-renderer.h @@ -56,6 +56,10 @@ public: /** Traverses the object tree and invokes the render methods. */ void renderItem(CairoRenderContext *ctx, SPItem *item); + + /** If _omitText is true, no text will be output to the PDF document. + The PDF will be exactly the same as if the text was written to it and then erased. */ + bool _omitText; }; // FIXME: this should be a static method of CairoRenderer diff --git a/src/extension/internal/filter/filter.cpp b/src/extension/internal/filter/filter.cpp index d98f8e9a2..30e622507 100644 --- a/src/extension/internal/filter/filter.cpp +++ b/src/extension/internal/filter/filter.cpp @@ -71,7 +71,9 @@ Filter::get_filter (Inkscape::Extension::Extension * ext) { } void -Filter::merge_filters (Inkscape::XML::Node * to, Inkscape::XML::Node * from, Inkscape::XML::Document * doc, gchar * srcGraphic, gchar * srcGraphicAlpha) +Filter::merge_filters( Inkscape::XML::Node * to, Inkscape::XML::Node * from, + Inkscape::XML::Document * doc, + gchar const * srcGraphic, gchar const * srcGraphicAlpha) { if (from == NULL) return; diff --git a/src/extension/internal/filter/filter.h b/src/extension/internal/filter/filter.h index fe6b678d9..a5d5d9d4e 100644 --- a/src/extension/internal/filter/filter.h +++ b/src/extension/internal/filter/filter.h @@ -25,7 +25,7 @@ protected: private: Inkscape::XML::Document * get_filter (Inkscape::Extension::Extension * ext); - void merge_filters (Inkscape::XML::Node * to, Inkscape::XML::Node * from, Inkscape::XML::Document * doc, gchar * srcGraphic = NULL, gchar * srcGraphicAlpha = NULL); + void merge_filters (Inkscape::XML::Node * to, Inkscape::XML::Node * from, Inkscape::XML::Document * doc, gchar const * srcGraphic = NULL, gchar const * srcGraphicAlpha = NULL); public: Filter(); diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp index 64a099c8a..a1295406c 100644 --- a/src/extension/internal/gdkpixbuf-input.cpp +++ b/src/extension/internal/gdkpixbuf-input.cpp @@ -1,11 +1,14 @@ #ifdef HAVE_CONFIG_H # include <config.h> #endif +#include <glib/gprintf.h> #include "document-private.h" #include <dir-util.h> +#include "extension/input.h" #include "extension/system.h" #include "gdkpixbuf-input.h" #include "selection-chemistry.h" +#include "sp-image.h" namespace Inkscape { @@ -16,69 +19,88 @@ GdkPixbuf* pixbuf_new_from_file( char const *utf8name, GError **error ); namespace Extension { namespace Internal { +static std::set<Glib::ustring> create_lossy_set() +{ + std::set<Glib::ustring> lossy; + lossy.insert(".jpg"); + lossy.insert(".jpeg"); + return lossy; +} + SPDocument * -GdkpixbufInput::open(Inkscape::Extension::Input */*mod*/, char const *uri) +GdkpixbufInput::open(Inkscape::Extension::Input *mod, char const *uri) { + bool embed = (strcmp(mod->get_param_optiongroup("link"), "embed") == 0); + SPDocument *doc = NULL; GdkPixbuf *pb = Inkscape::IO::pixbuf_new_from_file( uri, NULL ); + static std::set<Glib::ustring> lossy = create_lossy_set(); if (pb) { /* We are readable */ + bool is_lossy; + Glib::ustring mime_type, ext; + Glib::ustring u = uri; + std::size_t dotpos = u.rfind('.'); + if (dotpos != Glib::ustring::npos) { + ext = u.substr(dotpos, Glib::ustring::npos); + } + + // HACK: replace with something better based on GIO + if (!ext.empty() && lossy.find(ext) != lossy.end()) { + is_lossy = true; + mime_type = "image/jpeg"; + } else { + is_lossy = false; + mime_type = "image/png"; + } + doc = sp_document_new(NULL, TRUE, TRUE); bool saved = sp_document_get_undo_sensitive(doc); sp_document_set_undo_sensitive(doc, false); // no need to undo in this temporary document - Inkscape::XML::Node *repr = NULL; - double width = gdk_pixbuf_get_width(pb); double height = gdk_pixbuf_get_height(pb); gchar const *str = gdk_pixbuf_get_option( pb, "Inkscape::DpiX" ); - if ( str ) - { + if ( str ) { gint dpi = atoi(str); - if ( dpi > 0 && dpi != 72 ) - { + if ( dpi > 0 && dpi != 72 ) { double scale = 72.0 / (double)dpi; width *= scale; } } str = gdk_pixbuf_get_option( pb, "Inkscape::DpiY" ); - if ( str ) - { + if ( str ) { gint dpi = atoi(str); - if ( dpi > 0 && dpi != 72 ) - { + if ( dpi > 0 && dpi != 72 ) { double scale = 72.0 / (double)dpi; height *= scale; } } + // Create image node Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); - // import as <image> - repr = xml_doc->createElement("svg:image"); - - // convert filename to uri - gchar* _uri = g_filename_to_uri(uri, NULL, NULL); - if(_uri) { - repr->setAttribute("xlink:href", _uri); - g_free(_uri); + Inkscape::XML::Node *image_node = xml_doc->createElement("svg:image"); + sp_repr_set_svg_double(image_node, "width", width); + sp_repr_set_svg_double(image_node, "height", height); + + if (embed) { + sp_embed_image(image_node, pb, mime_type); } else { - repr->setAttribute("xlink:href", uri); + // convert filename to uri + gchar* _uri = g_filename_to_uri(uri, NULL, NULL); + if(_uri) { + image_node->setAttribute("xlink:href", _uri); + g_free(_uri); + } else { + image_node->setAttribute("xlink:href", uri); + } } - /* impl: doc->base is currently NULL, so we can use uri for href whether it's absolute - * or relative. The href will get rewritten by rebase_hrefs if by chance uri is relative - * and doc gets saved to a different directory. - * - * We don't bother setting sodipodi:absref, as we assume it's never useful to have - * sodipodi:absref with the same value as xlink:href, and rebase_hrefs will provide - * sodipodi:absref values where necessary. */ - - sp_repr_set_svg_double(repr, "width", width); - sp_repr_set_svg_double(repr, "height", height); - - SP_DOCUMENT_ROOT(doc)->appendChildRepr(repr); - Inkscape::GC::release(repr); - gdk_pixbuf_unref(pb); - //alter the canvas size to fit the image size + + g_object_unref(pb); + + // Add it to the current layer + SP_DOCUMENT_ROOT(doc)->appendChildRepr(image_node); + Inkscape::GC::release(image_node); fit_canvas_to_drawing(doc); // restore undo, as now this document may be shown to the user if a bitmap was opened sp_document_set_undo_sensitive(doc, saved); @@ -126,6 +148,11 @@ GdkpixbufInput::init(void) "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n" "<name>" N_("%s GDK pixbuf Input") "</name>\n" "<id>org.inkscape.input.gdkpixbuf.%s</id>\n" + "<param name='link' type='optiongroup' appearance='full' _gui-text='" N_("Link or embed image:") "' >\n" + "<_option value='embed'>" N_("embed") "</_option>\n" + "<_option value='link'>" N_("link") "</_option>\n" + "</param>\n" + "<_param name='help' type='description'>" N_("Embed results in stand-alone, larger SVG files. Link references a file outside this SVG document and all files must be moved together.") "</_param>\n" "<input>\n" "<extension>.%s</extension>\n" "<mimetype>%s</mimetype>\n" diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp index a2f387406..ca061a63a 100644 --- a/src/extension/internal/javafx-out.cpp +++ b/src/extension/internal/javafx-out.cpp @@ -707,7 +707,7 @@ bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj) * Check the type of node and process */ String id; - if (!obj->id) + if (!obj->getId()) { char buf[16]; sprintf(buf, "id%d", idindex++); @@ -715,7 +715,7 @@ bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj) } else { - id = obj->id; + id = obj->getId(); } if (SP_IS_ITEM(obj)) { @@ -773,7 +773,7 @@ bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj) * Check the type of node and process */ String id; - if (!obj->id) + if (!obj->getId()) { char buf[16]; sprintf(buf, "id%d", idindex++); @@ -781,7 +781,7 @@ bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj) } else { - id = obj->id; + id = obj->getId(); } if (SP_IS_ITEM(obj)) { diff --git a/src/extension/internal/latex-pstricks.cpp b/src/extension/internal/latex-pstricks.cpp index 789e5ea34..34b7532ce 100644 --- a/src/extension/internal/latex-pstricks.cpp +++ b/src/extension/internal/latex-pstricks.cpp @@ -203,13 +203,19 @@ PrintLatex::fill(Inkscape::Extension::Print *mod, if (style->fill.isColor()) { Inkscape::SVGOStringStream os; float rgb[3]; + float fill_opacity; os.setf(std::ios::fixed); + fill_opacity=SP_SCALE24_TO_FLOAT(style->fill_opacity.value); sp_color_get_rgb_floatv(&style->fill.value.color, rgb); os << "{\n\\newrgbcolor{curcolor}{" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "}\n"; + os << "\\pscustom[linestyle=none,fillstyle=solid,fillcolor=curcolor"; + if (fill_opacity!=1.0) { + os << ",opacity="<<fill_opacity; + } - os << "\\pscustom[linestyle=none,fillstyle=solid,fillcolor=curcolor]\n{\n"; + os << "]\n{\n"; print_pathvector(os, pathv, transform); @@ -230,14 +236,20 @@ PrintLatex::stroke (Inkscape::Extension::Print *mod, Geom::PathVector const &pat if (style->stroke.isColor()) { Inkscape::SVGOStringStream os; float rgb[3]; + float stroke_opacity; Geom::Matrix tr_stack = m_tr_stack.top(); double const scale = tr_stack.descrim(); os.setf(std::ios::fixed); + stroke_opacity=SP_SCALE24_TO_FLOAT(style->stroke_opacity.value); sp_color_get_rgb_floatv(&style->stroke.value.color, rgb); os << "{\n\\newrgbcolor{curcolor}{" << rgb[0] << " " << rgb[1] << " " << rgb[2] << "}\n"; os << "\\pscustom[linewidth=" << style->stroke_width.computed*scale<< ",linecolor=curcolor"; + + if (stroke_opacity!=1.0) { + os<<",strokeopacity="<<stroke_opacity; + } if (style->stroke_dasharray_set && style->stroke_dash.n_dash && diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp new file mode 100644 index 000000000..28bba1beb --- /dev/null +++ b/src/extension/internal/latex-text-renderer.cpp @@ -0,0 +1,569 @@ +#define EXTENSION_INTERNAL_LATEX_TEXT_RENDERER_CPP + +/** \file + * Rendering LaTeX file (pdf/eps/ps+latex output) + * + * The idea stems from GNUPlot's epslatex terminal output :-) + */ +/* + * Authors: + * Johan Engelen <goejendaagh@zonnet.nl> + * Miklos Erdelyi <erdelyim@gmail.com> + * + * Copyright (C) 2006-2010 Authors + * + * Licensed under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "latex-text-renderer.h" + +#include <signal.h> +#include <errno.h> + +#include "libnrtype/Layout-TNG.h" +#include <2geom/transforms.h> +#include <2geom/rect.h> + +#include <glibmm/i18n.h> +#include "sp-item.h" +#include "sp-item-group.h" +#include "style.h" +#include "sp-root.h" +#include "sp-use.h" +#include "sp-text.h" +#include "sp-flowtext.h" +#include "sp-rect.h" +#include "text-editing.h" + +#include <unit-constants.h> + +#include "extension/system.h" + +#include "io/sys.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +/** + * This method is called by the PDF, EPS and PS output extensions. + * @param filename This should be the filename without extension to which the tex code should be written. Output goes to <filename>.tex. + */ +bool +latex_render_document_text_to_file( SPDocument *doc, gchar const *filename, + const gchar * const exportId, bool exportDrawing, bool exportCanvas, + bool pdflatex) +{ + sp_document_ensure_up_to_date(doc); + + SPItem *base = NULL; + + bool pageBoundingBox = true; + if (exportId && strcmp(exportId, "")) { + // we want to export the given item only + base = SP_ITEM(doc->getObjectById(exportId)); + pageBoundingBox = exportCanvas; + } + else { + // we want to export the entire document from root + base = SP_ITEM(sp_document_root(doc)); + pageBoundingBox = !exportDrawing; + } + + if (!base) + return false; + + /* Create renderer */ + LaTeXTextRenderer *renderer = new LaTeXTextRenderer(pdflatex); + + bool ret = renderer->setTargetFile(filename); + if (ret) { + /* Render document */ + bool ret = renderer->setupDocument(doc, pageBoundingBox, base); + if (ret) { + renderer->renderItem(base); + } + } + + delete renderer; + + return ret; +} + +LaTeXTextRenderer::LaTeXTextRenderer(bool pdflatex) + : _stream(NULL), + _filename(NULL), + _pdflatex(pdflatex) +{ + push_transform(Geom::identity()); +} + +LaTeXTextRenderer::~LaTeXTextRenderer(void) +{ + if (_stream) { + writePostamble(); + + fclose(_stream); + } + + /* restore default signal handling for SIGPIPE */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_DFL); +#endif + + if (_filename) { + g_free(_filename); + } + + return; +} + +/** This should create the output LaTeX file, and assign it to _stream. + * @return Returns true when succesfull + */ +bool +LaTeXTextRenderer::setTargetFile(gchar const *filename) { + if (filename != NULL) { + while (isspace(*filename)) filename += 1; + + _filename = g_path_get_basename(filename); + + gchar *filename_ext = g_strdup_printf("%s.tex", filename); + Inkscape::IO::dump_fopen_call(filename_ext, "K"); + FILE *osf = Inkscape::IO::fopen_utf8name(filename_ext, "w+"); + if (!osf) { + fprintf(stderr, "inkscape: fopen(%s): %s\n", + filename_ext, strerror(errno)); + return false; + } + _stream = osf; + g_free(filename_ext); + } + + if (_stream) { + /* fixme: this is kinda icky */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_IGN); +#endif + } + + fprintf(_stream, "%%%% Creator: Inkscape %s, www.inkscape.org\n", PACKAGE_STRING); + fprintf(_stream, "%%%% PDF/EPS/PS + LaTeX output extension by Johan Engelen, 2010\n"); + fprintf(_stream, "%%%% Accompanies image file '%s' (pdf, eps, ps)\n", _filename); + fprintf(_stream, "%%%%\n"); + /* flush this to test output stream as early as possible */ + if (fflush(_stream)) { + if (ferror(_stream)) { + g_print("Error %d on LaTeX file output stream: %s\n", errno, + g_strerror(errno)); + } + g_print("Output to LaTeX file failed\n"); + /* fixme: should use pclose() for pipes */ + fclose(_stream); + _stream = NULL; + fflush(stdout); + return false; + } + + writePreamble(); + + return true; +} + +static char const preamble[] = +"%% To include the image in your LaTeX document, write\n" +"%% \\input{<filename>.tex}\n" +"%% instead of\n" +"%% \\includegraphics{<filename>.pdf}\n" +"%% To scale the image, write\n" +"%% \\def{\\svgwidth}{<desired width>}\n" +"%% \\input{<filename>.tex}\n" +"%% instead of\n" +"%% \\includegraphics[width=<desired width>]{<filename>.pdf}\n" +"\n" +"\\begingroup\n" +" \\makeatletter\n" +" \\providecommand\\color[2][]{%\n" +" \\errmessage{(Inkscape) Color is used for the text in Inkscape, but the package \'color.sty\' is not loaded}\n" +" \\renewcommand\\color[2][]{}%\n" +" }\n" +" \\providecommand\\transparent[1]{%\n" +" \\errmessage{(Inkscape) Transparency is used (non-zero) for the text in Inkscape, but the package \'transparent.sty\' is not loaded}\n" +" \\renewcommand\\transparent[1]{}%\n" +" }\n" +" \\providecommand\\rotatebox[2]{#2}\n"; + +static char const postamble[] = +" \\end{picture}%\n" +"\\endgroup\n"; + +void +LaTeXTextRenderer::writePreamble() +{ + fprintf(_stream, "%s", preamble); +} +void +LaTeXTextRenderer::writePostamble() +{ + fprintf(_stream, "%s", postamble); +} + +void +LaTeXTextRenderer::sp_group_render(SPItem *item) +{ + SPGroup *group = SP_GROUP(item); + + GSList *l = g_slist_reverse(group->childList(false)); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM(o)) { + renderItem (SP_ITEM (o)); + } + l = g_slist_remove (l, o); + } +} + +void +LaTeXTextRenderer::sp_use_render(SPItem *item) +{ + bool translated = false; + SPUse *use = SP_USE(item); + + if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) { + Geom::Matrix tp(Geom::Translate(use->x.computed, use->y.computed)); + push_transform(tp); + translated = true; + } + + if (use->child && SP_IS_ITEM(use->child)) { + renderItem(SP_ITEM(use->child)); + } + + if (translated) { + pop_transform(); + } +} + +void +LaTeXTextRenderer::sp_text_render(SPItem *item) +{ + SPText *textobj = SP_TEXT (item); + SPStyle *style = SP_OBJECT_STYLE (SP_OBJECT(item)); + + gchar *str = sp_te_get_string_multiline(item); + if (!str) { + return; + } + + // get position and alignment + // Align vertically on the baseline of the font (retreived from the anchor point) + // Align horizontally on anchorpoint + gchar const *alignment = NULL; + switch (style->text_anchor.computed) { + case SP_CSS_TEXT_ANCHOR_START: + alignment = "[lb]"; + break; + case SP_CSS_TEXT_ANCHOR_END: + alignment = "[rb]"; + break; + case SP_CSS_TEXT_ANCHOR_MIDDLE: + default: + alignment = "[b]"; + break; + } + Geom::Point anchor = textobj->attributes.firstXY() * transform(); + Geom::Point pos(anchor); + + // determine color and transparency (for now, use rgb color model as it is most native to Inkscape) + bool has_color = false; // if the item has no color set, don't force black color + bool has_transparency = false; + // TODO: how to handle ICC colors? + // give priority to fill color + guint32 rgba = 0; + float opacity = SP_SCALE24_TO_FLOAT(style->opacity.value); + if (style->fill.set && style->fill.isColor()) { + has_color = true; + rgba = style->fill.value.color.toRGBA32(1.); + opacity *= SP_SCALE24_TO_FLOAT(style->fill_opacity.value); + } else if (style->stroke.set && style->stroke.isColor()) { + has_color = true; + rgba = style->stroke.value.color.toRGBA32(1.); + opacity *= SP_SCALE24_TO_FLOAT(style->stroke_opacity.value); + } + if (opacity < 1.0) { + has_transparency = true; + } + + // get rotation + Geom::Matrix i2doc = sp_item_i2doc_affine(item); + Geom::Matrix wotransl = i2doc.without_translation(); + double degrees = -180/M_PI * Geom::atan2(wotransl.xAxis()); + bool has_rotation = !Geom::are_near(degrees,0.); + + // write to LaTeX + Inkscape::SVGOStringStream os; + os.setf(std::ios::fixed); // don't use scientific notation + + os << " \\put(" << pos[Geom::X] << "," << pos[Geom::Y] << "){"; + if (has_color) { + os << "\\color[rgb]{" << SP_RGBA32_R_F(rgba) << "," << SP_RGBA32_G_F(rgba) << "," << SP_RGBA32_B_F(rgba) << "}"; + } + if (_pdflatex && has_transparency) { + os << "\\transparent{" << opacity << "}"; + } + if (has_rotation) { + os << "\\rotatebox{" << degrees << "}{"; + } + os << "\\makebox(0,0)" << alignment << "{"; + os << "\\smash{" << str << "}"; // smash the text, to be able to put the makebox coordinates at the baseline + if (has_rotation) { + os << "}"; // rotatebox end + } + os << "}"; //makebox end + os << "}%\n"; // put end + + fprintf(_stream, "%s", os.str().c_str()); +} + +void +LaTeXTextRenderer::sp_flowtext_render(SPItem * item) +{ +/* +Flowtext is possible by using a minipage! :) +Flowing in rectangle is possible, not in arb shape. +*/ + + SPFlowtext *flowtext = SP_FLOWTEXT(item); + SPStyle *style = SP_OBJECT_STYLE (SP_OBJECT(item)); + + gchar *strtext = sp_te_get_string_multiline(item); + if (!strtext) { + return; + } + // replace carriage return with double slash + gchar ** splitstr = g_strsplit(strtext, "\n", -1); + gchar *str = g_strjoinv("\\\\ ", splitstr); + g_free(strtext); + g_strfreev(splitstr); + + if (!flowtext->has_internal_frame()) { + // has_internal_frame includes a check that frame is a SPRect + g_warning("LaTeX export: non-rectangular flowed text shapes are not supported, skipping text."); + return; // don't know how to handle non-rect frames yet. is quite uncommon for latex users i think + } + + SPRect *frame = SP_RECT(flowtext->get_frame(NULL)); + Geom::Rect framebox = sp_rect_get_rect(frame) * transform(); + + // get position and alignment + // Align on topleft corner. + gchar const *alignment = "[lt]"; + gchar const *justification = ""; + switch (flowtext->layout.paragraphAlignment(flowtext->layout.begin())) { + case Inkscape::Text::Layout::LEFT: + justification = "\\raggedright"; + break; + case Inkscape::Text::Layout::RIGHT: + justification = "\\raggedleft"; + break; + case Inkscape::Text::Layout::CENTER: + justification = "\\centering"; + case Inkscape::Text::Layout::FULL: + default: + // no need to add LaTeX code for standard justified output :) + break; + } + Geom::Point pos(framebox.corner(3)); //topleft corner + + // determine color and transparency (for now, use rgb color model as it is most native to Inkscape) + bool has_color = false; // if the item has no color set, don't force black color + bool has_transparency = false; + // TODO: how to handle ICC colors? + // give priority to fill color + guint32 rgba = 0; + float opacity = SP_SCALE24_TO_FLOAT(style->opacity.value); + if (style->fill.set && style->fill.isColor()) { + has_color = true; + rgba = style->fill.value.color.toRGBA32(1.); + opacity *= SP_SCALE24_TO_FLOAT(style->fill_opacity.value); + } else if (style->stroke.set && style->stroke.isColor()) { + has_color = true; + rgba = style->stroke.value.color.toRGBA32(1.); + opacity *= SP_SCALE24_TO_FLOAT(style->stroke_opacity.value); + } + if (opacity < 1.0) { + has_transparency = true; + } + + // get rotation + Geom::Matrix i2doc = sp_item_i2doc_affine(item); + Geom::Matrix wotransl = i2doc.without_translation(); + double degrees = -180/M_PI * Geom::atan2(wotransl.xAxis()); + bool has_rotation = !Geom::are_near(degrees,0.); + + // write to LaTeX + Inkscape::SVGOStringStream os; + os.setf(std::ios::fixed); // don't use scientific notation + + os << " \\put(" << pos[Geom::X] << "," << pos[Geom::Y] << "){"; + if (has_color) { + os << "\\color[rgb]{" << SP_RGBA32_R_F(rgba) << "," << SP_RGBA32_G_F(rgba) << "," << SP_RGBA32_B_F(rgba) << "}"; + } + if (_pdflatex && has_transparency) { + os << "\\transparent{" << opacity << "}"; + } + if (has_rotation) { + os << "\\rotatebox{" << degrees << "}{"; + } + os << "\\makebox(0,0)" << alignment << "{"; + os << "\\begin{minipage}{" << framebox.width() << "\\unitlength}"; + os << justification; + os << str; + os << "\\end{minipage}"; + if (has_rotation) { + os << "}"; // rotatebox end + } + os << "}"; //makebox end + os << "}%\n"; // put end + + fprintf(_stream, "%s", os.str().c_str()); +} + +void +LaTeXTextRenderer::sp_root_render(SPItem *item) +{ + SPRoot *root = SP_ROOT(item); + + push_transform(root->c2p); + sp_group_render(item); + pop_transform(); +} + +void +LaTeXTextRenderer::sp_item_invoke_render(SPItem *item) +{ + // Check item's visibility + if (item->isHidden()) { + return; + } + + if (SP_IS_ROOT(item)) { + return sp_root_render(item); + } else if (SP_IS_GROUP(item)) { + return sp_group_render(item); + } else if (SP_IS_USE(item)) { + sp_use_render(item); + } else if (SP_IS_TEXT(item)) { + return sp_text_render(item); + } else if (SP_IS_FLOWTEXT(item)) { + return sp_flowtext_render(item); + } + // We are not interested in writing the other SPItem types to LaTeX +} + +void +LaTeXTextRenderer::renderItem(SPItem *item) +{ + push_transform(item->transform); + sp_item_invoke_render(item); + pop_transform(); +} + +bool +LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base) +{ +// The boundingbox calculation here should be exactly the same as the one by CairoRenderer::setupDocument ! + + if (!base) + base = SP_ITEM(sp_document_root(doc)); + + Geom::OptRect d; + if (pageBoundingBox) { + d = Geom::Rect( Geom::Point(0,0), + Geom::Point(sp_document_width(doc), sp_document_height(doc)) ); + } else { + sp_item_invoke_bbox(base, d, sp_item_i2d_affine(base), TRUE, SPItem::RENDERING_BBOX); + } + if (!d) { + g_message("LaTeXTextRenderer: could not retrieve boundingbox."); + return false; + } + + // scale all coordinates, such that the width of the image is 1, this is convenient for scaling the image in LaTeX + double scale = 1/(d->width()); + double _width = d->width() * scale; + double _height = d->height() * scale; + push_transform( Geom::Scale(scale, scale) ); + + if (!pageBoundingBox) + { + push_transform( Geom::Translate( - d->min() ) ); + } + + // flip y-axis + push_transform( Geom::Scale(1,-1) * Geom::Translate(0, sp_document_height(doc)) ); + + // write the info to LaTeX + Inkscape::SVGOStringStream os; + os.setf(std::ios::fixed); // no scientific notation + + // scaling of the image when including it in LaTeX + + os << " \\ifx\\svgwidth\\undefined\n"; + os << " \\setlength{\\unitlength}{" << d->width() * PT_PER_PX << "pt}\n"; + os << " \\else\n"; + os << " \\setlength{\\unitlength}{\\svgwidth}\n"; + os << " \\fi\n"; + os << " \\global\\let\\svgwidth\\undefined\n"; + os << " \\makeatother\n"; + + os << " \\begin{picture}(" << _width << "," << _height << ")%\n"; + // strip pathname, as it is probably desired. Having a specific path in the TeX file is not convenient. + os << " \\put(0,0){\\includegraphics[width=\\unitlength]{" << _filename << "}}%\n"; + + fprintf(_stream, "%s", os.str().c_str()); + + return true; +} + +Geom::Matrix const & +LaTeXTextRenderer::transform() +{ + return _transform_stack.top(); +} + +void +LaTeXTextRenderer::push_transform(Geom::Matrix const &tr) +{ + if(_transform_stack.size()){ + Geom::Matrix tr_top = _transform_stack.top(); + _transform_stack.push(tr * tr_top); + } else { + _transform_stack.push(tr); + } +} + +void +LaTeXTextRenderer::pop_transform() +{ + _transform_stack.pop(); +} + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/latex-text-renderer.h b/src/extension/internal/latex-text-renderer.h new file mode 100644 index 000000000..b5d4bfac1 --- /dev/null +++ b/src/extension/internal/latex-text-renderer.h @@ -0,0 +1,85 @@ +#ifndef EXTENSION_INTERNAL_LATEX_TEXT_RENDERER_H_SEEN +#define EXTENSION_INTERNAL_LATEX_TEXT_RENDERER_H_SEEN + +/** \file + * Declaration of LaTeXTextRenderer, used for rendering the accompanying LaTeX file when exporting to PDF/EPS/PS + LaTeX + */ +/* + * Authors: + * Johan Engelen <goejendaagh@zonnet.nl> + * + * Copyright (C) 2010 Authors + * + * Licensed under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "extension/extension.h" +#include <2geom/matrix.h> +#include <stack> + +class SPItem; + +namespace Inkscape { +namespace Extension { +namespace Internal { + +bool latex_render_document_text_to_file(SPDocument *doc, gchar const *filename, + const gchar * const exportId, bool exportDrawing, bool exportCanvas, + bool pdflatex); + +class LaTeXTextRenderer { +public: + LaTeXTextRenderer(bool pdflatex); + virtual ~LaTeXTextRenderer(); + + bool setTargetFile(gchar const *filename); + + /** Initializes the LaTeXTextRenderer according to the specified + SPDocument. Important to set the boundingbox to the pdf boundingbox */ + bool setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base); + + /** Traverses the object tree and invokes the render methods. */ + void renderItem(SPItem *item); + +protected: + FILE * _stream; + gchar * _filename; + + bool _pdflatex; /** true if ouputting for pdfLaTeX*/ + + void push_transform(Geom::Matrix const &transform); + Geom::Matrix const & transform(); + void pop_transform(); + std::stack<Geom::Matrix> _transform_stack; + + void writePreamble(); + void writePostamble(); + + void sp_item_invoke_render(SPItem *item); + void sp_root_render(SPItem *item); + void sp_group_render(SPItem *item); + void sp_use_render(SPItem *item); + void sp_text_render(SPItem *item); + void sp_flowtext_render(SPItem *item); +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* !EXTENSION_INTERNAL_LATEX_TEXT_RENDERER_H_SEEN */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp index 46e0361ce..5331c072c 100644 --- a/src/extension/internal/odf.cpp +++ b/src/extension/internal/odf.cpp @@ -1659,9 +1659,9 @@ bool OdfOutput::processGradient(Writer &outs, SPItem *item, GradientInfo gi; - SPGradient *grvec = sp_gradient_get_vector(gradient, FALSE); - for (SPStop *stop = sp_first_stop(grvec) ; - stop ; stop = sp_next_stop(stop)) + SPGradient *grvec = gradient->getVector(FALSE); + for (SPStop *stop = grvec->getFirstStop() ; + stop ; stop = stop->getNextStop()) { unsigned long rgba = sp_stop_get_rgba32(stop); unsigned long rgb = (rgba >> 8) & 0xffffff; diff --git a/src/extension/internal/pdfinput/pdf-parser.cpp b/src/extension/internal/pdfinput/pdf-parser.cpp index 118896fd3..b37cbb777 100644 --- a/src/extension/internal/pdfinput/pdf-parser.cpp +++ b/src/extension/internal/pdfinput/pdf-parser.cpp @@ -809,7 +809,11 @@ void PdfParser::opSetExtGState(Object args[], int numArgs) { blendingColorSpace = NULL; isolated = knockout = gFalse; if (!obj4.dictLookup(const_cast<char*>("CS"), &obj5)->isNull()) { +#ifdef POPPLER_NEW_COLOR_SPACE_API + blendingColorSpace = GfxColorSpace::parse(&obj5, NULL); +#else blendingColorSpace = GfxColorSpace::parse(&obj5); +#endif } obj5.free(); if (obj4.dictLookup(const_cast<char*>("I"), &obj5)->isBool()) { @@ -1008,11 +1012,19 @@ void PdfParser::opSetFillColorSpace(Object args[], int numArgs) { state->setFillPattern(NULL); res->lookupColorSpace(args[0].getName(), &obj); +#ifdef POPPLER_NEW_COLOR_SPACE_API + if (obj.isNull()) { + colorSpace = GfxColorSpace::parse(&args[0], NULL); + } else { + colorSpace = GfxColorSpace::parse(&obj, NULL); + } +#else if (obj.isNull()) { colorSpace = GfxColorSpace::parse(&args[0]); } else { colorSpace = GfxColorSpace::parse(&obj); } +#endif obj.free(); if (colorSpace) { state->setFillColorSpace(colorSpace); @@ -1031,11 +1043,19 @@ void PdfParser::opSetStrokeColorSpace(Object args[], int numArgs) { state->setStrokePattern(NULL); res->lookupColorSpace(args[0].getName(), &obj); +#ifdef POPPLER_NEW_COLOR_SPACE_API + if (obj.isNull()) { + colorSpace = GfxColorSpace::parse(&args[0], NULL); + } else { + colorSpace = GfxColorSpace::parse(&obj, NULL); + } +#else if (obj.isNull()) { colorSpace = GfxColorSpace::parse(&args[0]); } else { colorSpace = GfxColorSpace::parse(&obj); } +#endif obj.free(); if (colorSpace) { state->setStrokeColorSpace(colorSpace); @@ -1100,11 +1120,19 @@ void PdfParser::opSetFillColorN(Object args[], int numArgs) { state->setFillColor(&color); builder->updateStyle(state); } +#ifdef POPPLER_NEW_COLOR_SPACE_API + if (args[numArgs-1].isName() && + (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) { + state->setFillPattern(pattern); + builder->updateStyle(state); + } +#else if (args[numArgs-1].isName() && (pattern = res->lookupPattern(args[numArgs-1].getName()))) { state->setFillPattern(pattern); builder->updateStyle(state); } +#endif } else { if (numArgs != state->getFillColorSpace()->getNComps()) { @@ -1144,11 +1172,19 @@ void PdfParser::opSetStrokeColorN(Object args[], int numArgs) { state->setStrokeColor(&color); builder->updateStyle(state); } +#ifdef POPPLER_NEW_COLOR_SPACE_API + if (args[numArgs-1].isName() && + (pattern = res->lookupPattern(args[numArgs-1].getName(), NULL))) { + state->setStrokePattern(pattern); + builder->updateStyle(state); + } +#else if (args[numArgs-1].isName() && (pattern = res->lookupPattern(args[numArgs-1].getName()))) { state->setStrokePattern(pattern); builder->updateStyle(state); } +#endif } else { if (numArgs != state->getStrokeColorSpace()->getNComps()) { @@ -1543,9 +1579,15 @@ void PdfParser::opShFill(Object args[], int numArgs) { double *matrix = NULL; GBool savedState = gFalse; +#ifdef POPPLER_NEW_COLOR_SPACE_API + if (!(shading = res->lookupShading(args[0].getName(), NULL))) { + return; + } +#else if (!(shading = res->lookupShading(args[0].getName()))) { return; } +#endif // save current graphics state if (shading->getType() != 2 && shading->getType() != 3) { @@ -2507,7 +2549,11 @@ void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) { } } if (!obj1.isNull()) { +#ifdef POPPLER_NEW_COLOR_SPACE_API + colorSpace = GfxColorSpace::parse(&obj1, NULL); +#else colorSpace = GfxColorSpace::parse(&obj1); +#endif } else if (csMode == streamCSDeviceGray) { colorSpace = new GfxDeviceGrayColorSpace(); } else if (csMode == streamCSDeviceRGB) { @@ -2592,7 +2638,11 @@ void PdfParser::doImage(Object *ref, Stream *str, GBool inlineImg) { obj2.free(); } } +#ifdef POPPLER_NEW_COLOR_SPACE_API + maskColorSpace = GfxColorSpace::parse(&obj1, NULL); +#else maskColorSpace = GfxColorSpace::parse(&obj1); +#endif obj1.free(); if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) { goto err1; @@ -2767,7 +2817,11 @@ void PdfParser::doForm(Object *str) { if (obj1.dictLookup(const_cast<char*>("S"), &obj2)->isName(const_cast<char*>("Transparency"))) { transpGroup = gTrue; if (!obj1.dictLookup(const_cast<char*>("CS"), &obj3)->isNull()) { +#ifdef POPPLER_NEW_COLOR_SPACE_API + blendingColorSpace = GfxColorSpace::parse(&obj3, NULL); +#else blendingColorSpace = GfxColorSpace::parse(&obj3); +#endif } obj3.free(); if (obj1.dictLookup(const_cast<char*>("I"), &obj3)->isBool()) { diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp index 1cb14fb58..16877c370 100644 --- a/src/extension/internal/pov-out.cpp +++ b/src/extension/internal/pov-out.cpp @@ -312,12 +312,12 @@ bool PovOutput::doCurve(SPItem *item, const String &id) int segmentCount = 0; /** * For all Subpaths in the <path> - */ + */ for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { /** * For all segments in the subpath, including extra closing segment defined by 2geom - */ + */ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) { @@ -340,14 +340,14 @@ bool PovOutput::doCurve(SPItem *item, const String &id) nrSegments += segmentCount; /** - * at moment of writing, 2geom lacks proper initialization of empty intervals in rect... - */ - Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() ); - - + * at moment of writing, 2geom lacks proper initialization of empty intervals in rect... + */ + Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() ); + + /** * For all Subpaths in the <path> - */ + */ for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { @@ -355,7 +355,7 @@ bool PovOutput::doCurve(SPItem *item, const String &id) /** * For all segments in the subpath, including extra closing segment defined by 2geom - */ + */ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) { @@ -372,7 +372,7 @@ bool PovOutput::doCurve(SPItem *item, const String &id) nrNodes += 8; } else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) - { + { std::vector<Geom::Point> points = cubic->points(); Geom::Point p0 = points[0]; Geom::Point p1 = points[1]; @@ -383,7 +383,7 @@ bool PovOutput::doCurve(SPItem *item, const String &id) nrNodes += 8; } else - { + { err("logical error, because pathv_to_linear_and_cubic_beziers was used"); return false; } @@ -444,7 +444,7 @@ bool PovOutput::doTreeRecursive(SPDocument *doc, SPObject *obj) { String id; - if (!obj->id) + if (!obj->getId()) { char buf[16]; sprintf(buf, "id%d", idIndex++); @@ -452,7 +452,7 @@ bool PovOutput::doTreeRecursive(SPDocument *doc, SPObject *obj) } else { - id = obj->id; + id = obj->getId(); } if (SP_IS_ITEM(obj)) @@ -467,9 +467,9 @@ bool PovOutput::doTreeRecursive(SPDocument *doc, SPObject *obj) */ for (SPObject *child = obj->firstChild() ; child ; child = child->next) { - if (!doTreeRecursive(doc, child)) - return false; - } + if (!doTreeRecursive(doc, child)) + return false; + } return true; } @@ -610,7 +610,7 @@ void PovOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8) err("Could not output curves for %s", filename_utf8); return; } - + String curveBuf = outbuf; outbuf.clear(); diff --git a/src/extension/internal/svg.cpp b/src/extension/internal/svg.cpp index a3589e905..b10aa87ec 100644 --- a/src/extension/internal/svg.cpp +++ b/src/extension/internal/svg.cpp @@ -21,6 +21,7 @@ #include "extension/system.h" #include "extension/output.h" #include <vector> +#include "xml/attribute-record.h" #ifdef WITH_GNOME_VFS # include <libgnomevfs/gnome-vfs.h> @@ -32,6 +33,37 @@ namespace Internal { #include "clear-n_.h" + +using Inkscape::Util::List; +using Inkscape::XML::AttributeRecord; +using Inkscape::XML::Node; + + + +void pruneExtendedAttributes( Inkscape::XML::Node *repr ) +{ + if (repr) { + if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) { + std::vector<gchar const*> toBeRemoved; + for ( List<AttributeRecord const> it = repr->attributeList(); it; ++it ) { + const gchar* attrName = g_quark_to_string(it->key); + if ((strncmp("inkscape:", attrName, 9) == 0) || (strncmp("sodipodi:", attrName, 9) == 0)) { + toBeRemoved.push_back(attrName); + } + } + // Can't change the set we're interating over while we are iterating. + for ( std::vector<gchar const*>::iterator it = toBeRemoved.begin(); it != toBeRemoved.end(); ++it ) { + repr->setAttribute(*it, 0); + } + } + + for ( Node *child = repr->firstChild(); child; child = child->next() ) { + pruneExtendedAttributes(child); + } + } +} + + /** \return None \brief What would an SVG editor be without loading/saving SVG @@ -179,7 +211,7 @@ Svg::open (Inkscape::Extension::Input */*mod*/, const gchar *uri) we're getting good data. It also checks the module ID of the incoming module to figure out whether this save should include the Inkscape namespace stuff or not. The result of that comparison - is stored in the spns variable. + is stored in the exportExtensions variable. If there is not to be Inkscape name spaces a new document is created without. (I think, I'm not sure on this code) @@ -198,18 +230,20 @@ Svg::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena gchar *save_path = g_path_get_dirname(filename); - bool const spns = ( !mod->get_id() + bool const exportExtensions = ( !mod->get_id() || !strcmp (mod->get_id(), SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE) || !strcmp (mod->get_id(), SP_MODULE_KEY_OUTPUT_SVGZ_INKSCAPE)); Inkscape::XML::Document *rdoc = NULL; Inkscape::XML::Node *repr = NULL; - if (spns) { + if (exportExtensions) { repr = sp_document_repr_root (doc); } else { rdoc = sp_repr_document_new ("svg:svg"); repr = rdoc->root(); repr = sp_document_root (doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD); + + pruneExtendedAttributes(repr); } if (!sp_repr_save_rebased_file(repr->document(), filename, SP_SVG_NS_URI, @@ -217,7 +251,7 @@ Svg::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filena throw Inkscape::Extension::Output::save_failed(); } - if (!spns) { + if (!exportExtensions) { Inkscape::GC::release(rdoc); } diff --git a/src/extension/param/parameter.cpp b/src/extension/param/parameter.cpp index 2773af61d..3320cddca 100644 --- a/src/extension/param/parameter.cpp +++ b/src/extension/param/parameter.cpp @@ -204,6 +204,16 @@ Parameter::get_enum (const SPDocument * doc, const Inkscape::XML::Node * node) return param->get(doc, node); } +/** \brief Wrapper to cast to the object and use it's function. */ +gchar const *Parameter::get_optiongroup(SPDocument const * doc, Inkscape::XML::Node const * node) +{ + ParamRadioButton * param = dynamic_cast<ParamRadioButton *>(this); + if (!param) { + throw Extension::param_not_optiongroup_param(); + } + return param->get(doc, node); +} + guint32 Parameter::get_color(const SPDocument* doc, const Inkscape::XML::Node* node) { @@ -253,6 +263,17 @@ Parameter::set_string (const gchar * in, SPDocument * doc, Inkscape::XML::Node * throw Extension::param_not_string_param(); return stringpntr->set(in, doc, node); } + +gchar const * Parameter::set_optiongroup( gchar const * in, SPDocument * doc, Inkscape::XML::Node * node ) +{ + ParamRadioButton *param = dynamic_cast<ParamRadioButton *>(this); + if (!param) { + throw Extension::param_not_optiongroup_param(); + } + return param->set(in, doc, node); +} + + /** \brief Wrapper to cast to the object and use it's function. */ guint32 Parameter::set_color (guint32 in, SPDocument * doc, Inkscape::XML::Node * node) diff --git a/src/extension/param/parameter.h b/src/extension/param/parameter.h index 54249c12e..c62dad9cc 100644 --- a/src/extension/param/parameter.h +++ b/src/extension/param/parameter.h @@ -98,9 +98,13 @@ public: const gchar * get_enum (const SPDocument * doc, const Inkscape::XML::Node * node); + gchar const * get_optiongroup( SPDocument const * doc, + Inkscape::XML::Node const * node); + bool set_bool (bool in, SPDocument * doc, Inkscape::XML::Node * node); int set_int (int in, SPDocument * doc, Inkscape::XML::Node * node); float set_float (float in, SPDocument * doc, Inkscape::XML::Node * node); + gchar const * set_optiongroup(gchar const *in, SPDocument * doc, Inkscape::XML::Node *node); const gchar * set_string (const gchar * in, SPDocument * doc, Inkscape::XML::Node * node); guint32 set_color (guint32 in, SPDocument * doc, Inkscape::XML::Node * node); diff --git a/src/gc-allocator.h b/src/gc-allocator.h index 4d809cfe4..e69de29bb 100644 --- a/src/gc-allocator.h +++ b/src/gc-allocator.h @@ -1,106 +0,0 @@ -/** @file - * @brief Garbage-collected STL allocator for standard containers - */ -/* Authors: - * Krzysztof Kosiński <tweenk.pl@gmail.com> - * - * Copyright 2008 Authors - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * See the file COPYING for details. - */ - -#ifndef SEEN_INKSCAPE_GC_ALLOCATOR_H -#define SEEN_INKSCAPE_GC_ALLOCATOR_H - -#include <cstddef> -#include <limits> -#include "gc-core.h" - -namespace Inkscape { -namespace GC { - -/** - * @brief Garbage-collected allocator for the standard containers - * - * STL containers with default parameters cannot be used as members in garbage-collected - * objects, because by default the destructors are not called, causing a memory leak - * (the memory allocated by the container is not freed). To address this, STL containers - * can be told to use this garbage-collected allocator. It usually is the last template - * parameter. For example, to define a GC-managed map of ints to Unicode strings: - * - * @code typedef std::map<int, Glib::ustring, less<int>, Inkscape::GC::Allocator> gcmap; @endcode - * - * Afterwards, you can place gcmap as a member in a non-finalized GC-managed object, because - * all memory used by gcmap will also be reclaimable by the garbage collector, therefore - * avoiding memory leaks. - */ -template <typename T> -class Allocator { - // required typedefs - typedef T value_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef T * pointer; - typedef T const * const_pointer; - typedef T & reference; - typedef T const & const_reference; - - // required structure that allows accessing the same allocator for a different type - template <typename U> - struct rebind { - typedef Allocator<U> other; - }; - - // constructors - no-ops since the allocator doesn't have any state - Allocator() throw() {} - Allocator(Allocator const &) throw() {} - template <typename U> Allocator(Allocator<U> const &) throw() {} - ~Allocator() throw() {} - - // trivial required methods - pointer address(reference ref) { return &ref; } - const_pointer address(const_reference ref) { return &ref; } - void construct(pointer p, T const &value) { new (static_cast<void*>(p)) T(value); } - void destroy(pointer p) { p->~T(); } - - // maximum meaningful memory amount that can be requested from the allocator - size_type max_size() { - return numeric_limits<size_type>::max() / sizeof(T); - } - - // allocate memory for num elements without initializing them - pointer allocate(size_type num, Allocator<void>::const_pointer) { - return static_cast<pointer>( Inkscape::GC::Core::malloc(num * sizeof(T)) ); - } - - // deallocate memory at p - void deallocate(pointer p, size_type) { - Inkscape::GC::Core::free(p); - } -}; - -// required comparison operators -template <typename T1, typename T2> -bool operator==(Allocator<T1> const &, Allocator<T2> const &) { return true; } -template <typename T1, typename T2> -bool operator!=(Allocator<T1> const &, Allocator<T2> const &) { return false; } - -} // namespace GC -} // namespace Inkscape - -#endif // !SEEN_INKSCAPE_GC_ALLOCATOR_H -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp index 4abd7483f..c95c1b2c5 100644 --- a/src/gradient-chemistry.cpp +++ b/src/gradient-chemistry.cpp @@ -1,5 +1,3 @@ -#define __SP_GRADIENT_CHEMISTRY_C__ - /* * Various utility methods for gradients * @@ -7,7 +5,9 @@ * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org> * + * Copyright (C) 2010 Authors * Copyright (C) 2007 Johan Engelen * Copyright (C) 2001-2005 authors * Copyright (C) 2001 Ximian, Inc. @@ -62,7 +62,7 @@ sp_gradient_ensure_vector_normalized(SPGradient *gr) if (gr->state == SP_GRADIENT_STATE_VECTOR) return gr; /* Fail, if we have wrong state set */ if (gr->state != SP_GRADIENT_STATE_UNKNOWN) { - g_warning("file %s: line %d: Cannot normalize private gradient to vector (%s)", __FILE__, __LINE__, SP_OBJECT_ID(gr)); + g_warning("file %s: line %d: Cannot normalize private gradient to vector (%s)", __FILE__, __LINE__, gr->getId()); return NULL; } @@ -190,7 +190,7 @@ sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *vector, // Check the number of uses of the gradient within this object; // if we are private and there are no other users, - if (SP_OBJECT_HREFCOUNT(gr) <= count_gradient_hrefs(user, gr)) { + if (!vector->isSwatch() && (SP_OBJECT_HREFCOUNT(gr) <= count_gradient_hrefs(user, gr))) { // check vector if ( gr != vector && gr->ref->getObject() != vector ) { /* our href is not the vector, and vector is different from gr; relink */ @@ -264,7 +264,7 @@ sp_gradient_fork_vector_if_necessary (SPGradient *gr) SPGradient * sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector) { - SPGradient *vector = sp_gradient_get_vector (gradient, force_vector); + SPGradient *vector = gradient->getVector(force_vector); vector = sp_gradient_fork_vector_if_necessary (vector); if ( gradient != vector && gradient->ref->getObject() != vector ) { sp_gradient_repr_set_link(SP_OBJECT_REPR(gradient), vector); @@ -336,7 +336,7 @@ sp_gradient_convert_to_userspace(SPGradient *gr, SPItem *item, gchar const *prop g_return_val_if_fail(SP_IS_GRADIENT(gr), NULL); // First, fork it if it is shared - gr = sp_gradient_fork_private_if_necessary(gr, sp_gradient_get_vector(gr, FALSE), + gr = sp_gradient_fork_private_if_necessary(gr, gr->getVector(), SP_IS_RADIALGRADIENT(gr) ? SP_GRADIENT_TYPE_RADIAL : SP_GRADIENT_TYPE_LINEAR, SP_OBJECT(item)); if (gr->units == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { @@ -482,107 +482,45 @@ sp_item_gradient (SPItem *item, bool fill_or_stroke) return gradient; } - -SPStop* -sp_first_stop(SPGradient *gradient) -{ - for (SPObject *ochild = sp_object_first_child(gradient); ochild != NULL; ochild = SP_OBJECT_NEXT(ochild)) { - if (SP_IS_STOP (ochild)) - return SP_STOP(ochild); - } - return NULL; -} - -SPStop* -sp_prev_stop(SPStop *stop, SPGradient *gradient) -{ - if (sp_object_first_child(SP_OBJECT(gradient)) == SP_OBJECT(stop)) - return NULL; - SPObject *found = NULL; - for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { - if (SP_IS_STOP (ochild)) { - found = ochild; - } - if (SP_OBJECT_NEXT(ochild) == SP_OBJECT(stop) || SP_OBJECT(ochild) == SP_OBJECT(stop)) { - break; - } - } - return SP_STOP(found); -} - -SPStop* -sp_next_stop(SPStop *stop) -{ - for (SPObject *ochild = SP_OBJECT_NEXT(stop); ochild != NULL; ochild = SP_OBJECT_NEXT(ochild)) { - if (SP_IS_STOP (ochild)) - return SP_STOP(ochild); - } - return NULL; -} - -SPStop* -sp_last_stop(SPGradient *gradient) +SPStop* sp_last_stop(SPGradient *gradient) { - for (SPStop *stop = sp_first_stop (gradient); stop != NULL; stop = sp_next_stop (stop)) { - if (sp_next_stop (stop) == NULL) + for (SPStop *stop = gradient->getFirstStop(); stop != NULL; stop = stop->getNextStop()) { + if (stop->getNextStop() == NULL) return stop; } return NULL; -} - -guint -sp_number_of_stops(SPGradient *gradient) -{ - guint n = 0; - for (SPStop *stop = sp_first_stop (gradient); stop != NULL; stop = sp_next_stop (stop)) { - if (sp_next_stop (stop) == NULL) - return n; - n ++; - } - return n; -} - -guint -sp_number_of_stops_before_stop(SPGradient *gradient, SPStop *target) -{ - guint n = 0; - for (SPStop *stop = sp_first_stop (gradient); stop != NULL; stop = sp_next_stop (stop)) { - if (stop == target) - return n; - n ++; - } - return n; -} - +} SPStop* sp_get_stop_i(SPGradient *gradient, guint stop_i) -{ - SPStop *stop = sp_first_stop (gradient); +{ + SPStop *stop = gradient->getFirstStop(); - // if this is valid but weird gradient without an offset-zero stop element, + // if this is valid but weird gradient without an offset-zero stop element, // inkscape has created a handle for the start of gradient anyway, // so when it asks for stop N that corresponds to stop element N-1 if (stop->offset != 0) stop_i --; - - for (guint i=0; i < stop_i; i++) { - if (!stop) return NULL; - stop = sp_next_stop (stop); - } - + + for (guint i = 0; i < stop_i; i++) { + if (!stop) { + return NULL; + } + stop = stop->getNextStop(); + } + return stop; } guint32 average_color (guint32 c1, guint32 c2, gdouble p) { - guint32 r = (guint32) (SP_RGBA32_R_U (c1) * (1 - p) + SP_RGBA32_R_U (c2) * p); - guint32 g = (guint32) (SP_RGBA32_G_U (c1) * (1 - p) + SP_RGBA32_G_U (c2) * p); - guint32 b = (guint32) (SP_RGBA32_B_U (c1) * (1 - p) + SP_RGBA32_B_U (c2) * p); - guint32 a = (guint32) (SP_RGBA32_A_U (c1) * (1 - p) + SP_RGBA32_A_U (c2) * p); + guint32 r = (guint32) (SP_RGBA32_R_U (c1) * (1 - p) + SP_RGBA32_R_U (c2) * p); + guint32 g = (guint32) (SP_RGBA32_G_U (c1) * (1 - p) + SP_RGBA32_G_U (c2) * p); + guint32 b = (guint32) (SP_RGBA32_B_U (c1) * (1 - p) + SP_RGBA32_B_U (c2) * p); + guint32 a = (guint32) (SP_RGBA32_A_U (c1) * (1 - p) + SP_RGBA32_A_U (c2) * p); - return SP_RGBA32_U_COMPOSE (r, g, b, a); + return SP_RGBA32_U_COMPOSE (r, g, b, a); } SPStop * @@ -617,13 +555,13 @@ sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool if (!gradient || !SP_IS_GRADIENT(gradient)) return; - SPGradient *vector = sp_gradient_get_vector (gradient, false); + SPGradient *vector = gradient->getVector(); switch (point_type) { case POINT_LG_BEGIN: case POINT_RG_CENTER: case POINT_RG_FOCUS: { - GtkWidget *dialog = sp_gradient_vector_editor_new (vector, sp_first_stop (vector)); + GtkWidget *dialog = sp_gradient_vector_editor_new (vector, vector->getFirstStop()); gtk_widget_show (dialog); } break; @@ -636,7 +574,7 @@ sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool gtk_widget_show (dialog); } break; - + case POINT_LG_MID: case POINT_RG_MID1: case POINT_RG_MID2: @@ -658,7 +596,7 @@ sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i if (!gradient || !SP_IS_GRADIENT(gradient)) return 0; - SPGradient *vector = sp_gradient_get_vector (gradient, false); + SPGradient *vector = gradient->getVector(); if (!vector) // orphan! return 0; // what else to do? @@ -668,7 +606,7 @@ sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i case POINT_RG_CENTER: case POINT_RG_FOCUS: { - SPStop *first = sp_first_stop (vector); + SPStop *first = vector->getFirstStop(); if (first) { return sp_stop_get_rgba32(first); } @@ -685,7 +623,7 @@ sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint point_i } } break; - + case POINT_LG_MID: case POINT_RG_MID1: case POINT_RG_MID2: @@ -711,7 +649,7 @@ sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, if (!gradient || !SP_IS_GRADIENT(gradient)) return; - SPGradient *vector = sp_gradient_get_vector (gradient, false); + SPGradient *vector = gradient->getVector(); if (!vector) // orphan! return; @@ -726,7 +664,7 @@ sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, case POINT_RG_CENTER: case POINT_RG_FOCUS: { - SPStop *first = sp_first_stop (vector); + SPStop *first = vector->getFirstStop(); if (first) { sp_repr_css_change (SP_OBJECT_REPR (first), stop, "style"); } @@ -743,7 +681,7 @@ sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, } } break; - + case POINT_LG_MID: case POINT_RG_MID1: case POINT_RG_MID2: @@ -754,7 +692,7 @@ sp_item_gradient_stop_set_style (SPItem *item, guint point_type, guint point_i, } } break; - + default: break; } @@ -767,7 +705,7 @@ sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke) if (!gradient || !SP_IS_GRADIENT(gradient)) return; - SPGradient *vector = sp_gradient_get_vector (gradient, false); + SPGradient *vector = gradient->getVector(); if (!vector) // orphan! return; @@ -873,9 +811,9 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom } else { SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); } - break; + break; case POINT_LG_MID: - { + { // using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end. double offset = get_offset_between_points (p, Geom::Point(lg->x1.computed, lg->y1.computed), Geom::Point(lg->x2.computed, lg->y2.computed)); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (lg, false); @@ -890,10 +828,10 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom } } break; - default: - break; - } - } else if (SP_IS_RADIALGRADIENT(gradient)) { + default: + break; + } + } else if (SP_IS_RADIALGRADIENT(gradient)) { SPRadialGradient *rg = SP_RADIALGRADIENT(gradient); Geom::Point c (rg->cx.computed, rg->cy.computed); Geom::Point c_w = c * gradient->gradientTransform * i2d; // now in desktop coords @@ -904,66 +842,66 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom Geom::Matrix new_transform; bool transform_set = false; - switch (point_type) { - case POINT_RG_CENTER: - rg->fx.computed = p[Geom::X] + (rg->fx.computed - rg->cx.computed); - rg->fy.computed = p[Geom::Y] + (rg->fy.computed - rg->cy.computed); - rg->cx.computed = p[Geom::X]; - rg->cy.computed = p[Geom::Y]; - if (write_repr) { - sp_repr_set_svg_double(repr, "fx", rg->fx.computed); - sp_repr_set_svg_double(repr, "fy", rg->fy.computed); - sp_repr_set_svg_double(repr, "cx", rg->cx.computed); - sp_repr_set_svg_double(repr, "cy", rg->cy.computed); - } else { - SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); - } - break; - case POINT_RG_FOCUS: - rg->fx.computed = p[Geom::X]; - rg->fy.computed = p[Geom::Y]; - if (write_repr) { - sp_repr_set_svg_double(repr, "fx", rg->fx.computed); - sp_repr_set_svg_double(repr, "fy", rg->fy.computed); - } else { - SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); - } - break; - case POINT_RG_R1: - { + switch (point_type) { + case POINT_RG_CENTER: + rg->fx.computed = p[Geom::X] + (rg->fx.computed - rg->cx.computed); + rg->fy.computed = p[Geom::Y] + (rg->fy.computed - rg->cy.computed); + rg->cx.computed = p[Geom::X]; + rg->cy.computed = p[Geom::Y]; + if (write_repr) { + sp_repr_set_svg_double(repr, "fx", rg->fx.computed); + sp_repr_set_svg_double(repr, "fy", rg->fy.computed); + sp_repr_set_svg_double(repr, "cx", rg->cx.computed); + sp_repr_set_svg_double(repr, "cy", rg->cy.computed); + } else { + SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); + } + break; + case POINT_RG_FOCUS: + rg->fx.computed = p[Geom::X]; + rg->fy.computed = p[Geom::Y]; + if (write_repr) { + sp_repr_set_svg_double(repr, "fx", rg->fx.computed); + sp_repr_set_svg_double(repr, "fy", rg->fy.computed); + } else { + SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); + } + break; + case POINT_RG_R1: + { Geom::Point r1_w = (c + Geom::Point(rg->r.computed, 0)) * gradient->gradientTransform * i2d; - double r1_angle = Geom::atan2(r1_w - c_w); - double move_angle = Geom::atan2(p_w - c_w) - r1_angle; - double move_stretch = Geom::L2(p_w - c_w) / Geom::L2(r1_w - c_w); - - Geom::Matrix move = Geom::Matrix (Geom::Translate (-c_w)) * - Geom::Matrix (Geom::Rotate(-r1_angle)) * - Geom::Matrix (Geom::Scale(move_stretch, scale? move_stretch : 1)) * - Geom::Matrix (Geom::Rotate(r1_angle)) * - Geom::Matrix (Geom::Rotate(move_angle)) * - Geom::Matrix (Geom::Translate (c_w)); - - new_transform = gradient->gradientTransform * i2d * move * i2d.inverse(); - transform_set = true; - - break; - } - case POINT_RG_R2: - { - Geom::Point r2_w = (c + Geom::Point(0, -rg->r.computed)) * gradient->gradientTransform * i2d; - double r2_angle = Geom::atan2(r2_w - c_w); - double move_angle = Geom::atan2(p_w - c_w) - r2_angle; - double move_stretch = Geom::L2(p_w - c_w) / Geom::L2(r2_w - c_w); - - Geom::Matrix move = Geom::Matrix (Geom::Translate (-c_w)) * - Geom::Matrix (Geom::Rotate(-r2_angle)) * - Geom::Matrix (Geom::Scale(move_stretch, scale? move_stretch : 1)) * - Geom::Matrix (Geom::Rotate(r2_angle)) * - Geom::Matrix (Geom::Rotate(move_angle)) * - Geom::Matrix (Geom::Translate (c_w)); - - new_transform = gradient->gradientTransform * i2d * move * i2d.inverse(); - transform_set = true; + double r1_angle = Geom::atan2(r1_w - c_w); + double move_angle = Geom::atan2(p_w - c_w) - r1_angle; + double move_stretch = Geom::L2(p_w - c_w) / Geom::L2(r1_w - c_w); + + Geom::Matrix move = Geom::Matrix (Geom::Translate (-c_w)) * + Geom::Matrix (Geom::Rotate(-r1_angle)) * + Geom::Matrix (Geom::Scale(move_stretch, scale? move_stretch : 1)) * + Geom::Matrix (Geom::Rotate(r1_angle)) * + Geom::Matrix (Geom::Rotate(move_angle)) * + Geom::Matrix (Geom::Translate (c_w)); + + new_transform = gradient->gradientTransform * i2d * move * i2d.inverse(); + transform_set = true; + + break; + } + case POINT_RG_R2: + { + Geom::Point r2_w = (c + Geom::Point(0, -rg->r.computed)) * gradient->gradientTransform * i2d; + double r2_angle = Geom::atan2(r2_w - c_w); + double move_angle = Geom::atan2(p_w - c_w) - r2_angle; + double move_stretch = Geom::L2(p_w - c_w) / Geom::L2(r2_w - c_w); + + Geom::Matrix move = Geom::Matrix (Geom::Translate (-c_w)) * + Geom::Matrix (Geom::Rotate(-r2_angle)) * + Geom::Matrix (Geom::Scale(move_stretch, scale? move_stretch : 1)) * + Geom::Matrix (Geom::Rotate(r2_angle)) * + Geom::Matrix (Geom::Rotate(move_angle)) * + Geom::Matrix (Geom::Translate (c_w)); + + new_transform = gradient->gradientTransform * i2d * move * i2d.inverse(); + transform_set = true; break; } @@ -974,7 +912,7 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom double offset = get_offset_between_points (p, start, end); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (rg, false); sp_gradient_ensure_vector(rg); - rg->vector.stops.at(point_i).offset = offset; + rg->vector.stops.at(point_i).offset = offset; SPStop* stopi = sp_get_stop_i(vector, point_i); stopi->offset = offset; if (write_repr) { @@ -999,19 +937,20 @@ sp_item_gradient_set_coords (SPItem *item, guint point_type, guint point_i, Geom SP_OBJECT (stopi)->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); } break; + } + + if (transform_set) { + gradient->gradientTransform = new_transform; + gradient->gradientTransform_set = TRUE; + if (write_repr) { + gchar *s=sp_svg_transform_write(gradient->gradientTransform); + SP_OBJECT_REPR(gradient)->setAttribute("gradientTransform", s); + g_free(s); + } else { + SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); } - if (transform_set) { - gradient->gradientTransform = new_transform; - gradient->gradientTransform_set = TRUE; - if (write_repr) { - gchar *s=sp_svg_transform_write(gradient->gradientTransform); - SP_OBJECT_REPR(gradient)->setAttribute("gradientTransform", s); - g_free(s); - } else { - SP_OBJECT (gradient)->requestModified(SP_OBJECT_MODIFIED_FLAG); - } - } - } + } + } } SPGradient * @@ -1019,8 +958,9 @@ sp_item_gradient_get_vector (SPItem *item, bool fill_or_stroke) { SPGradient *gradient = sp_item_gradient (item, fill_or_stroke); - if (gradient) - return sp_gradient_get_vector (gradient, false); + if (gradient) { + return gradient->getVector(); + } return NULL; } @@ -1060,7 +1000,7 @@ sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool p = Geom::Point (lg->x2.computed, lg->y2.computed); break; case POINT_LG_MID: - { + { gdouble offset = lg->vector.stops.at(point_i).offset; p = (1-offset) * Geom::Point(lg->x1.computed, lg->y1.computed) + offset * Geom::Point(lg->x2.computed, lg->y2.computed); } @@ -1085,13 +1025,13 @@ sp_item_gradient_get_coords (SPItem *item, guint point_type, guint point_i, bool { gdouble offset = rg->vector.stops.at(point_i).offset; p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed + rg->r.computed, rg->cy.computed); - } + } break; case POINT_RG_MID2: { gdouble offset = rg->vector.stops.at(point_i).offset; p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed, rg->cy.computed - rg->r.computed); - } + } break; } } @@ -1140,15 +1080,16 @@ sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_ /* Current fill style is the gradient of the required type */ SPGradient *current = SP_GRADIENT(ps); - //g_print("hrefcount %d count %d\n", SP_OBJECT_HREFCOUNT(ig), count_gradient_hrefs(SP_OBJECT(item), ig)); + //g_message("hrefcount %d count %d\n", SP_OBJECT_HREFCOUNT(current), count_gradient_hrefs(SP_OBJECT(item), current)); - if (SP_OBJECT_HREFCOUNT(current) == 1 || - SP_OBJECT_HREFCOUNT(current) == count_gradient_hrefs(SP_OBJECT(item), current)) { + if (!current->isSwatch() + && (SP_OBJECT_HREFCOUNT(current) == 1 || + SP_OBJECT_HREFCOUNT(current) == count_gradient_hrefs(SP_OBJECT(item), current))) { // current is private and it's either used once, or all its uses are by children of item; // so just change its href to vector - if ( current != gr && sp_gradient_get_vector(current, false) != gr ) { + if ( current != gr && current->getVector() != gr ) { /* href is not the vector */ sp_gradient_repr_set_link(SP_OBJECT_REPR(current), gr); } @@ -1184,25 +1125,20 @@ sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, bool is_ } } -static void -sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *link) +static void sp_gradient_repr_set_link(Inkscape::XML::Node *repr, SPGradient *link) { g_return_if_fail(repr != NULL); - g_return_if_fail(link != NULL); - g_return_if_fail(SP_IS_GRADIENT(link)); + if (link) { + g_return_if_fail(SP_IS_GRADIENT(link)); + } - gchar *ref; if (link) { - gchar const *id = SP_OBJECT_ID(link); - size_t const len = strlen(id); - ref = (gchar*) alloca(len + 2); - *ref = '#'; - memcpy(ref + 1, id, len + 1); + Glib::ustring ref("#"); + ref += link->getId(); + repr->setAttribute("xlink:href", ref.c_str()); } else { - ref = NULL; + repr->setAttribute("xlink:href", 0); } - - repr->setAttribute("xlink:href", ref); } /* @@ -1286,7 +1222,7 @@ sp_gradient_vector_for_object(SPDocument *const doc, SPDesktop *const desktop, if (paint.isPaintserver()) { SPObject *server = is_fill? SP_OBJECT_STYLE_FILL_SERVER(o) : SP_OBJECT_STYLE_STROKE_SERVER(o); if (SP_IS_GRADIENT (server)) { - return sp_gradient_get_vector(SP_GRADIENT (server), TRUE); + return SP_GRADIENT(server)->getVector(true); } else { rgba = sp_desktop_get_color(desktop, is_fill); } diff --git a/src/gradient-chemistry.h b/src/gradient-chemistry.h index 73b9893bc..3f72fa394 100644 --- a/src/gradient-chemistry.h +++ b/src/gradient-chemistry.h @@ -1,5 +1,5 @@ -#ifndef __SP_GRADIENT_CHEMISTRY_H__ -#define __SP_GRADIENT_CHEMISTRY_H__ +#ifndef SEEN_SP_GRADIENT_CHEMISTRY_H +#define SEEN_SP_GRADIENT_CHEMISTRY_H /* * Various utility methods for gradients @@ -8,7 +8,9 @@ * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org> * + * Copyright (C) 2010 Authors * Copyright (C) 2007 Johan Engelen * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. @@ -54,13 +56,10 @@ SPGradient *sp_gradient_fork_vector_if_necessary (SPGradient *gr); SPGradient *sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector); -SPStop* sp_first_stop(SPGradient *gradient); SPStop* sp_last_stop(SPGradient *gradient); -SPStop* sp_prev_stop(SPStop *stop, SPGradient *gradient); -SPStop* sp_next_stop(SPStop *stop); SPStop* sp_get_stop_i(SPGradient *gradient, guint i); -guint sp_number_of_stops(SPGradient *gradient); -guint sp_number_of_stops_before_stop(SPGradient *gradient, SPStop *target); +guint sp_number_of_stops(SPGradient const *gradient); +guint sp_number_of_stops_before_stop(SPGradient const *gradient, SPStop *target); guint32 average_color (guint32 c1, guint32 c2, gdouble p = 0.5); @@ -80,7 +79,7 @@ guint32 sp_item_gradient_stop_query_style (SPItem *item, guint point_type, guint void sp_item_gradient_edit_stop (SPItem *item, guint point_type, guint point_i, bool fill_or_stroke); void sp_item_gradient_reverse_vector (SPItem *item, bool fill_or_stroke); -#endif +#endif // SEEN_SP_GRADIENT_CHEMISTRY_H /* Local Variables: diff --git a/src/gradient-context.cpp b/src/gradient-context.cpp index fc5c1af44..ed20f9b61 100644 --- a/src/gradient-context.cpp +++ b/src/gradient-context.cpp @@ -148,7 +148,7 @@ gradient_selection_changed (Inkscape::Selection *, gpointer data) GrDrag *drag = rc->_grdrag; Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(rc)->desktop); if (selection == NULL) { - return; + return; } guint n_obj = g_slist_length((GSList *) selection->itemList()); @@ -160,34 +160,34 @@ gradient_selection_changed (Inkscape::Selection *, gpointer data) //The use of ngettext in the following code is intentional even if the English singular form would never be used if (n_sel == 1) { if (drag->singleSelectedDraggerNumDraggables() == 1) { - gchar * message = g_strconcat( - //TRANSLATORS: %s will be substituted with the point name (see previous messages); This is part of a compound message - _("%s selected"), - //TRANSLATORS: Mind the space in front. This is part of a compound message - ngettext(" out of %d gradient handle"," out of %d gradient handles",n_tot), - ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); - rc->_message_context->setF(Inkscape::NORMAL_MESSAGE, - message,_(gr_handle_descr[drag->singleSelectedDraggerSingleDraggableType()]), n_tot, n_obj); + gchar * message = g_strconcat( + //TRANSLATORS: %s will be substituted with the point name (see previous messages); This is part of a compound message + _("%s selected"), + //TRANSLATORS: Mind the space in front. This is part of a compound message + ngettext(" out of %d gradient handle"," out of %d gradient handles",n_tot), + ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); + rc->_message_context->setF(Inkscape::NORMAL_MESSAGE, + message,_(gr_handle_descr[drag->singleSelectedDraggerSingleDraggableType()]), n_tot, n_obj); } else { - gchar * message = g_strconcat( - //TRANSLATORS: This is a part of a compound message (out of two more indicating: grandint handle count & object count) - ngettext("One handle merging %d stop (drag with <b>Shift</b> to separate) selected", - "One handle merging %d stops (drag with <b>Shift</b> to separate) selected",drag->singleSelectedDraggerNumDraggables()), - ngettext(" out of %d gradient handle"," out of %d gradient handles",n_tot), - ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); - rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message,drag->singleSelectedDraggerNumDraggables(), n_tot, n_obj); + gchar * message = g_strconcat( + //TRANSLATORS: This is a part of a compound message (out of two more indicating: grandint handle count & object count) + ngettext("One handle merging %d stop (drag with <b>Shift</b> to separate) selected", + "One handle merging %d stops (drag with <b>Shift</b> to separate) selected",drag->singleSelectedDraggerNumDraggables()), + ngettext(" out of %d gradient handle"," out of %d gradient handles",n_tot), + ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); + rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message,drag->singleSelectedDraggerNumDraggables(), n_tot, n_obj); } } else if (n_sel > 1) { - //TRANSLATORS: The plural refers to number of selected gradient handles. This is part of a compound message (part two indicates selected object count) - gchar * message = g_strconcat(ngettext("<b>%d</b> gradient handle selected out of %d","<b>%d</b> gradient handles selected out of %d",n_sel), - //TRANSLATORS: Mind the space in front. (Refers to gradient handles selected). This is part of a compound message - ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); + //TRANSLATORS: The plural refers to number of selected gradient handles. This is part of a compound message (part two indicates selected object count) + gchar * message = g_strconcat(ngettext("<b>%d</b> gradient handle selected out of %d","<b>%d</b> gradient handles selected out of %d",n_sel), + //TRANSLATORS: Mind the space in front. (Refers to gradient handles selected). This is part of a compound message + ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message, n_sel, n_tot, n_obj); } else if (n_sel == 0) { rc->_message_context->setF(Inkscape::NORMAL_MESSAGE, - //TRANSLATORS: The plural refers to number of selected objects - ngettext("<b>No</b> gradient handles selected out of %d on %d selected object", - "<b>No</b> gradient handles selected out of %d on %d selected objects",n_obj), n_tot, n_obj); + //TRANSLATORS: The plural refers to number of selected objects + ngettext("<b>No</b> gradient handles selected out of %d on %d selected object", + "<b>No</b> gradient handles selected out of %d on %d selected objects",n_obj), n_tot, n_obj); } } @@ -291,7 +291,7 @@ sp_gradient_context_get_stop_intervals (GrDrag *drag, GSList **these_stops, GSLi // from draggables to stops SPStop *this_stop = sp_get_stop_i (vector, d->point_i); - SPStop *next_stop = sp_next_stop (this_stop); + SPStop *next_stop = this_stop->getNextStop(); SPStop *last_stop = sp_last_stop (vector); gint fs = d->fill_or_stroke; @@ -362,7 +362,7 @@ sp_gradient_context_add_stops_between_selected_stops (SPGradientContext *rc) SPGradient *gradient = sp_item_gradient (d->item, d->fill_or_stroke); SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false); SPStop *this_stop = sp_get_stop_i (vector, d->point_i); - SPStop *next_stop = sp_next_stop (this_stop); + SPStop *next_stop = this_stop->getNextStop(); if (this_stop && next_stop) { these_stops = g_slist_prepend (these_stops, this_stop); next_stops = g_slist_prepend (next_stops, next_stop); @@ -558,7 +558,7 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, button_dt, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); rc->origin = from_2geom(button_dt); } @@ -593,6 +593,15 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) ret = TRUE; } else { + if (!drag->mouseOver()) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt = event_context->desktop->w2d(motion_w); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); + } + bool over_line = false; if (drag->lines) { for (GSList *l = drag->lines; l != NULL; l = l->next) { diff --git a/src/gradient-context.h b/src/gradient-context.h index f4d8c572a..6f8a804ae 100644 --- a/src/gradient-context.h +++ b/src/gradient-context.h @@ -7,9 +7,10 @@ * Authors: * bulia byak <buliabyak@users.sf.net> * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org. * * Copyright (C) 2007 Johan Engelen - * Copyright (C) 2005 Authors + * Copyright (C) 2005,2010 Authors * * Released under GNU GPL */ @@ -29,13 +30,13 @@ class SPGradientContextClass; struct SPGradientContext : public SPEventContext { Geom::Point origin; - + bool cursor_addnode; - + bool node_added; Geom::Point mousepoint_doc; // stores mousepoint when over_line in doc coords - + Inkscape::MessageContext *_message_context; sigc::connection *selcon; diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index e61bd9552..9796fc5da 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -6,9 +6,10 @@ * Authors: * bulia byak <buliabyak@users.sf.net> * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2007 Johan Engelen - * Copyright (C) 2005 Authors + * Copyright (C) 2005,2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -332,12 +333,12 @@ GrDrag::addStopNearPoint (SPItem *item, Geom::Point mouse_p, double tolerance) if (addknot) { SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false); - SPStop* prev_stop = sp_first_stop(vector); - SPStop* next_stop = sp_next_stop(prev_stop); + SPStop* prev_stop = vector->getFirstStop(); + SPStop* next_stop = prev_stop->getNextStop(); guint i = 1; while ( (next_stop) && (next_stop->offset < offset) ) { prev_stop = next_stop; - next_stop = sp_next_stop(next_stop); + next_stop = next_stop->getNextStop(); i++; } if (!next_stop) { @@ -542,8 +543,8 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp Geom::Point p = ppointer; SPDesktop *desktop = dragger->parent->desktop; - SnapManager &m = desktop->namedview->snap_manager; - double snap_dist = m.snapprefs.getObjectTolerance() / dragger->parent->desktop->current_zoom(); + SnapManager &m = desktop->namedview->snap_manager; + double snap_dist = m.snapprefs.getObjectTolerance() / dragger->parent->desktop->current_zoom(); if (state & GDK_SHIFT_MASK) { // with Shift; unsnap if we carry more than one draggable @@ -594,49 +595,19 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp } } - if (!((state & GDK_SHIFT_MASK) || ((state & GDK_CONTROL_MASK) && (state & GDK_MOD1_MASK)))) { - // Try snapping to the grid or guides - m.setup(desktop); - Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p), Inkscape::SNAPSOURCE_HANDLE); + m.setup(desktop); + if (!((state & GDK_SHIFT_MASK) || (state & GDK_CONTROL_MASK))) { + Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE)); if (s.getSnapped()) { p = s.getPoint(); sp_knot_moveto (knot, p); - } else if (m.snapprefs.getSnapEnabledGlobally() && m.snapprefs.getSnapModeNode() && !(m.snapprefs.getSnapPostponedGlobally())) { - bool was_snapped = false; - double dist = NR_HUGE; - // No snapping so far, let's see if we need to snap to any of the levels - for (guint i = 0; i < dragger->parent->hor_levels.size(); i++) { - dist = fabs(p[Geom::Y] - dragger->parent->hor_levels[i]); - if (dist < snap_dist) { - p[Geom::Y] = dragger->parent->hor_levels[i]; - s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, 0, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false); - was_snapped = true; - sp_knot_moveto (knot, p); - } - } - for (guint i = 0; i < dragger->parent->vert_levels.size(); i++) { - dist = fabs(p[Geom::X] - dragger->parent->vert_levels[i]); - if (dist < snap_dist) { - p[Geom::X] = dragger->parent->vert_levels[i]; - s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, 0, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false); - was_snapped = true; - sp_knot_moveto (knot, p); - } - } - if (was_snapped) { - desktop->snapindicator->set_new_snaptarget(s); - } } - } - - if (state & GDK_CONTROL_MASK) { + } else if (state & GDK_CONTROL_MASK) { + SnappedConstraints sc; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); /* 0 means no snapping. */ - // This list will store snap vectors from all draggables of dragger - GSList *snap_vectors = NULL; - for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { GrDraggable *draggable = (GrDraggable *) i->data; @@ -687,25 +658,27 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp // with Ctrl, snap to M_PI/snaps snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0); } - } - if (snap_vector) { - snap_vectors = g_slist_prepend (snap_vectors, &(*snap_vector)); + if (snap_vector) { + Inkscape::Snapper::ConstraintLine cl(dr_snap, p + *snap_vector - dr_snap); + Inkscape::SnappedPoint s = m.constrainedSnap(Inkscape::SnapCandidatePoint(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE), cl); + if (s.getSnapped()) { + s.setTransformation(s.getPoint() - p); + sc.points.push_back(s); + } else { + Inkscape::SnappedPoint dummy(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE, 0, Inkscape::SNAPTARGET_CONSTRAINED_ANGLE, Geom::L2(*snap_vector), 10000, true, false); + dummy.setTransformation(*snap_vector); + sc.points.push_back(dummy); + } + } } } - // Move by the smallest of snap vectors: - Geom::Point move(9999, 9999); - for (GSList const *i = snap_vectors; i != NULL; i = i->next) { - Geom::Point *snap_vector = (Geom::Point *) i->data; - if (Geom::L2(*snap_vector) < Geom::L2(move)) - move = *snap_vector; - } - if (move[Geom::X] < 9999) { - p += move; + Inkscape::SnappedPoint bsp = m.findBestSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE), sc, true); // snap indicator will be displayed if needed + + if (bsp.getSnapped()) { + p += bsp.getTransformation(); sp_knot_moveto (knot, p); } - - g_slist_free(snap_vectors); } drag->keep_selection = (bool) g_list_find(drag->selected, dragger); @@ -835,6 +808,12 @@ gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, gu p = snap_vector_midpoint (p, low_lim, high_lim, snap_fraction); } else { p = snap_vector_midpoint (p, low_lim, high_lim, 0); + if (!(state & GDK_SHIFT_MASK)) { + SPDesktop *desktop = dragger->parent->desktop; + SnapManager &m = desktop->namedview->snap_manager; + Inkscape::Snapper::ConstraintLine cl(low_lim, high_lim - low_lim); + m.constrainedSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE, cl); + } } Geom::Point displacement = p - dragger->point; @@ -925,45 +904,45 @@ gr_knot_clicked_handler(SPKnot */*knot*/, guint state, gpointer data) if ( (state & GDK_CONTROL_MASK) && (state & GDK_MOD1_MASK ) ) { // delete this knot from vector - SPGradient *gradient = sp_item_gradient (draggable->item, draggable->fill_or_stroke); - gradient = sp_gradient_get_vector (gradient, false); - if (gradient->vector.stops.size() > 2) { // 2 is the minimum - SPStop *stop = NULL; - switch (draggable->point_type) { // if we delete first or last stop, move the next/previous to the edge - case POINT_LG_BEGIN: - case POINT_RG_CENTER: - stop = sp_first_stop(gradient); - { - SPStop *next = sp_next_stop (stop); - if (next) { - next->offset = 0; - sp_repr_set_css_double (SP_OBJECT_REPR (next), "offset", 0); - } - } - break; - case POINT_LG_END: - case POINT_RG_R1: - case POINT_RG_R2: - stop = sp_last_stop(gradient); - { - SPStop *prev = sp_prev_stop (stop, gradient); - if (prev) { - prev->offset = 1; - sp_repr_set_css_double (SP_OBJECT_REPR (prev), "offset", 1); - } - } - break; - case POINT_LG_MID: - case POINT_RG_MID1: - case POINT_RG_MID2: - stop = sp_get_stop_i(gradient, draggable->point_i); - break; - } - - SP_OBJECT_REPR(gradient)->removeChild(SP_OBJECT_REPR(stop)); - sp_document_done (SP_OBJECT_DOCUMENT (gradient), SP_VERB_CONTEXT_GRADIENT, - _("Delete gradient stop")); - } + SPGradient *gradient = sp_item_gradient (draggable->item, draggable->fill_or_stroke); + gradient = gradient->getVector(); + if (gradient->vector.stops.size() > 2) { // 2 is the minimum + SPStop *stop = NULL; + switch (draggable->point_type) { // if we delete first or last stop, move the next/previous to the edge + case POINT_LG_BEGIN: + case POINT_RG_CENTER: + stop = gradient->getFirstStop(); + { + SPStop *next = stop->getNextStop(); + if (next) { + next->offset = 0; + sp_repr_set_css_double (SP_OBJECT_REPR (next), "offset", 0); + } + } + break; + case POINT_LG_END: + case POINT_RG_R1: + case POINT_RG_R2: + stop = sp_last_stop(gradient); + { + SPStop *prev = stop->getPrevStop(); + if (prev) { + prev->offset = 1; + sp_repr_set_css_double (SP_OBJECT_REPR (prev), "offset", 1); + } + } + break; + case POINT_LG_MID: + case POINT_RG_MID1: + case POINT_RG_MID2: + stop = sp_get_stop_i(gradient, draggable->point_i); + break; + } + + SP_OBJECT_REPR(gradient)->removeChild(SP_OBJECT_REPR(stop)); + sp_document_done (SP_OBJECT_DOCUMENT (gradient), SP_VERB_CONTEXT_GRADIENT, + _("Delete gradient stop")); + } } else { // select the dragger dragger->point_original = dragger->point; @@ -1114,10 +1093,10 @@ Updates the statusbar tip of the dragger knot, based on its draggables void GrDragger::updateTip () { - if (this->knot && this->knot->tip) { - g_free (this->knot->tip); - this->knot->tip = NULL; - } + if (this->knot && this->knot->tip) { + g_free (this->knot->tip); + this->knot->tip = NULL; + } if (g_slist_length (this->draggables) == 1) { GrDraggable *draggable = (GrDraggable *) this->draggables->data; @@ -1182,7 +1161,10 @@ Moves this dragger to the point of the given draggable, acting upon all other dr void GrDragger::moveThisToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr) { - this->point = sp_item_gradient_get_coords (item, point_type, point_i, fill_or_stroke); + GrDraggable *dr_first = (GrDraggable *) this->draggables->data; + if (!dr_first) return; + + this->point = sp_item_gradient_get_coords (dr_first->item, dr_first->point_type, dr_first->point_i, dr_first->fill_or_stroke); this->point_original = this->point; sp_knot_moveto (this->knot, this->point); @@ -1681,6 +1663,22 @@ GrDrag::updateDraggers () } } + +/** + * \brief Returns true if at least one of the draggers' knots has the mouse hovering above it + */ + +bool +GrDrag::mouseOver() +{ + for (GList const* i = this->draggers; i != NULL; i = i->next) { + GrDragger *d = (GrDragger *) i->data; + if (d->knot && (d->knot->flags & SP_KNOT_MOUSEOVER)) { + return true; + } + } + return false; +} /** Regenerates the lines list from the current selection; is called on each move of a dragger, so that lines are always in sync with the actual gradient @@ -1958,7 +1956,7 @@ GrDrag::deleteSelected (bool just_one) { SPStop *stop = NULL; if ( (draggable->point_type == POINT_LG_BEGIN) || (draggable->point_type == POINT_RG_CENTER) ) { - stop = sp_first_stop(vector); + stop = vector->getFirstStop(); } else { stop = sp_last_stop(vector); } @@ -2019,7 +2017,7 @@ GrDrag::deleteSelected (bool just_one) SPLinearGradient *lg = SP_LINEARGRADIENT(stopinfo->gradient); Geom::Point oldbegin = Geom::Point (lg->x1.computed, lg->y1.computed); Geom::Point end = Geom::Point (lg->x2.computed, lg->y2.computed); - SPStop *stop = sp_first_stop(stopinfo->vector); + SPStop *stop = stopinfo->vector->getFirstStop(); gdouble offset = stop->offset; Geom::Point newbegin = oldbegin + offset * (end - oldbegin); lg->x1.computed = newbegin[Geom::X]; @@ -2033,11 +2031,11 @@ GrDrag::deleteSelected (bool just_one) // iterate through midstops to set new offset values such that they won't move on canvas. SPStop *laststop = sp_last_stop(stopinfo->vector); - stop = sp_next_stop(stop); + stop = stop->getNextStop(); while ( stop != laststop ) { stop->offset = (stop->offset - offset)/(1 - offset); sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset); - stop = sp_next_stop(stop); + stop = stop->getNextStop(); } } break; @@ -2061,18 +2059,18 @@ GrDrag::deleteSelected (bool just_one) sp_repr_set_css_double (SP_OBJECT_REPR (laststop), "offset", 1); // iterate through midstops to set new offset values such that they won't move on canvas. - SPStop *stop = sp_first_stop(stopinfo->vector); - stop = sp_next_stop(stop); + SPStop *stop = stopinfo->vector->getFirstStop(); + stop = stop->getNextStop(); while ( stop != laststop ) { stop->offset = stop->offset / offset; sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset); - stop = sp_next_stop(stop); + stop = stop->getNextStop(); } } break; case POINT_RG_CENTER: { - SPStop *newfirst = sp_next_stop (stopinfo->spstop); + SPStop *newfirst = stopinfo->spstop->getNextStop(); if (newfirst) { newfirst->offset = 0; sp_repr_set_css_double (SP_OBJECT_REPR (newfirst), "offset", 0); @@ -2097,12 +2095,12 @@ GrDrag::deleteSelected (bool just_one) sp_repr_set_css_double (SP_OBJECT_REPR (laststop), "offset", 1); // iterate through midstops to set new offset values such that they won't move on canvas. - SPStop *stop = sp_first_stop(stopinfo->vector); - stop = sp_next_stop(stop); + SPStop *stop = stopinfo->vector->getFirstStop(); + stop = stop->getNextStop(); while ( stop != laststop ) { stop->offset = stop->offset / offset; sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset); - stop = sp_next_stop(stop); + stop = stop->getNextStop(); } break; } diff --git a/src/gradient-drag.h b/src/gradient-drag.h index a53acffe6..460d1396e 100644 --- a/src/gradient-drag.h +++ b/src/gradient-drag.h @@ -154,6 +154,8 @@ public: // FIXME: make more of this private! void updateLines (); void updateLevels (); + bool mouseOver(); + void selected_move_nowrite (double x, double y, bool scale_radial); void selected_move (double x, double y, bool write_repr = true, bool scale_radial = false); void selected_move_screen (double x, double y); diff --git a/src/graphlayout/graphlayout.cpp b/src/graphlayout.cpp index 81ea59059..a89af10af 100644 --- a/src/graphlayout/graphlayout.cpp +++ b/src/graphlayout.cpp @@ -23,7 +23,7 @@ #include "inkscape.h" #include "sp-namedview.h" #include "util/glib-list-iterators.h" -#include "graphlayout/graphlayout.h" +#include "graphlayout.h" #include "sp-path.h" #include "sp-item.h" #include "sp-item-transform.h" @@ -44,21 +44,21 @@ using namespace vpsc; * Returns true if item is a connector */ bool isConnector(SPItem const *const i) { - SPPath *path = NULL; - if(SP_IS_PATH(i)) { - path = SP_PATH(i); - } - return path && path->connEndPair.isAutoRoutingConn(); + SPPath *path = NULL; + if(SP_IS_PATH(i)) { + path = SP_PATH(i); + } + return path && path->connEndPair.isAutoRoutingConn(); } struct CheckProgress : TestConvergence { CheckProgress(double d,unsigned i,list<SPItem *>& - selected,vector<Rectangle*>& rs,map<string,unsigned>& nodelookup) : + selected,vector<Rectangle*>& rs,map<string,unsigned>& nodelookup) : TestConvergence(d,i), selected(selected), rs(rs), nodelookup(nodelookup) {} - bool operator()(double new_stress, double* X, double* Y) { + bool operator()(double new_stress, double* X, double* Y) { /* This is where, if we wanted to animate the layout, we would need to update * the positions of all objects and redraw the canvas and maybe sleep a bit - cout << "stress="<<new_stress<<endl; + cout << "stress="<<new_stress<<endl; cout << "x[0]="<<rs[0]->getMinX()<<endl; for (list<SPItem *>::iterator it(selected.begin()); it != selected.end(); @@ -74,43 +74,43 @@ struct CheckProgress : TestConvergence { } } */ - return TestConvergence::operator()(new_stress,X,Y); - } + return TestConvergence::operator()(new_stress,X,Y); + } list<SPItem *>& selected; vector<Rectangle*>& rs; map<string,unsigned>& nodelookup; }; /** - * Scans the items list and places those items that are + * Scans the items list and places those items that are * not connectors in filtered */ void filterConnectors(GSList const *const items, list<SPItem *> &filtered) { - for(GSList *i=(GSList *)items; i!=NULL; i=i->next) { - SPItem *item=SP_ITEM(i->data); - if(!isConnector(item)) { - filtered.push_back(item); - } - } + for(GSList *i=(GSList *)items; i!=NULL; i=i->next) { + SPItem *item=SP_ITEM(i->data); + if(!isConnector(item)) { + filtered.push_back(item); + } + } } /** -* Takes a list of inkscape items, extracts the graph defined by +* Takes a list of inkscape items, extracts the graph defined by * connectors between them, and uses graph layout techniques to find * a nice layout */ void graphlayout(GSList const *const items) { - if(!items) { - return; - } + if(!items) { + return; + } - using Inkscape::Util::GSListConstIterator; - list<SPItem *> selected; - filterConnectors(items,selected); - if (selected.empty()) return; + using Inkscape::Util::GSListConstIterator; + list<SPItem *> selected; + filterConnectors(items,selected); + if (selected.empty()) return; - const unsigned n=selected.size(); - //Check 2 or more selected objects - if (n < 2) return; + const unsigned n=selected.size(); + //Check 2 or more selected objects + if (n < 2) return; // add the connector spacing to the size of node bounding boxes // so that connectors can always be routed between shapes @@ -118,19 +118,19 @@ void graphlayout(GSList const *const items) { double spacing = 0; if(desktop) spacing = desktop->namedview->connector_spacing+0.1; - map<string,unsigned> nodelookup; - vector<Rectangle*> rs; - vector<Edge> es; - for (list<SPItem *>::iterator i(selected.begin()); - i != selected.end(); - ++i) - { - SPItem *u=*i; - Geom::OptRect const item_box(sp_item_bbox_desktop(u)); + map<string,unsigned> nodelookup; + vector<Rectangle*> rs; + vector<Edge> es; + for (list<SPItem *>::iterator i(selected.begin()); + i != selected.end(); + ++i) + { + SPItem *u=*i; + Geom::OptRect const item_box(sp_item_bbox_desktop(u)); if(item_box) { Geom::Point ll(item_box->min()); Geom::Point ur(item_box->max()); - nodelookup[u->id]=rs.size(); + nodelookup[u->getId()]=rs.size(); rs.push_back(new Rectangle(ll[0]-spacing,ur[0]+spacing, ll[1]-spacing,ur[1]+spacing)); } else { @@ -140,29 +140,29 @@ void graphlayout(GSList const *const items) { // probably pretty safe to simply ignore //fprintf(stderr,"NULL item_box found in graphlayout, ignoring!\n"); } - } + } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - SimpleConstraints scx,scy; - double ideal_connector_length = prefs->getDouble("/tools/connector/length", 100.0); - double directed_edge_height_modifier = 1.0; - - bool directed = prefs->getBool("/tools/connector/directedlayout"); - bool avoid_overlaps = prefs->getBool("/tools/connector/avoidoverlaplayout"); + SimpleConstraints scx,scy; + double ideal_connector_length = prefs->getDouble("/tools/connector/length", 100.0); + double directed_edge_height_modifier = 1.0; + + bool directed = prefs->getBool("/tools/connector/directedlayout"); + bool avoid_overlaps = prefs->getBool("/tools/connector/avoidoverlaplayout"); - for (list<SPItem *>::iterator i(selected.begin()); - i != selected.end(); - ++i) - { - SPItem *iu=*i; - map<string,unsigned>::iterator i=nodelookup.find(iu->id); + for (list<SPItem *>::iterator i(selected.begin()); + i != selected.end(); + ++i) + { + SPItem *iu=*i; + map<string,unsigned>::iterator i=nodelookup.find(iu->getId()); if(i==nodelookup.end()) { continue; } unsigned u=i->second; GSList *nlist=iu->avoidRef->getAttachedConnectors(Avoid::runningFrom); list<SPItem *> connectors; - + connectors.insert<GSListConstIterator<SPItem *> >(connectors.end(),nlist,NULL); for (list<SPItem *>::iterator j(connectors.begin()); j != connectors.end(); @@ -177,35 +177,35 @@ void graphlayout(GSList const *const items) { } else { iv=items[0]; } - + if (iv == NULL) { - // The connector is not attached to anything at the + // The connector is not attached to anything at the // other end so we should just ignore it. continue; } // If iv not in nodelookup we again treat the connector // as disconnected and continue - map<string,unsigned>::iterator v_pair=nodelookup.find(iv->id); + map<string,unsigned>::iterator v_pair=nodelookup.find(iv->getId()); if(v_pair!=nodelookup.end()) { unsigned v=v_pair->second; //cout << "Edge: (" << u <<","<<v<<")"<<endl; es.push_back(make_pair(u,v)); if(conn->style->marker[SP_MARKER_LOC_END].set) { if(directed && strcmp(conn->style->marker[SP_MARKER_LOC_END].value,"none")) { - scy.push_back(new SimpleConstraint(v, u, + scy.push_back(new SimpleConstraint(v, u, (ideal_connector_length * directed_edge_height_modifier))); } } } } - if(nlist) { - g_slist_free(nlist); - } - } - const unsigned E = es.size(); - double eweights[E]; - fill(eweights,eweights+E,1); + if(nlist) { + g_slist_free(nlist); + } + } + const unsigned E = es.size(); + double eweights[E]; + fill(eweights,eweights+E,1); vector<Component*> cs; connectedComponents(rs,es,scx,scy,cs); for(unsigned i=0;i<cs.size();i++) { @@ -218,15 +218,15 @@ void graphlayout(GSList const *const items) { alg.run(); } separateComponents(cs); - - for (list<SPItem *>::iterator it(selected.begin()); - it != selected.end(); - ++it) - { - SPItem *u=*it; - if(!isConnector(u)) { - map<string,unsigned>::iterator i=nodelookup.find(u->id); - if(i!=nodelookup.end()) { + + for (list<SPItem *>::iterator it(selected.begin()); + it != selected.end(); + ++it) + { + SPItem *u=*it; + if(!isConnector(u)) { + map<string,unsigned>::iterator i=nodelookup.find(u->getId()); + if(i!=nodelookup.end()) { Rectangle* r=rs[i->second]; Geom::OptRect item_box(sp_item_bbox_desktop(u)); if(item_box) { @@ -235,8 +235,8 @@ void graphlayout(GSList const *const items) { sp_item_move_rel(u, Geom::Translate(dest - curr)); } } - } - } + } + } for(unsigned i=0;i<scx.size();i++) { delete scx[i]; } @@ -247,7 +247,7 @@ void graphlayout(GSList const *const items) { delete rs[i]; } } -// vim: set cindent +// vim: set cindent // vim: ts=4 sw=4 et tw=0 wm=0 /* diff --git a/src/graphlayout/graphlayout.h b/src/graphlayout.h index 40090ef6b..40090ef6b 100644 --- a/src/graphlayout/graphlayout.h +++ b/src/graphlayout.h diff --git a/src/graphlayout/CMakeLists.txt b/src/graphlayout/CMakeLists.txt deleted file mode 100644 index c8847a228..000000000 --- a/src/graphlayout/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -SET(graphlayout_SRC -graphlayout.cpp -) -ADD_LIBRARY(graphlayout STATIC ${graphlayout_SRC}) -TARGET_LINK_LIBRARIES(graphlayout -2geom ${INKSCAPE_LIBS})
\ No newline at end of file diff --git a/src/graphlayout/Makefile_insert b/src/graphlayout/Makefile_insert deleted file mode 100644 index 790c951ae..000000000 --- a/src/graphlayout/Makefile_insert +++ /dev/null @@ -1,5 +0,0 @@ -## Makefile.am fragment sourced by src/Makefile.am. - -ink_common_sources += \ - graphlayout/graphlayout.cpp \ - graphlayout/graphlayout.h diff --git a/src/graphlayout/makefile b/src/graphlayout/makefile deleted file mode 100644 index 5d8ac906e..000000000 --- a/src/graphlayout/makefile +++ /dev/null @@ -1,17 +0,0 @@ -# Convenience stub makefile to call the real Makefile. - - - -# Explicit so that it's the default rule. -all: - cd .. && $(MAKE) graphlayout/all - -clean %.a %.o: - cd .. && $(MAKE) graphlayout/$@ - -.PHONY: all clean - -OBJEXT = o - -.SUFFIXES: -.SUFFIXES: .a .$(OBJEXT) diff --git a/src/helper/pixbuf-ops.cpp b/src/helper/pixbuf-ops.cpp index 1e43df5f3..3be63aa68 100644 --- a/src/helper/pixbuf-ops.cpp +++ b/src/helper/pixbuf-ops.cpp @@ -26,6 +26,7 @@ #include <document.h> #include <sp-item.h> #include <sp-root.h> +#include <sp-use.h> #include <sp-defs.h> #include "unit-constants.h" @@ -47,6 +48,7 @@ hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey) && !SP_IS_DEFS(o) && !SP_IS_ROOT(o) && !SP_IS_GROUP(o) + && !SP_IS_USE(o) && !g_slist_find(list, o) ) { sp_item_invoke_hide(SP_ITEM(o), dkey); diff --git a/src/helper/stock-items.cpp b/src/helper/stock-items.cpp index 575197fee..1c184da72 100644 --- a/src/helper/stock-items.cpp +++ b/src/helper/stock-items.cpp @@ -23,7 +23,7 @@ #include <xml/repr.h> -#include "sp-gradient-fns.h" +#include "sp-gradient.h" #include "document-private.h" #include "sp-pattern.h" #include "marker.h" diff --git a/src/id-clash.cpp b/src/id-clash.cpp index b215576a4..43ce393dd 100644 --- a/src/id-clash.cpp +++ b/src/id-clash.cpp @@ -132,7 +132,7 @@ find_references(SPObject *elem, refmap_type *refmap) if (paint->isPaintserver() && paint->value.href) { const SPObject *obj = paint->value.href->getObject(); if (obj) { - const gchar *id = SP_OBJECT_ID(obj); + const gchar *id = obj->getId(); IdReference idref = { REF_STYLE, elem, SPIPaint_properties[i] }; (*refmap)[id].push_back(idref); } @@ -144,7 +144,7 @@ find_references(SPObject *elem, refmap_type *refmap) if (filter->href) { const SPObject *obj = filter->href->getObject(); if (obj) { - const gchar *id = SP_OBJECT_ID(obj); + const gchar *id = obj->getId(); IdReference idref = { REF_STYLE, elem, "filter" }; (*refmap)[id].push_back(idref); } @@ -181,7 +181,7 @@ change_clashing_ids(SPDocument *imported_doc, SPDocument *current_doc, SPObject *elem, const refmap_type *refmap, id_changelist_type *id_changes) { - const gchar *id = SP_OBJECT_ID(elem); + const gchar *id = elem->getId(); if (id && current_doc->getObjectById(id)) { // Choose a new ID. @@ -226,7 +226,7 @@ fix_up_refs(const refmap_type *refmap, const id_changelist_type &id_changes) const std::list<IdReference>::const_iterator it_end = pos->second.end(); for (it = pos->second.begin(); it != it_end; ++it) { if (it->type == REF_HREF) { - gchar *new_uri = g_strdup_printf("#%s", SP_OBJECT_ID(obj)); + gchar *new_uri = g_strdup_printf("#%s", obj->getId()); SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, new_uri); g_free(new_uri); } @@ -234,13 +234,13 @@ fix_up_refs(const refmap_type *refmap, const id_changelist_type &id_changes) sp_style_set_property_url(it->elem, it->attr, obj, false); } else if (it->type == REF_URL) { - gchar *url = g_strdup_printf("url(#%s)", SP_OBJECT_ID(obj)); + gchar *url = g_strdup_printf("url(#%s)", obj->getId()); SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, url); g_free(url); } else if (it->type == REF_CLIPBOARD) { SPCSSAttr *style = sp_repr_css_attr(SP_OBJECT_REPR(it->elem), "style"); - gchar *url = g_strdup_printf("url(#%s)", SP_OBJECT_ID(obj)); + gchar *url = g_strdup_printf("url(#%s)", obj->getId()); sp_repr_css_set_property(style, it->attr, url); g_free(url); gchar *style_string = sp_repr_css_write_string(style); diff --git a/src/inkjar/CMakeLists.txt b/src/inkjar/CMakeLists.txt deleted file mode 100644 index 7a1e86525..000000000 --- a/src/inkjar/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -SET(inkjar_SRC -jar.cpp -) -ADD_LIBRARY(inkjar STATIC ${inkjar_SRC}) -TARGET_LINK_LIBRARIES(inkjar -2geom ${INKSCAPE_LIBS})
\ No newline at end of file diff --git a/src/inkjar/Makefile_insert b/src/inkjar/Makefile_insert deleted file mode 100644 index ef4f1fd19..000000000 --- a/src/inkjar/Makefile_insert +++ /dev/null @@ -1,5 +0,0 @@ -## Makefile.am fragment sourced by src/Makefile.am. - -ink_common_sources += \ - inkjar/jar.cpp \ - inkjar/jar.h diff --git a/src/inkjar/makefile.in b/src/inkjar/makefile.in deleted file mode 100644 index 96834aa2f..000000000 --- a/src/inkjar/makefile.in +++ /dev/null @@ -1,17 +0,0 @@ -# Convenience stub makefile to call the real Makefile. - -@SET_MAKE@ - -OBJEXT = @OBJEXT@ - -# Explicit so that it's the default rule. -all: - cd .. && $(MAKE) inkjar/all - -clean %.a %.$(OBJEXT): - cd .. && $(MAKE) inkjar/$@ - -.PHONY: all clean - -.SUFFIXES: -.SUFFIXES: .a .$(OBJEXT) diff --git a/src/inkscape.rc b/src/inkscape.rc index 3b946a067..9163658f0 100644 --- a/src/inkscape.rc +++ b/src/inkscape.rc @@ -3,8 +3,8 @@ APPLICATION_ICON ICON DISCARDABLE "../inkscape.ico" 1 24 DISCARDABLE "./inkscape-manifest.xml" 1 VERSIONINFO - FILEVERSION 0,47,0,0 - PRODUCTVERSION 0,47,0,0 + FILEVERSION 0,47,0,9 + PRODUCTVERSION 0,47,0,9 BEGIN BLOCK "StringFileInfo" BEGIN @@ -13,11 +13,11 @@ BEGIN VALUE "Comments", "Published under the GNU GPL" VALUE "CompanyName", "inkscape.org" VALUE "FileDescription", "Inkscape" - VALUE "FileVersion", "0.47" + VALUE "FileVersion", "0.47+devel" VALUE "InternalName", "Inkscape" VALUE "LegalCopyright", "© 2009 Inkscape" VALUE "ProductName", "Inkscape" - VALUE "ProductVersion", "0.47" + VALUE "ProductVersion", "0.47+devel" END END BLOCK "VarFileInfo" diff --git a/src/inkview.cpp b/src/inkview.cpp index 5cfde2c81..c151991dd 100644 --- a/src/inkview.cpp +++ b/src/inkview.cpp @@ -63,7 +63,7 @@ #include "svg-view-widget.h" #ifdef WITH_INKJAR -#include "inkjar/jar.h" +#include "io/inkjar.h" #endif #include "inkscape-private.h" @@ -446,9 +446,6 @@ sp_svgview_set_document(struct SPSlideShow *ss, SPDocument *doc, int current) if (doc && doc != ss->doc) { sp_document_ensure_up_to_date (doc); reinterpret_cast<SPSVGView*>(SP_VIEW_WIDGET_VIEW (ss->view))->setDocument (doc); - if (ss->doc) { - delete ss->doc; - } ss->doc = doc; ss->current = current; } diff --git a/src/inkview.rc b/src/inkview.rc index 7a02bf11a..390f4fb07 100644 --- a/src/inkview.rc +++ b/src/inkview.rc @@ -3,8 +3,8 @@ APPLICATION_ICON ICON DISCARDABLE "../inkscape.ico" 1 24 DISCARDABLE "./inkview-manifest.xml"
1 VERSIONINFO
- FILEVERSION 0,47,0,0
- PRODUCTVERSION 0,47,0,0
+ FILEVERSION 0,47,0,9
+ PRODUCTVERSION 0,47,0,9
BEGIN
BLOCK "StringFileInfo"
BEGIN
@@ -13,11 +13,11 @@ BEGIN VALUE "Comments", "Published under the GNU GPL"
VALUE "CompanyName", "inkscape.org"
VALUE "FileDescription", "Inkview"
- VALUE "FileVersion", "0.47"
+ VALUE "FileVersion", "0.47+devel"
VALUE "InternalName", "Inkview"
VALUE "LegalCopyright", "© 2009 Inkscape"
VALUE "ProductName", "Inkview"
- VALUE "ProductVersion", "0.47"
+ VALUE "ProductVersion", "0.47+devel"
END
END
BLOCK "VarFileInfo"
diff --git a/src/interface.cpp b/src/interface.cpp index b29b91d18..1a6da5635 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -23,7 +23,9 @@ #include <glib.h> #include "inkscape-private.h" +#include "extension/db.h" #include "extension/effect.h" +#include "extension/input.h" #include "widgets/icon.h" #include "preferences.h" #include "path-prefix.h" @@ -1098,7 +1100,7 @@ sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item) if ( group && group != dt->currentLayer() ) { /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */ - gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group)); + gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId()); GtkWidget *w = gtk_menu_item_new_with_label(label); g_free(label); g_object_set_data(G_OBJECT(w), "group", group); @@ -1316,7 +1318,7 @@ sp_ui_drag_data_received(GtkWidget *widget, const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); - if ( color.descr == grad->id ) { + if ( color.descr == grad->getId() ) { if ( grad->has_stops ) { matches = grad; break; @@ -1325,7 +1327,7 @@ sp_ui_drag_data_received(GtkWidget *widget, } if (matches) { colorspec = "url(#"; - colorspec += matches->id; + colorspec += matches->getId(); colorspec += ")"; } else { gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b); @@ -1454,48 +1456,26 @@ sp_ui_drag_data_received(GtkWidget *widget, case PNG_DATA: case JPEG_DATA: case IMAGE_DATA: { - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); - Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image"); - gchar *atom_name = gdk_atom_name(data->type); - - // this formula taken from Glib docs - guint needed_size = data->length * 4 / 3 + data->length * 4 / (3 * 72) + 7; - needed_size += 5 + 8 + strlen(atom_name); // 5 bytes for data:, 8 for ;base64, - - gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer; - buf_work += g_sprintf(buffer, "data:%s;base64,", atom_name); - - gint state = 0, save = 0; - g_base64_encode_step(data->data, data->length, TRUE, buf_work, &state, &save); - g_base64_encode_close(TRUE, buf_work, &state, &save); - - newImage->setAttribute("xlink:href", buffer); - g_free(buffer); - - GError *error = NULL; - GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error ); - if ( loader ) { - error = NULL; - if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) { - GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader); - if ( pbuf ) { - char tmp[1024]; - int width = gdk_pixbuf_get_width(pbuf); - int height = gdk_pixbuf_get_height(pbuf); - snprintf( tmp, sizeof(tmp), "%d", width ); - newImage->setAttribute("width", tmp); - - snprintf( tmp, sizeof(tmp), "%d", height ); - newImage->setAttribute("height", tmp); - } - } - } - g_free(atom_name); + const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png"); - // Add it to the current layer - desktop->currentLayer()->appendChildRepr(newImage); - - Inkscape::GC::release(newImage); + Inkscape::Extension::DB::InputList o; + Inkscape::Extension::db.get_input_list(o); + Inkscape::Extension::DB::InputList::const_iterator i = o.begin(); + while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) { + ++i; + } + Inkscape::Extension::Extension *ext = *i; + bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0); + ext->set_param_optiongroup("link", "embed"); + ext->set_gui(false); + + gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL ); + g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL); + file_import(doc, filename, ext); + g_free(filename); + + ext->set_param_optiongroup("link", save ? "embed" : "link"); + ext->set_gui(true); sp_document_done( doc , SP_VERB_NONE, _("Drop bitmap image")); break; diff --git a/src/io/Makefile_insert b/src/io/Makefile_insert index 2696e9334..bb47b46b8 100644 --- a/src/io/Makefile_insert +++ b/src/io/Makefile_insert @@ -7,6 +7,8 @@ ink_common_sources += \ io/ftos.h \ io/gzipstream.cpp \ io/gzipstream.h \ + io/inkjar.cpp \ + io/inkjar.h \ io/inkscapestream.cpp \ io/inkscapestream.h \ io/resource.cpp \ diff --git a/src/inkjar/jar.cpp b/src/io/inkjar.cpp index 6897cb317..c238aba36 100644 --- a/src/inkjar/jar.cpp +++ b/src/io/inkjar.cpp @@ -48,7 +48,7 @@ #include <glib.h> #include <zlib.h> -#include "jar.h" +#include "inkjar.h" #include <fstream> #ifdef WORDS_BIGENDIAN diff --git a/src/inkjar/jar.h b/src/io/inkjar.h index 0fe088b24..0fe088b24 100644 --- a/src/inkjar/jar.h +++ b/src/io/inkjar.h diff --git a/src/io/sys.cpp b/src/io/sys.cpp index 2841f0af8..34160b648 100644 --- a/src/io/sys.cpp +++ b/src/io/sys.cpp @@ -36,9 +36,11 @@ #include <process.h> // declares spawn functions #include <wchar.h> // declares _wspawn functions -#ifdef __cplusplus + +#ifndef __MINGW32__ +# ifdef __cplusplus extern "C" { -#endif +# endif _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnl (int, const wchar_t*, const wchar_t*, ...); _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnle (int, const wchar_t*, const wchar_t*, ...); _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnlp (int, const wchar_t*, const wchar_t*, ...); @@ -47,8 +49,9 @@ _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnv (int, const wchar_t*, const wchar_t _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnve (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*); _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvp (int, const wchar_t*, const wchar_t* const*); _CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvpe (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*); -#ifdef __cplusplus +# ifdef __cplusplus } +# endif #endif #include <unistd.h> #include <glibmm/i18n.h> @@ -409,10 +412,9 @@ dup_noninherited (int fd, GetCurrentProcess (), &filehandle, 0, FALSE, DUPLICATE_SAME_ACCESS); close (fd); - return _open_osfhandle ((long) filehandle, mode | _O_NOINHERIT); + return _open_osfhandle(reinterpret_cast<LONG_PTR>(filehandle), mode | _O_NOINHERIT); } - /* The helper process writes a status report back to us, through a * pipe, consisting of two ints. */ diff --git a/src/knot-holder-entity.cpp b/src/knot-holder-entity.cpp index bf7505f3c..2d0d5eb02 100644 --- a/src/knot-holder-entity.cpp +++ b/src/knot-holder-entity.cpp @@ -95,7 +95,7 @@ KnotHolderEntity::snap_knot_position(Geom::Point const &p) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, item); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, s, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE); return s * i2d.inverse(); } @@ -119,12 +119,12 @@ KnotHolderEntity::snap_knot_position_constrained(Geom::Point const &p, Inkscape: // freeSnap() will try snapping point p. This will not take into account the constraint, which // is therefore to be enforced after snap_knot_position_constrained() has finished - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, s, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE); } else { // constrainedSnap() will first project the point p onto the constraint line and then try to snap along that line. // This way the constraint is already enforced, no need to worry about that later on Inkscape::Snapper::ConstraintLine transformed_constraint = Inkscape::Snapper::ConstraintLine(constraint.getPoint() * i2d, (constraint.getPoint() + constraint.getDirection()) * i2d - constraint.getPoint() * i2d); - m.constrainedSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, s, Inkscape::SNAPSOURCE_HANDLE, transformed_constraint); + m.constrainedSnapReturnByRef(s, Inkscape::SNAPSOURCE_NODE_HANDLE, transformed_constraint); } return s * i2d.inverse(); diff --git a/src/knot.cpp b/src/knot.cpp index b17e41b24..04520ed22 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -284,7 +284,7 @@ void sp_knot_start_dragging(SPKnot *knot, Geom::Point const &p, gint x, gint y, */ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot) { - g_assert(knot != NULL); + g_assert(knot != NULL); g_assert(SP_IS_KNOT(knot)); /* Run client universal event handler, if present */ @@ -302,7 +302,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); switch (event->type) { - case GDK_2BUTTON_PRESS: + case GDK_2BUTTON_PRESS: if (event->button.button == 1) { g_signal_emit(knot, knot_signals[DOUBLECLICKED], 0, event->button.state); @@ -311,23 +311,23 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot consumed = TRUE; } break; - case GDK_BUTTON_PRESS: + case GDK_BUTTON_PRESS: if (event->button.button == 1 && !knot->desktop->event_context->space_panning) { Geom::Point const p = knot->desktop->w2d(Geom::Point(event->button.x, event->button.y)); sp_knot_start_dragging(knot, p, (gint) event->button.x, (gint) event->button.y, event->button.time); - consumed = TRUE; + consumed = TRUE; } break; - case GDK_BUTTON_RELEASE: - if (event->button.button == 1 && !knot->desktop->event_context->space_panning) { - // If we have any pending snap event, then invoke it now - if (knot->desktop->event_context->_delayed_snap_event) { - sp_event_context_snap_watchdog_callback(knot->desktop->event_context->_delayed_snap_event); - } + case GDK_BUTTON_RELEASE: + if (event->button.button == 1 && !knot->desktop->event_context->space_panning) { + // If we have any pending snap event, then invoke it now + if (knot->desktop->event_context->_delayed_snap_event) { + sp_event_context_snap_watchdog_callback(knot->desktop->event_context->_delayed_snap_event); + } - sp_event_context_discard_delayed_snap_event(knot->desktop->event_context); + sp_event_context_discard_delayed_snap_event(knot->desktop->event_context); - knot->pressure = 0; + knot->pressure = 0; if (transform_escaped) { transform_escaped = false; consumed = TRUE; @@ -356,7 +356,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot } } break; - case GDK_MOTION_NOTIFY: + case GDK_MOTION_NOTIFY: if (grabbed && !knot->desktop->event_context->space_panning) { consumed = TRUE; @@ -384,12 +384,12 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot SP_KNOT_DRAGGING, TRUE); } - sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, knot, (GdkEventMotion *)event, DelayedSnapEvent::KNOT_HANDLER); + sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, (gpointer) knot, (GdkEventMotion *)event, DelayedSnapEvent::KNOT_HANDLER); sp_knot_handler_request_position(event, knot); moved = TRUE; } break; - case GDK_ENTER_NOTIFY: + case GDK_ENTER_NOTIFY: sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE); sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE); @@ -401,7 +401,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot moved = FALSE; consumed = TRUE; break; - case GDK_LEAVE_NOTIFY: + case GDK_LEAVE_NOTIFY: sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, FALSE); sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE); @@ -413,35 +413,35 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot moved = FALSE; consumed = TRUE; break; - case GDK_KEY_PRESS: // keybindings for knot + case GDK_KEY_PRESS: // keybindings for knot switch (get_group0_keyval(&event->key)) { - case GDK_Escape: - sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE); - if (!nograb) { - sp_canvas_item_ungrab(knot->item, event->button.time); - } - if (moved) { - sp_knot_set_flag(knot, - SP_KNOT_DRAGGING, - FALSE); - g_signal_emit(knot, - knot_signals[UNGRABBED], 0, - event->button.state); - sp_document_undo(sp_desktop_document(knot->desktop)); - knot->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Node or handle drag canceled.")); - transform_escaped = true; - consumed = TRUE; - } - grabbed = FALSE; - moved = FALSE; - sp_event_context_discard_delayed_snap_event(knot->desktop->event_context); - break; - default: - consumed = FALSE; - break; - } - break; - default: + case GDK_Escape: + sp_knot_set_flag(knot, SP_KNOT_GRABBED, FALSE); + if (!nograb) { + sp_canvas_item_ungrab(knot->item, event->button.time); + } + if (moved) { + sp_knot_set_flag(knot, + SP_KNOT_DRAGGING, + FALSE); + g_signal_emit(knot, + knot_signals[UNGRABBED], 0, + event->button.state); + sp_document_undo(sp_desktop_document(knot->desktop)); + knot->desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Node or handle drag canceled.")); + transform_escaped = true; + consumed = TRUE; + } + grabbed = FALSE; + moved = FALSE; + sp_event_context_discard_delayed_snap_event(knot->desktop->event_context); + break; + default: + consumed = FALSE; + break; + } + break; + default: break; } @@ -452,14 +452,14 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot void sp_knot_handler_request_position(GdkEvent *event, SPKnot *knot) { - Geom::Point const motion_w(event->motion.x, event->motion.y); - Geom::Point const motion_dt = knot->desktop->w2d(motion_w); - Geom::Point p = motion_dt - knot->grabbed_rel_pos; - sp_knot_request_position (knot, p, event->motion.state); - knot->desktop->scroll_to_point (motion_dt); - knot->desktop->set_coordinate_status(knot->pos); // display the coordinate of knot, not cursor - they may be different! - if (event->motion.state & GDK_BUTTON1_MASK) - gobble_motion_events(GDK_BUTTON1_MASK); + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point const motion_dt = knot->desktop->w2d(motion_w); + Geom::Point p = motion_dt - knot->grabbed_rel_pos; + sp_knot_request_position (knot, p, event->motion.state); + knot->desktop->scroll_to_point (motion_dt); + knot->desktop->set_coordinate_status(knot->pos); // display the coordinate of knot, not cursor - they may be different! + if (event->motion.state & GDK_BUTTON1_MASK) + gobble_motion_events(GDK_BUTTON1_MASK); } /** @@ -620,20 +620,20 @@ void sp_knot_set_flag(SPKnot *knot, guint flag, bool set) } switch (flag) { - case SP_KNOT_VISIBLE: + case SP_KNOT_VISIBLE: if (set) { sp_canvas_item_show(knot->item); } else { sp_canvas_item_hide(knot->item); } break; - case SP_KNOT_MOUSEOVER: - case SP_KNOT_DRAGGING: + case SP_KNOT_MOUSEOVER: + case SP_KNOT_DRAGGING: sp_knot_set_ctrl_state(knot); break; - case SP_KNOT_GRABBED: + case SP_KNOT_GRABBED: break; - default: + default: g_assert_not_reached(); break; } diff --git a/src/knotholder.cpp b/src/knotholder.cpp index 45cb140c0..314ad807c 100644 --- a/src/knotholder.cpp +++ b/src/knotholder.cpp @@ -91,6 +91,21 @@ KnotHolder::update_knots() } } +/** + * \brief Returns true if at least one of the KnotHolderEntities has the mouse hovering above it + */ +bool KnotHolder::knot_mouseover() +{ + for(std::list<KnotHolderEntity *>::iterator i = entity.begin(); i != entity.end(); ++i) { + SPKnot *knot = (*i)->knot; + if (knot && (knot->flags & SP_KNOT_MOUSEOVER)) { + return true; + } + } + + return false; +} + void KnotHolder::knot_clicked_handler(SPKnot *knot, guint state) { diff --git a/src/knotholder.h b/src/knotholder.h index fa1abd071..0b37d211c 100644 --- a/src/knotholder.h +++ b/src/knotholder.h @@ -49,6 +49,8 @@ public: const SPItem *getItem() { return item; } + bool knot_mouseover(); + friend class ShapeEditor; protected: diff --git a/src/layer-fns.cpp b/src/layer-fns.cpp index 75bb89bcf..ca7c1c493 100644 --- a/src/layer-fns.cpp +++ b/src/layer-fns.cpp @@ -16,7 +16,7 @@ #include "document.h" #include "sp-item-group.h" #include "xml/repr.h" -#include "algorithms/find-last-if.h" +#include "util/find-last-if.h" #include "layer-fns.h" namespace Inkscape { diff --git a/src/libavoid/Makefile_insert b/src/libavoid/Makefile_insert index 77728499c..3a9b97cef 100644 --- a/src/libavoid/Makefile_insert +++ b/src/libavoid/Makefile_insert @@ -6,6 +6,7 @@ libavoid/clean: rm -f libavoid/libavoid.a $(libavoid_libavoid_a_OBJECTS) libavoid_libavoid_a_SOURCES = \ + libavoid/assertions.h \ libavoid/connector.cpp \ libavoid/connector.h \ libavoid/debug.h \ diff --git a/src/libnr/nr-point-fns.cpp b/src/libnr/nr-point-fns.cpp index 5082c3a10..0142655f2 100644 --- a/src/libnr/nr-point-fns.cpp +++ b/src/libnr/nr-point-fns.cpp @@ -61,19 +61,6 @@ NR::Point abs(NR::Point const &b) return ret; } -NR::Point * -get_snap_vector (NR::Point p, NR::Point o, double snap, double initial) -{ - double r = NR::L2 (p - o); - if (r < 1e-3) - return NULL; - double angle = NR::atan2 (p - o); - // snap angle to snaps increments, starting from initial: - double a_snapped = initial + floor((angle - initial)/snap + 0.5) * snap; - // calculate the new position and subtract p to get the vector: - return new NR::Point (o + r * NR::Point(cos(a_snapped), sin(a_snapped)) - p); -} - NR::Point snap_vector_midpoint (NR::Point p, NR::Point begin, NR::Point end, double snap) { diff --git a/src/libnr/nr-point-fns.h b/src/libnr/nr-point-fns.h index e927725b4..9ef7205c6 100644 --- a/src/libnr/nr-point-fns.h +++ b/src/libnr/nr-point-fns.h @@ -90,8 +90,6 @@ Point abs(Point const &b); } /* namespace NR */ -NR::Point *get_snap_vector (NR::Point p, NR::Point o, double snap, double initial); - NR::Point snap_vector_midpoint (NR::Point p, NR::Point begin, NR::Point end, double snap); double get_offset_between_points (NR::Point p, NR::Point begin, NR::Point end); diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index 1f85ee5ca..06fb93f2d 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -10,26 +10,19 @@ #define PANGO_ENABLE_ENGINE -#include "FontFactory.h" -#include <libnrtype/font-instance.h> - -#include <glibmm.h> - - #ifdef HAVE_CONFIG_H # include "config.h" #endif +#include <glibmm.h> #include <glib/gmem.h> #include <glibmm/i18n.h> // _() +#include <pango/pangoft2.h> +#include "libnrtype/FontFactory.h" +#include "libnrtype/font-instance.h" +#include "util/unordered-containers.h" -/* Freetype2 */ -# include <pango/pangoft2.h> - -#include <ext/hash_map> - - -typedef __gnu_cxx::hash_map<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; +typedef INK_UNORDERED_MAP<PangoFontDescription*, font_instance*, font_descr_hash, font_descr_equal> FaceMapType; // need to avoid using the size field size_t font_descr_hash::operator()( PangoFontDescription *const &x) const { @@ -47,7 +40,7 @@ size_t font_descr_hash::operator()( PangoFontDescription *const &x) const { h += (int)pango_font_description_get_stretch(x); return h; } -bool font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) { +bool font_descr_equal::operator()( PangoFontDescription *const&a, PangoFontDescription *const &b) const { //if ( pango_font_description_equal(a,b) ) return true; char const *fa = pango_font_description_get_family(a); char const *fb = pango_font_description_get_family(b); @@ -332,6 +325,7 @@ font_factory::~font_factory(void) { if (loadedPtr) { FaceMapType* tmp = static_cast<FaceMapType*>(loadedPtr); + delete tmp; loadedPtr = 0; } @@ -824,9 +818,11 @@ font_instance *font_factory::Face(PangoFontDescription *descr, bool canFail) if ( nFace ) { // duplicate FcPattern, the hard way res = new font_instance(); - // store the descr of the font we asked for, since this is the key where we intend to put the font_instance at - // in the hash_map. the descr of the returned pangofont may differ from what was asked, so we don't know (at this - // point) whether loadedFaces[that_descr] is free or not (and overwriting an entry will bring deallocation problems) + // store the descr of the font we asked for, since this is the key where we intend + // to put the font_instance at in the unordered_map. the descr of the returned + // pangofont may differ from what was asked, so we don't know (at this + // point) whether loadedFaces[that_descr] is free or not (and overwriting + // an entry will bring deallocation problems) res->descr = pango_font_description_copy(descr); res->daddy = this; res->InstallFace(nFace); diff --git a/src/libnrtype/FontFactory.h b/src/libnrtype/FontFactory.h index 8d85bcf3e..0118c862d 100644 --- a/src/libnrtype/FontFactory.h +++ b/src/libnrtype/FontFactory.h @@ -45,7 +45,7 @@ struct font_descr_hash : public std::unary_function<PangoFontDescription*,size_t size_t operator()(PangoFontDescription *const &x) const; }; struct font_descr_equal : public std::binary_function<PangoFontDescription*, PangoFontDescription*, bool> { - bool operator()(PangoFontDescription *const &a, PangoFontDescription *const &b); + bool operator()(PangoFontDescription *const &a, PangoFontDescription *const &b) const; }; // Comparison functions for style names diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index f34a230c1..be5eb86c8 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -11,39 +11,33 @@ #define PANGO_ENABLE_ENGINE #ifdef HAVE_CONFIG_H -# include <config.h> +# include "config.h" #endif -#include <libnr/nr-rect.h> -#include <libnrtype/font-glyph.h> -#include <libnrtype/font-instance.h> -#include <2geom/pathvector.h> -#include <livarot/Path.h> - -#include "RasterFont.h" - -/* Freetype 2 */ -# include <ft2build.h> -# include FT_OUTLINE_H -# include FT_BBOX_H -# include FT_TRUETYPE_TAGS_H -# include FT_TRUETYPE_TABLES_H -# include <pango/pangoft2.h> -#include <ext/hash_map> +#include <ft2build.h> +#include FT_OUTLINE_H +#include FT_BBOX_H +#include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_TABLES_H +#include <pango/pangoft2.h> +#include <2geom/pathvector.h> +#include "libnr/nr-rect.h" +#include "libnrtype/font-glyph.h" +#include "libnrtype/font-instance.h" +#include "libnrtype/RasterFont.h" +#include "livarot/Path.h" +#include "util/unordered-containers.h" -// the various raster_font in use at a given time are held in a hash_map whose indices are the -// styles, hence the 2 following 'classes' struct font_style_hash : public std::unary_function<font_style, size_t> { size_t operator()(font_style const &x) const; }; struct font_style_equal : public std::binary_function<font_style, font_style, bool> { - bool operator()(font_style const &a, font_style const &b); + bool operator()(font_style const &a, font_style const &b) const; }; - -typedef __gnu_cxx::hash_map<font_style, raster_font*, font_style_hash, font_style_equal> StyleMap; +typedef INK_UNORDERED_MAP<font_style, raster_font*, font_style_hash, font_style_equal> StyleMap; @@ -76,7 +70,7 @@ size_t font_style_hash::operator()(const font_style &x) const { return h; } -bool font_style_equal::operator()(const font_style &a,const font_style &b) { +bool font_style_equal::operator()(const font_style &a,const font_style &b) const { for (int i=0;i<6;i++) { if ( (int)(100*a.transform[i]) != (int)(100*b.transform[i]) ) return false; } diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 7a2924d98..f6b9688bb 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -1467,8 +1467,12 @@ bool Layout::Calculator::calculate() } para.free(); - if (_scanline_maker) + if (_scanline_maker) { delete _scanline_maker; + _flow._input_truncated = false; + } else { + _flow._input_truncated = true; + } return true; } diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index 33371ab10..fb2769edc 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -19,8 +19,6 @@ #include "sp-string.h" #include "FontFactory.h" -#include "text-editing.h" // for inputTruncated() - namespace Inkscape { namespace Text { @@ -324,71 +322,5 @@ Layout::InputStreamTextSource::~InputStreamTextSource() sp_style_unref(style); } -bool -Layout::inputTruncated() const -{ - if (!inputExists()) - return false; - - // Find out the SPObject to which the last visible character corresponds: - Layout::iterator last = end(); - if (last == begin()) { - // FIXME: this returns a wrong "not truncated" when a flowtext is entirely - // truncated, so there are no visible characters. But how can I find out the - // originator SPObject without having anything to do getSourceOfCharacter - // from? - return false; - } - last.prevCharacter(); - void *source; - Glib::ustring::iterator offset; - getSourceOfCharacter(last, &source, &offset); - SPObject *obj = SP_OBJECT(source); - - // if that is SPString, see if it has further characters beyond the last visible - if (obj && SP_IS_STRING(obj)) { - Glib::ustring::iterator offset_next = offset; - offset_next ++; - if (offset_next != SP_STRING(obj)->string.end()) { - // truncated: source SPString has next char - return true; - } - } - - // otherwise, see if the SPObject at end() or any of its text-tree ancestors - // (excluding top-level SPText or SPFlowText) have a text-tree next sibling with - // visible text - if (obj) { - for (SPObject *ascend = obj; - ascend && (is_part_of_text_subtree (ascend) && !is_top_level_text_object(ascend)); - ascend = SP_OBJECT_PARENT(ascend)) { - if (SP_OBJECT_NEXT(ascend)) { - SPObject *next = SP_OBJECT_NEXT(ascend); - if (next && is_part_of_text_subtree(next) && has_visible_text(next)) { - // truncated: source text object has next text sibling - return true; - } - } - } - } - - // the above works for flowed text, but not for text on path. - // so now, we also check if the last of the _characters, if coming from a TEXT_SOURCE, - // has in_glyph different from -1 - unsigned last_char = _characters.size() - 1; - unsigned span_index = _characters[last_char].in_span; - Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character; - - if (_input_stream[_spans[span_index].in_input_stream_item]->Type() == TEXT_SOURCE) { - if (_characters[last_char].in_glyph == -1) { - //truncated: last char has no glyph - return true; - } - } - - // not truncated - return false; -} - }//namespace Text }//namespace Inkscape diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index 2b4b80e7c..d6b68ab40 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -513,8 +513,10 @@ void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path) _glyphs[glyph_index].y = midpoint[1] - _lines.front().baseline_y + tangent[1] * tangent_shift + tangent[0] * normal_shift; _glyphs[glyph_index].rotation += rotation; } + _input_truncated = false; } else { // outside the bounds of the path: hide the glyphs _characters[char_index].in_glyph = -1; + _input_truncated = true; } g_free(midpoint_otp); diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 05b5103fc..0a2463a56 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -212,7 +212,9 @@ public: bool inputExists() const {return !_input_stream.empty();} - bool inputTruncated() const; + bool _input_truncated; + bool inputTruncated() const + {return _input_truncated;} /** adds a new piece of text to the end of the current list of text to be processed. This method can only add text of a consistent style. diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index 521c9a424..d52bd723f 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -25,7 +25,7 @@ public: // depending on the rendering backend, different temporary data // that's the font's fingerprint; this particular PangoFontDescription gives the entry at which this font_instance - // resides in the font_factory loadedFaces hash_map + // resides in the font_factory loadedFaces unordered_map PangoFontDescription* descr; // refcount int refCount; @@ -86,7 +86,7 @@ private: void FreeTheFace(); // hashmap to get the raster_font for a given style - void* loadedPtr; // Pointer to a hash_map. Moved into .cpp to not expose use of __gnu_cxx extension. + void* loadedPtr; #ifdef USE_PANGO_WIN32 HFONT theFace; diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index 31fa07515..f2f025332 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -22,22 +22,17 @@ Inkscape::LineSnapper::LineSnapper(SnapManager *sm, Geom::Coord const d) : Snapp } void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &/*bbox_to_snap*/, std::vector<SPItem const *> const */*it*/, - std::vector<std::pair<Geom::Point, int> > */*unselected_nodes*/) const + std::vector<Inkscape::SnapCandidatePoint> */*unselected_nodes*/) const { - if (!(_snap_enabled && _snapmanager->snapprefs.getSnapFrom(t)) ) { + if (!(_snap_enabled && _snapmanager->snapprefs.getSnapFrom(p.getSourceType())) ) { return; } /* Get the lines that we will try to snap to */ - const LineList lines = _getSnapLines(p); - - // std::cout << "snap point " << p << " to: " << std::endl; + const LineList lines = _getSnapLines(p.getPoint()); for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) { Geom::Point const p1 = i->second; // point at guide/grid line @@ -45,17 +40,17 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, // std::cout << " line through " << i->second << " with normal " << i->first; g_assert(i->first != Geom::Point(0,0)); // we cannot project on an linesegment of zero length - Geom::Point const p_proj = Geom::projection(p, Geom::Line(p1, p2)); - Geom::Coord const dist = Geom::L2(p_proj - p); + Geom::Point const p_proj = Geom::projection(p.getPoint(), Geom::Line(p1, p2)); + Geom::Coord const dist = Geom::L2(p_proj - p.getPoint()); //Store any line that's within snapping range if (dist < getSnapperTolerance()) { - _addSnappedLine(sc, p_proj, dist, source_type, source_num, i->first, i->second); + _addSnappedLine(sc, p_proj, dist, p.getSourceType(), p.getSourceNum(), i->first, i->second); // For any line that's within range, we will also look at it's "point on line" p1. For guides // this point coincides with its origin; for grids this is of no use, but we cannot // discern between grids and guides here - Geom::Coord const dist_p1 = Geom::L2(p1 - p); + Geom::Coord const dist_p1 = Geom::L2(p1 - p.getPoint()); if (dist_p1 < getSnapperTolerance()) { - _addSnappedLinesOrigin(sc, p1, dist_p1, source_type, source_num); + _addSnappedLinesOrigin(sc, p1, dist_p1, p.getSourceType(), p.getSourceNum()); // Only relevant for guides; grids don't have an origin per line // Therefore _addSnappedLinesOrigin() will only be implemented for guides } @@ -66,26 +61,23 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, } void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &/*bbox_to_snap*/, ConstraintLine const &c, std::vector<SPItem const *> const */*it*/) const { - if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false) { + if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) { return; } /* Get the lines that we will try to snap to */ - const LineList lines = _getSnapLines(p); + const LineList lines = _getSnapLines(p.getPoint()); for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) { if (Geom::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint // constraint line - Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p; + Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p.getPoint(); Geom::Line line1(point_on_line, point_on_line + c.getDirection()); // grid/guide line @@ -106,19 +98,19 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, if (inters) { Geom::Point t = line1.pointAt((*inters).ta); - const Geom::Coord dist = Geom::L2(t - p); + const Geom::Coord dist = Geom::L2(t - p.getPoint()); if (dist < getSnapperTolerance()) { // When doing a constrained snap, we're already at an intersection. // This snappoint is therefore fully constrained, so there's no need // to look for additional intersections; just return the snapped point // and forget about the line - _addSnappedPoint(sc, t, dist, source_type, source_num); + _addSnappedPoint(sc, t, dist, p.getSourceType(), p.getSourceNum()); // For any line that's within range, we will also look at it's "point on line" p1. For guides // this point coincides with its origin; for grids this is of no use, but we cannot // discern between grids and guides here - Geom::Coord const dist_p1 = Geom::L2(p1 - p); + Geom::Coord const dist_p1 = Geom::L2(p1 - p.getPoint()); if (dist_p1 < getSnapperTolerance()) { - _addSnappedLinesOrigin(sc, p1, dist_p1, source_type, source_num); + _addSnappedLinesOrigin(sc, p1, dist_p1, p.getSourceType(), p.getSourceNum()); // Only relevant for guides; grids don't have an origin per line // Therefore _addSnappedLinesOrigin() will only be implemented for guides } diff --git a/src/line-snapper.h b/src/line-snapper.h index af36b8330..429139421 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -18,6 +18,7 @@ namespace Inkscape { +class SnapCandidatePoint; class LineSnapper : public Snapper { @@ -25,19 +26,13 @@ public: LineSnapper(SnapManager *sm, Geom::Coord const d); void freeSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, std::vector<SPItem const *> const *it, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes) const; void constrainedSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, ConstraintLine const &c, std::vector<SPItem const *> const *it) const; diff --git a/src/livarot/Shape.h b/src/livarot/Shape.h index ca8ae1c6e..44dd43a4a 100644 --- a/src/livarot/Shape.h +++ b/src/livarot/Shape.h @@ -41,9 +41,9 @@ enum { // possible values for the "type" field in the Shape class: enum { - shape_graph = 0, // it's just a graph; a bunch of edges, maybe intersections - shape_polygon = 1, // a polygon: intersection-free, edges oriented so that the inside is on their left - shape_polypatch = 2 // a graph without intersection; each face is a polygon (not yet used) + shape_graph = 0, // it's just a graph; a bunch of edges, maybe intersections + shape_polygon = 1, // a polygon: intersection-free, edges oriented so that the inside is on their left + shape_polypatch = 2 // a graph without intersection; each face is a polygon (not yet used) }; class IntLigne; @@ -56,59 +56,59 @@ public: struct back_data { - int pathID, pieceID; - double tSt, tEn; + int pathID, pieceID; + double tSt, tEn; }; struct voronoi_point - { // info for points treated as points of a voronoi diagram (obtained by MakeShape()) - double value; // distance to source - int winding; // winding relatively to source + { // info for points treated as points of a voronoi diagram (obtained by MakeShape()) + double value; // distance to source + int winding; // winding relatively to source }; struct voronoi_edge - { // info for edges, treated as approximation of edges of the voronoi diagram - int leF, riF; // left and right site - double leStX, leStY, riStX, riStY; // on the left side: (leStX,leStY) is the smallest vector from the source to st - // etc... - double leEnX, leEnY, riEnX, riEnY; + { // info for edges, treated as approximation of edges of the voronoi diagram + int leF, riF; // left and right site + double leStX, leStY, riStX, riStY; // on the left side: (leStX,leStY) is the smallest vector from the source to st + // etc... + double leEnX, leEnY, riEnX, riEnY; }; struct quick_raster_data { - double x; // x-position on the sweepline - int bord; // index of the edge - int ind; // index of qrsData elem for edge (ie inverse of the bord) - int next,prev; // dbl linkage + double x; // x-position on the sweepline + int bord; // index of the edge + int ind; // index of qrsData elem for edge (ie inverse of the bord) + int next,prev; // dbl linkage }; enum sTreeChangeType { - EDGE_INSERTED = 0, - EDGE_REMOVED = 1, - INTERSECTION = 2 + EDGE_INSERTED = 0, + EDGE_REMOVED = 1, + INTERSECTION = 2 }; struct sTreeChange { - sTreeChangeType type; // type of modification to the sweepline: - int ptNo; // point at which the modification takes place - - Shape *src; // left edge (or unique edge if not an intersection) involved in the event - int bord; - Shape *osrc; // right edge (if intersection) - int obord; - Shape *lSrc; // edge directly on the left in the sweepline at the moment of the event - int lBrd; - Shape *rSrc; // edge directly on the right - int rBrd; + sTreeChangeType type; // type of modification to the sweepline: + int ptNo; // point at which the modification takes place + + Shape *src; // left edge (or unique edge if not an intersection) involved in the event + int bord; + Shape *osrc; // right edge (if intersection) + int obord; + Shape *lSrc; // edge directly on the left in the sweepline at the moment of the event + int lBrd; + Shape *rSrc; // edge directly on the right + int rBrd; }; struct incidenceData { - int nextInc; // next incidence in the linked list - int pt; // point incident to the edge (there is one list per edge) - double theta; // coordinate of the incidence on the edge + int nextInc; // next incidence in the linked list + int pt; // point incident to the edge (there is one list per edge) + double theta; // coordinate of the incidence on the edge }; Shape(); @@ -124,14 +124,14 @@ public: // -reset the graph, and ensure there's room for n points and m edges void Reset(int n = 0, int m = 0); // -points: - int AddPoint(const Geom::Point x); // as the function name says + int AddPoint(const Geom::Point x); // as the function name says // returns the index at which the point has been added in the array - void SubPoint(int p); // removes the point at index p + void SubPoint(int p); // removes the point at index p // nota: this function relocates the last point to the index p // so don't trust point indices if you use SubPoint - void SwapPoints(int a, int b); // swaps 2 points at indices a and b - void SwapPoints(int a, int b, int c); // swaps 3 points: c <- a <- b <- c - void SortPoints(); // sorts the points if needed (checks the need_points_sorting flag) + void SwapPoints(int a, int b); // swaps 2 points at indices a and b + void SwapPoints(int a, int b, int c); // swaps 3 points: c <- a <- b <- c + void SortPoints(); // sorts the points if needed (checks the need_points_sorting flag) // -edges: // add an edge between points of indices st and en @@ -143,98 +143,98 @@ public: // return the edge index in the array // version for the voronoi (with faces IDs) - void SubEdge(int e); // removes the edge at index e (same remarks as for SubPoint) - void SwapEdges(int a, int b); // swaps 2 edges - void SwapEdges(int a, int b, int c); // swaps 3 edges - void SortEdges(); // sort the edges if needed (checks the need_edges_sorting falg) + void SubEdge(int e); // removes the edge at index e (same remarks as for SubPoint) + void SwapEdges(int a, int b); // swaps 2 edges + void SwapEdges(int a, int b, int c); // swaps 3 edges + void SortEdges(); // sort the edges if needed (checks the need_edges_sorting falg) // primitives for topological manipulations // endpoint of edge at index b that is different from the point p inline int Other(int p, int b) const { - if (getEdge(b).st == p) { - return getEdge(b).en; - } - return getEdge(b).st; + if (getEdge(b).st == p) { + return getEdge(b).en; + } + return getEdge(b).st; } // next edge (after edge b) in the double-linked list at point p - inline int NextAt(int p, int b) const + inline int NextAt(int p, int b) const { - if (p == getEdge(b).st) { - return getEdge(b).nextS; - } - else if (p == getEdge(b).en) { - return getEdge(b).nextE; - } - - return -1; + if (p == getEdge(b).st) { + return getEdge(b).nextS; + } + else if (p == getEdge(b).en) { + return getEdge(b).nextE; + } + + return -1; } // previous edge inline int PrevAt(int p, int b) const { - if (p == getEdge(b).st) { - return getEdge(b).prevS; - } - else if (p == getEdge(b).en) { - return getEdge(b).prevE; - } - - return -1; + if (p == getEdge(b).st) { + return getEdge(b).prevS; + } + else if (p == getEdge(b).en) { + return getEdge(b).prevE; + } + + return -1; } // same as NextAt, but the list is considered circular - inline int CycleNextAt(int p, int b) const + inline int CycleNextAt(int p, int b) const { - if (p == getEdge(b).st) { - if (getEdge(b).nextS < 0) { - return getPoint(p).incidentEdge[FIRST]; - } - return getEdge(b).nextS; - } else if (p == getEdge(b).en) { - if (getEdge(b).nextE < 0) { - return getPoint(p).incidentEdge[FIRST]; - } - - return getEdge(b).nextE; - } - - return -1; + if (p == getEdge(b).st) { + if (getEdge(b).nextS < 0) { + return getPoint(p).incidentEdge[FIRST]; + } + return getEdge(b).nextS; + } else if (p == getEdge(b).en) { + if (getEdge(b).nextE < 0) { + return getPoint(p).incidentEdge[FIRST]; + } + + return getEdge(b).nextE; + } + + return -1; } // same as PrevAt, but the list is considered circular inline int CyclePrevAt(int p, int b) const { - if (p == getEdge(b).st) { - if (getEdge(b).prevS < 0) { - return getPoint(p).incidentEdge[LAST]; - } - return getEdge(b).prevS; - } else if (p == getEdge(b).en) { - if (getEdge(b).prevE < 0) { - return getPoint(p).incidentEdge[LAST]; - } - return getEdge(b).prevE; - } - - return -1; + if (p == getEdge(b).st) { + if (getEdge(b).prevS < 0) { + return getPoint(p).incidentEdge[LAST]; + } + return getEdge(b).prevS; + } else if (p == getEdge(b).en) { + if (getEdge(b).prevE < 0) { + return getPoint(p).incidentEdge[LAST]; + } + return getEdge(b).prevE; + } + + return -1; } - void ConnectStart(int p, int b); // set the point p as the start of edge b - void ConnectEnd(int p, int b); // set the point p as the end of edge b - void DisconnectStart(int b); // disconnect edge b from its start point - void DisconnectEnd(int b); // disconnect edge b from its end point + void ConnectStart(int p, int b); // set the point p as the start of edge b + void ConnectEnd(int p, int b); // set the point p as the end of edge b + void DisconnectStart(int b); // disconnect edge b from its start point + void DisconnectEnd(int b); // disconnect edge b from its end point // reverses edge b (start <-> end) - void Inverse(int b); + void Inverse(int b); // calc bounding box and sets leftX,rightX,topY and bottomY to their values void CalcBBox(bool strict_degree = false); // debug function: plots the graph (mac only) void Plot(double ix, double iy, double ir, double mx, double my, bool doPoint, - bool edgesNo, bool pointNo, bool doDir, char *fileName); + bool edgesNo, bool pointNo, bool doDir, char *fileName); // transforms a polygon in a "forme" structure, ie a set of contours, which can be holes (see ShapeUtils.h) // return NULL in case it's not possible @@ -246,7 +246,7 @@ public: void ConvertToForme(Path *dest, int nbP, Path **orig, bool splitWhenForced = false); // version trying to recover the nesting of subpaths (ie: holes) void ConvertToFormeNested(Path *dest, int nbP, Path **orig, int wildPath, int &nbNest, - int *&nesting, int *&contStart, bool splitWhenForced = false); + int *&nesting, int *&contStart, bool splitWhenForced = false); // sweeping a digraph to produce a intersection-free polygon // return 0 if everything is ok and a return code otherwise (see LivarotDefs.h) @@ -255,29 +255,29 @@ public: int ConvertToShape(Shape *a, FillRule directed = fill_nonZero, bool invert = false); // directed=false <=> even-odd fill rule // invert=true: make as if you inverted all edges in the source - int Reoriente(Shape *a); // subcase of ConvertToShape: the input a is already intersection-free + int Reoriente(Shape *a); // subcase of ConvertToShape: the input a is already intersection-free // all that's missing are the correct directions of the edges // Reoriented is equivalent to ConvertToShape(a,false,false) , but faster sicne // it doesn't computes interections nor adjacencies - void ForceToPolygon(); // force the Shape to believe it's a polygon (eulerian+intersection-free+no + void ForceToPolygon(); // force the Shape to believe it's a polygon (eulerian+intersection-free+no // duplicate edges+no duplicate points) // be careful when using this function // the coordinate rounding function inline static double Round(double x) { - return ldexp(rint(ldexp(x, 5)), -5); + return ldexp(rint(ldexp(x, 5)), -5); } // 2 miscannellous variations on it, to scale to and back the rounding grid inline static double HalfRound(double x) { - return ldexp(x, -5); + return ldexp(x, -5); } inline static double IHalfRound(double x) { - return ldexp(x, 5); + return ldexp(x, 5); } // boolean operations on polygons (requests intersection-free poylygons) @@ -341,20 +341,20 @@ public: // topological information: who links who? struct dg_point { - Geom::Point x; // position - int dI, dO; // indegree and outdegree + Geom::Point x; // position + int dI, dO; // indegree and outdegree int incidentEdge[2]; // first and last incident edge - int oldDegree; - - int totalDegree() const { return dI + dO; } + int oldDegree; + + int totalDegree() const { return dI + dO; } }; struct dg_arete { - Geom::Point dx; // edge vector - int st, en; // start and end points of the edge - int nextS, prevS; // next and previous edge in the double-linked list at the start point - int nextE, prevE; // next and previous edge in the double-linked list at the end point + Geom::Point dx; // edge vector + int st, en; // start and end points of the edge + int nextS, prevS; // next and previous edge in the double-linked list at the start point + int nextE, prevE; // next and previous edge in the double-linked list at the end point }; // lists of the nodes and edges @@ -386,68 +386,68 @@ private: // temporary data for the various algorithms struct edge_data { - int weight; // weight of the edge (to handle multiple edges) - Geom::Point rdx; // rounded edge vector - double length, sqlength, ilength, isqlength; // length^2, length, 1/length^2, 1/length - double siEd, coEd; // siEd=abs(rdy/length) and coEd=rdx/length - edge_data() : weight(0), length(0.0), sqlength(0.0), ilength(0.0), isqlength(0.0), siEd(0.0), coEd(0.0) {} - // used to determine the "most horizontal" edge between 2 edges + int weight; // weight of the edge (to handle multiple edges) + Geom::Point rdx; // rounded edge vector + double length, sqlength, ilength, isqlength; // length^2, length, 1/length^2, 1/length + double siEd, coEd; // siEd=abs(rdy/length) and coEd=rdx/length + edge_data() : weight(0), length(0.0), sqlength(0.0), ilength(0.0), isqlength(0.0), siEd(0.0), coEd(0.0) {} + // used to determine the "most horizontal" edge between 2 edges }; struct sweep_src_data { - void *misc; // pointer to the SweepTree* in the sweepline - int firstLinkedPoint; // not used - int stPt, enPt; // start- end end- points for this edge in the resulting polygon - int ind; // for the GetAdjacencies function: index in the sliceSegs array (for quick deletions) - int leftRnd, rightRnd; // leftmost and rightmost points (in the result polygon) that are incident to - // the edge, for the current sweep position - // not set if the edge doesn't start/end or intersect at the current sweep position - Shape *nextSh; // nextSh and nextBo identify the next edge in the list - int nextBo; // they are used to maintain a linked list of edge that start/end or intersect at - // the current sweep position - int curPoint, doneTo; - double curT; + void *misc; // pointer to the SweepTree* in the sweepline + int firstLinkedPoint; // not used + int stPt, enPt; // start- end end- points for this edge in the resulting polygon + int ind; // for the GetAdjacencies function: index in the sliceSegs array (for quick deletions) + int leftRnd, rightRnd; // leftmost and rightmost points (in the result polygon) that are incident to + // the edge, for the current sweep position + // not set if the edge doesn't start/end or intersect at the current sweep position + Shape *nextSh; // nextSh and nextBo identify the next edge in the list + int nextBo; // they are used to maintain a linked list of edge that start/end or intersect at + // the current sweep position + int curPoint, doneTo; + double curT; }; struct sweep_dest_data { - void *misc; // used to check if an edge has already been seen during the depth-first search - int suivParc, precParc; // previous and current next edge in the depth-first search - int leW, riW; // left and right winding numbers for this edge - int ind; // order of the edges during the depth-first search + void *misc; // used to check if an edge has already been seen during the depth-first search + int suivParc, precParc; // previous and current next edge in the depth-first search + int leW, riW; // left and right winding numbers for this edge + int ind; // order of the edges during the depth-first search }; struct raster_data { - SweepTree *misc; // pointer to the associated SweepTree* in the sweepline - double lastX, lastY, curX, curY; // curX;curY is the current intersection of the edge with the sweepline - // lastX;lastY is the intersection with the previous sweepline - bool sens; // true if the edge goes down, false otherwise - double calcX; // horizontal position of the intersection of the edge with the - // previous sweepline - double dxdy, dydx; // horizontal change per unit vertical move of the intersection with the sweepline - int guess; + SweepTree *misc; // pointer to the associated SweepTree* in the sweepline + double lastX, lastY, curX, curY; // curX;curY is the current intersection of the edge with the sweepline + // lastX;lastY is the intersection with the previous sweepline + bool sens; // true if the edge goes down, false otherwise + double calcX; // horizontal position of the intersection of the edge with the + // previous sweepline + double dxdy, dydx; // horizontal change per unit vertical move of the intersection with the sweepline + int guess; }; struct point_data { - int oldInd, newInd; // back and forth indices used when sorting the points, to know where they have - // been relocated in the array - int pending; // number of intersection attached to this edge, and also used when sorting arrays - int edgeOnLeft; // not used (should help speeding up winding calculations) - int nextLinkedPoint; // not used - Shape *askForWindingS; - int askForWindingB; - Geom::Point rx; // rounded coordinates of the point + int oldInd, newInd; // back and forth indices used when sorting the points, to know where they have + // been relocated in the array + int pending; // number of intersection attached to this edge, and also used when sorting arrays + int edgeOnLeft; // not used (should help speeding up winding calculations) + int nextLinkedPoint; // not used + Shape *askForWindingS; + int askForWindingB; + Geom::Point rx; // rounded coordinates of the point }; struct edge_list - { // temporary array of edges for easier sorting - int no; - bool starting; - Geom::Point x; + { // temporary array of edges for easier sorting + int no; + bool starting; + Geom::Point x; }; void initialisePointData(); @@ -470,27 +470,27 @@ private: void SortPointsByOldInd(int s, int e); // fonctions annexes pour ConvertToShape et Booleen - void ResetSweep(); // allocates sweep structures - void CleanupSweep(); // deallocates them + void ResetSweep(); // allocates sweep structures + void CleanupSweep(); // deallocates them // edge sorting function void SortEdgesList(edge_list *edges, int s, int e); - void TesteIntersection(SweepTree *t, Side s, bool onlyDiff); // test if there is an intersection + void TesteIntersection(SweepTree *t, Side s, bool onlyDiff); // test if there is an intersection bool TesteIntersection(SweepTree *iL, SweepTree *iR, Geom::Point &atx, double &atL, double &atR, bool onlyDiff); bool TesteIntersection(Shape *iL, Shape *iR, int ilb, int irb, - Geom::Point &atx, double &atL, double &atR, - bool onlyDiff); + Geom::Point &atx, double &atL, double &atR, + bool onlyDiff); bool TesteAdjacency(Shape *iL, int ilb, const Geom::Point atx, int nPt, - bool push); + bool push); int PushIncidence(Shape *a, int cb, int pt, double theta); int CreateIncidence(Shape *a, int cb, int pt); void AssemblePoints(Shape *a); int AssemblePoints(int st, int en); void AssembleAretes(FillRule directed = fill_nonZero); void AddChgt(int lastPointNo, int lastChgtPt, Shape *&shapeHead, - int &edgeHead, sTreeChangeType type, Shape *lS, int lB, Shape *rS, - int rB); + int &edgeHead, sTreeChangeType type, Shape *lS, int lB, Shape *rS, + int rB); void CheckAdjacencies(int lastPointNo, int lastChgtPt, Shape *shapeHead, int edgeHead); void CheckEdges(int lastPointNo, int lastChgtPt, Shape *a, Shape *b, BooleanOp mod); void Avance(int lastPointNo, int lastChgtPt, Shape *iS, int iB, Shape *a, Shape *b, BooleanOp mod); @@ -513,14 +513,14 @@ private: void AvanceEdge(int no, float to, AlphaLigne *line, bool exact, float step); void AddContour(Path * dest, int nbP, Path **orig, int startBord, - int curBord, bool splitWhenForced); + int curBord, bool splitWhenForced); int ReFormeLineTo(int bord, int curBord, Path *dest, Path *orig); int ReFormeArcTo(int bord, int curBord, Path *dest, Path *orig); int ReFormeCubicTo(int bord, int curBord, Path *dest, Path *orig); int ReFormeBezierTo(int bord, int curBord, Path *dest, Path *orig); void ReFormeBezierChunk(const Geom::Point px, const Geom::Point nx, - Path *dest, int inBezier, int nbInterm, - Path *from, int p, double ts, double te); + Path *dest, int inBezier, int nbInterm, + Path *from, int p, double ts, double te); int QuickRasterChgEdge(int oBord, int nbord, double x); int QuickRasterAddEdge(int bord, double x, int guess); @@ -554,11 +554,11 @@ private: std::vector<point_data> pData; static int CmpQRs(const quick_raster_data &p1, const quick_raster_data &p2) { - if ( fabs(p1.x - p2.x) < 0.00001 ) { - return 0; - } - - return ( ( p1.x < p2.x ) ? -1 : 1 ); + if ( fabs(p1.x - p2.x) < 0.00001 ) { + return 0; + } + + return ( ( p1.x < p2.x ) ? -1 : 1 ); }; // edge direction comparison function diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 9232792f6..f761a6a7c 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -28,7 +28,6 @@ #include "tools-switch.h" #include "message-stack.h" #include "desktop.h" -#include "nodepath.h" #include "knotholder.h" #include "live_effects/lpeobject.h" @@ -665,13 +664,6 @@ Effect::resetDefaults(SPItem * /*item*/) } void -Effect::setup_nodepath(Inkscape::NodePath::Path *np) -{ - np->helperpath_rgba = 0xff0000ff; - np->helperpath_width = 1.0; -} - -void Effect::transform_multiply(Geom::Matrix const& postmul, bool set) { // cycle through all parameters. Most parameters will not need transformation, but path and point params do. @@ -681,7 +673,6 @@ Effect::transform_multiply(Geom::Matrix const& postmul, bool set) } } -// TODO: take _all_ parameters into account, not only PointParams bool Effect::providesKnotholder() { diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 5d67ed016..a8d34a233 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -92,8 +92,6 @@ public: */ virtual void resetDefaults(SPItem * item); - virtual void setup_nodepath(Inkscape::NodePath::Path *np); - /// /todo: is this method really necessary? it causes UI inconsistensies... (johan) virtual void transform_multiply(Geom::Matrix const& postmul, bool set); diff --git a/src/live_effects/lpe-constructgrid.cpp b/src/live_effects/lpe-constructgrid.cpp index 144f4720d..4725573d7 100644 --- a/src/live_effects/lpe-constructgrid.cpp +++ b/src/live_effects/lpe-constructgrid.cpp @@ -16,8 +16,6 @@ #include <2geom/path.h> #include <2geom/transforms.h> -#include "nodepath.h" - namespace Inkscape { namespace LivePathEffect { @@ -81,13 +79,6 @@ LPEConstructGrid::doEffect_path (std::vector<Geom::Path> const & path_in) } } -void -LPEConstructGrid::setup_nodepath(Inkscape::NodePath::Path *np) -{ - Effect::setup_nodepath(np); - sp_nodepath_make_straight_path(np); -} - } //namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-constructgrid.h b/src/live_effects/lpe-constructgrid.h index 716960d32..c7e695794 100644 --- a/src/live_effects/lpe-constructgrid.h +++ b/src/live_effects/lpe-constructgrid.h @@ -27,8 +27,6 @@ public: virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); - virtual void setup_nodepath(Inkscape::NodePath::Path *np); - private: ScalarParam nr_x; ScalarParam nr_y; diff --git a/src/live_effects/lpe-extrude.cpp b/src/live_effects/lpe-extrude.cpp index af933eae6..c861515aa 100644 --- a/src/live_effects/lpe-extrude.cpp +++ b/src/live_effects/lpe-extrude.cpp @@ -16,6 +16,7 @@ #include <2geom/path.h> #include <2geom/piecewise.h> #include <2geom/transforms.h> +#include <algorithm> namespace Inkscape { namespace LivePathEffect { @@ -35,6 +36,25 @@ LPEExtrude::~LPEExtrude() } +static bool are_colinear(Geom::Point a, Geom::Point b) { + return Geom::are_near(cross(a,b), 0., 0.5); +} + +// find cusps, except at start/end for closed paths. +// this should be factored out later. +static std::vector<double> find_cusps( Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in ) { + using namespace Geom; + Piecewise<D2<SBasis> > deriv = derivative(pwd2_in); + std::vector<double> cusps; + // cusps are spots where the derivative jumps. + for (unsigned i = 1 ; i < deriv.size() ; ++i) { + if ( ! are_colinear(deriv[i-1].at1(), deriv[i].at0()) ) { + // there is a jump in the derivative, so add it to the cusps list + cusps.push_back(deriv.cuts[i]); + } + } + return cusps; +} Geom::Piecewise<Geom::D2<Geom::SBasis> > LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in) @@ -48,6 +68,9 @@ LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2 switch( 1 ) { case 0: { + /* This one results in the following subpaths: the original, a displaced copy, and connector lines between the two + */ + Piecewise<D2<SBasis> > pwd2_out = pwd2_in; // generate extrusion bottom: (just a copy of original path, displaced a bit) pwd2_out.concat( pwd2_in + extrude_vector.getVector() ); @@ -75,15 +98,42 @@ LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2 default: case 1: { + /* This one creates separate closed subpaths that correspond to the faces of the extruded shape. + * When the LPE is complete, one can convert the shape to a normal path, then break subpaths apart and start coloring them. + */ + Piecewise<D2<SBasis> > pwd2_out; - bool closed_path = are_near(pwd2_in.firstValue(), pwd2_in.lastValue()); // split input path in pieces between points where deriv == vector Piecewise<D2<SBasis> > deriv = derivative(pwd2_in); std::vector<double> rts = roots(dot(deriv, rot90(extrude_vector.getVector()))); + + std::vector<double> cusps = find_cusps(pwd2_in); + + // see if we should treat the path as being closed. + bool closed_path = false; + if ( are_near(pwd2_in.firstValue(), pwd2_in.lastValue()) ) { + // the path is closed, however if there is a cusp at the closing point, we should treat it as being an open path. + if ( are_colinear(deriv.firstValue(), deriv.lastValue()) ) { + // there is no jump in the derivative, so treat path as being closed + closed_path = true; + } + } + + std::vector<double> connector_pts; + if (rts.size() < 1) { + connector_pts = cusps; + } else if (cusps.size() < 1) { + connector_pts = rts; + } else { + connector_pts = rts; + connector_pts.insert(connector_pts.begin(), cusps.begin(), cusps.end()); + sort(connector_pts.begin(), connector_pts.end()); + } + double portion_t = 0.; - for (unsigned i = 0; i < rts.size() ; ++i) { - Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, rts[i] ); - portion_t = rts[i]; + for (unsigned i = 0; i < connector_pts.size() ; ++i) { + Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, connector_pts[i] ); + portion_t = connector_pts[i]; if (closed_path && i == 0) { // if the path is closed, skip the first cut and add it to the last cut later continue; @@ -96,7 +146,7 @@ LPEExtrude::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2 } if (closed_path) { Piecewise<D2<SBasis> > cut = portion(pwd2_in, portion_t, pwd2_in.domain().max() ); - cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), rts[0] )); + cut.continuousConcat(portion(pwd2_in, pwd2_in.domain().min(), connector_pts[0] )); Piecewise<D2<SBasis> > part = cut; part.continuousConcat(connector + cut.lastValue()); part.continuousConcat(reverse(cut) + extrude_vector.getVector()); diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp index e211483c6..00f7ec193 100644 --- a/src/live_effects/lpe-gears.cpp +++ b/src/live_effects/lpe-gears.cpp @@ -2,6 +2,8 @@ /* * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> + * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com> + * Copyright 2006 Aaron Spike <aaron@ekips.org> * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -14,8 +16,6 @@ #include <2geom/bezier-to-sbasis.h> #include <2geom/path.h> -#include "nodepath.h" - using std::vector; using namespace Geom; @@ -261,13 +261,6 @@ LPEGears::doEffect_path (std::vector<Geom::Path> const & path_in) return path_out; } -void -LPEGears::setup_nodepath(Inkscape::NodePath::Path *np) -{ - Effect::setup_nodepath(np); - sp_nodepath_make_straight_path(np); -} - } // namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-gears.h b/src/live_effects/lpe-gears.h index 4c3a9938b..bd5e4c4f9 100644 --- a/src/live_effects/lpe-gears.h +++ b/src/live_effects/lpe-gears.h @@ -24,8 +24,6 @@ public: virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in); - virtual void setup_nodepath(Inkscape::NodePath::Path *np); - private: ScalarParam teeth; ScalarParam phi; diff --git a/src/live_effects/lpe-lattice.cpp b/src/live_effects/lpe-lattice.cpp index 0beedb537..50ecdf04b 100644 --- a/src/live_effects/lpe-lattice.cpp +++ b/src/live_effects/lpe-lattice.cpp @@ -22,7 +22,6 @@ #include "sp-path.h" #include "display/curve.h" #include "svg/svg.h" -#include "nodepath.h" #include <2geom/sbasis.h> #include <2geom/sbasis-2d.h> diff --git a/src/live_effects/lpe-rough-hatches.cpp b/src/live_effects/lpe-rough-hatches.cpp index 228857ebf..f110aa743 100644 --- a/src/live_effects/lpe-rough-hatches.cpp +++ b/src/live_effects/lpe-rough-hatches.cpp @@ -279,6 +279,10 @@ LPERoughHatches::LPERoughHatches(LivePathEffectObject *lpeobject) : front_thickness.param_set_range(0, NR_HUGE); back_thickness.param_set_range(0, NR_HUGE); + // hide the widgets for direction and bender vectorparams + direction.widget_is_visible = false; + bender.widget_is_visible = false; + concatenate_before_pwd2 = false; show_orig_path = true; } diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp index 794fd980e..7c8262af6 100644 --- a/src/live_effects/lpe-spiro.cpp +++ b/src/live_effects/lpe-spiro.cpp @@ -7,7 +7,6 @@ #include "live_effects/lpe-spiro.h" #include "display/curve.h" -#include "nodepath.h" #include <typeinfo> #include <2geom/pathvector.h> #include <2geom/matrix.h> @@ -116,14 +115,6 @@ LPESpiro::~LPESpiro() } void -LPESpiro::setup_nodepath(Inkscape::NodePath::Path *np) -{ - Effect::setup_nodepath(np); - sp_nodepath_show_handles(np, false); -// sp_nodepath_show_helperpath(np, false); -} - -void LPESpiro::doEffect(SPCurve * curve) { using Geom::X; diff --git a/src/live_effects/lpe-spiro.h b/src/live_effects/lpe-spiro.h index 7256665a2..4fcd9eaaa 100644 --- a/src/live_effects/lpe-spiro.h +++ b/src/live_effects/lpe-spiro.h @@ -24,7 +24,6 @@ public: virtual LPEPathFlashType pathFlashType() { return SUPPRESS_FLASH; } - virtual void setup_nodepath(Inkscape::NodePath::Path *np); virtual void doEffect(SPCurve * curve); private: diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp index 7fd0ac0b4..85f8cde0c 100644 --- a/src/live_effects/lpe-vonkoch.cpp +++ b/src/live_effects/lpe-vonkoch.cpp @@ -8,7 +8,6 @@ #include <cstdio> #include "live_effects/lpe-vonkoch.h" -#include "nodepath.h" #include <2geom/transforms.h> //using std::vector; @@ -19,7 +18,7 @@ void VonKochPathParam::param_setup_nodepath(Inkscape::NodePath::Path *np) { PathParam::param_setup_nodepath(np); - sp_nodepath_make_straight_path(np); + //sp_nodepath_make_straight_path(np); } //FIXME: a path is used here instead of 2 points to work around path/point param incompatibility bug. @@ -27,12 +26,12 @@ void VonKochRefPathParam::param_setup_nodepath(Inkscape::NodePath::Path *np) { PathParam::param_setup_nodepath(np); - sp_nodepath_make_straight_path(np); + //sp_nodepath_make_straight_path(np); } bool VonKochRefPathParam::param_readSVGValue(const gchar * strvalue) { - std::vector<Geom::Path> old = _pathvector; + Geom::PathVector old = _pathvector; bool res = PathParam::param_readSVGValue(strvalue); if (res && _pathvector.size()==1 && _pathvector.front().size()==1){ return true; diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp index 33e50155c..d8d5b0a7c 100644 --- a/src/live_effects/parameter/path.cpp +++ b/src/live_effects/parameter/path.cpp @@ -28,10 +28,8 @@ // needed for on-canvas editting: #include "tools-switch.h" #include "shape-editor.h" -#include "node-context.h" #include "desktop-handles.h" #include "selection.h" -#include "nodepath.h" // clipboard support #include "ui/clipboard.h" // required for linking to other paths @@ -40,6 +38,10 @@ #include "sp-text.h" #include "display/curve.h" +#include "ui/tool/node-tool.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/shape-record.h" + namespace Inkscape { @@ -193,28 +195,35 @@ PathParam::param_newWidget(Gtk::Tooltips * tooltips) } void -PathParam::param_editOncanvas(SPItem * item, SPDesktop * dt) +PathParam::param_editOncanvas(SPItem *item, SPDesktop * dt) { - // If not already in nodecontext, goto it! + using namespace Inkscape::UI; + + // TODO remove the tools_switch atrocity. if (!tools_isactive(dt, TOOLS_NODES)) { tools_switch(dt, TOOLS_NODES); } - ShapeEditor * shape_editor = dt->event_context->shape_editor; + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + std::set<ShapeRecord> shapes; + ShapeRecord r; + + r.role = SHAPE_ROLE_LPE_PARAM; + r.edit_transform = sp_item_i2d_affine(item); // TODO is it right? if (!href) { - shape_editor->set_item_lpe_path_parameter(item, param_effect->getLPEObj(), param_key.c_str()); + r.item = reinterpret_cast<SPItem*>(param_effect->getLPEObj()); + r.lpe_key = param_key; } else { - // set referred item for editing - shape_editor->set_item(ref.getObject(), SH_NODEPATH); + r.item = ref.getObject(); } + shapes.insert(r); + nt->_multipath->setItems(shapes); } void -PathParam::param_setup_nodepath(Inkscape::NodePath::Path *np) +PathParam::param_setup_nodepath(Inkscape::NodePath::Path *) { - np->show_helperpath = true; - np->helperpath_rgba = 0x009000ff; - np->helperpath_width = 1.0; + // TODO this method should not exist at all! } void @@ -403,7 +412,7 @@ void PathParam::on_paste_button_click() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - Glib::ustring svgd = cm->getPathParameter(); + Glib::ustring svgd = cm->getPathParameter(SP_ACTIVE_DESKTOP); paste_param_path(svgd.data()); sp_document_done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Paste path parameter")); @@ -420,7 +429,7 @@ void PathParam::on_link_button_click() { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - Glib::ustring pathid = cm->getShapeOrTextObjectId(); + Glib::ustring pathid = cm->getShapeOrTextObjectId(SP_ACTIVE_DESKTOP); if (pathid == "") { return; diff --git a/src/live_effects/parameter/vector.cpp b/src/live_effects/parameter/vector.cpp index 35afe7f5d..5496b52f2 100644 --- a/src/live_effects/parameter/vector.cpp +++ b/src/live_effects/parameter/vector.cpp @@ -13,8 +13,9 @@ #include "svg/stringstream.h" #include <gtkmm.h> -// needed for on-canvas editting: -class SPDesktop; +#include "ui/widget/registered-widget.h" +#include "live_effects/effect.h" +#include "desktop.h" namespace Inkscape { @@ -82,28 +83,23 @@ VectorParam::param_getSVGValue() const Gtk::Widget * VectorParam::param_newWidget(Gtk::Tooltips * /*tooltips*/) { -/* - Inkscape::UI::Widget::RegisteredTransformedPoint * pointwdg = Gtk::manage( - new Inkscape::UI::Widget::RegisteredTransformedPoint( param_label, - param_tooltip, - param_key, - *param_wr, - param_effect->getRepr(), - param_effect->getSPDoc() ) ); - // TODO: fix to get correct desktop (don't use SP_ACTIVE_DESKTOP) - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Geom::Matrix transf = desktop->doc2dt(); - pointwdg->setTransform(transf); - pointwdg->setValue( *this ); + Inkscape::UI::Widget::RegisteredVector * pointwdg = Gtk::manage( + new Inkscape::UI::Widget::RegisteredVector( param_label, + param_tooltip, + param_key, + *param_wr, + param_effect->getRepr(), + param_effect->getSPDoc() ) ); + pointwdg->setPolarCoords(); + pointwdg->setValue( vector, origin ); pointwdg->clearProgrammatically(); - pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter")); + pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change vector parameter")); Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() ); static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true); static_cast<Gtk::HBox*>(hbox)->show_all_children(); return dynamic_cast<Gtk::Widget *> (hbox); - */ return NULL; } void diff --git a/src/lpe-tool-context.cpp b/src/lpe-tool-context.cpp index be465e324..438258cb3 100644 --- a/src/lpe-tool-context.cpp +++ b/src/lpe-tool-context.cpp @@ -22,7 +22,6 @@ #include "macros.h" #include "forward.h" -#include "pixmaps/cursor-node.xpm" #include "pixmaps/cursor-crosshairs.xpm" #include <gtk/gtk.h> #include "desktop.h" @@ -179,9 +178,6 @@ sp_lpetool_context_setup(SPEventContext *ec) } lc->_lpetool_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); - - - lc->shape_editor->update_statusbar(); } /** @@ -193,13 +189,9 @@ sp_lpetool_context_selection_changed(Inkscape::Selection *selection, gpointer da { SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(data); - // TODO: update ShapeEditorsCollective instead - lc->shape_editor->unset_item(SH_NODEPATH); lc->shape_editor->unset_item(SH_KNOTHOLDER); SPItem *item = selection->singleItem(); - lc->shape_editor->set_item(item, SH_NODEPATH); lc->shape_editor->set_item(item, SH_KNOTHOLDER); - lc->shape_editor->update_statusbar(); } static void @@ -280,7 +272,6 @@ sp_lpetool_context_root_handler(SPEventContext *event_context, GdkEvent *event) event_context->xp = (gint) event->button.x; event_context->yp = (gint) event->button.y; event_context->within_tolerance = true; - lc->shape_editor->cancel_hit(); using namespace Inkscape::LivePathEffect; @@ -296,28 +287,6 @@ sp_lpetool_context_root_handler(SPEventContext *event_context, GdkEvent *event) ret = ((SPEventContextClass *) lpetool_parent_class)->root_handler(event_context, event); } break; - case GDK_MOTION_NOTIFY: - { - if (!lc->shape_editor->has_nodepath() || selection->singleItem() == NULL) { - break; - } - - bool over_stroke = false; - over_stroke = lc->shape_editor->is_over_stroke(Geom::Point(event->motion.x, event->motion.y), false); - - if (over_stroke) { - event_context->cursor_shape = cursor_node_xpm; - event_context->hot_x = 1; - event_context->hot_y = 1; - sp_event_context_update_cursor(event_context); - } else { - lc->cursor_shape = cursor_crosshairs_xpm; - lc->hot_x = 7; - lc->hot_y = 7; - sp_event_context_update_cursor(event_context); - } - } - break; case GDK_BUTTON_RELEASE: diff --git a/src/main.cpp b/src/main.cpp index 75e882e99..d11222203 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -56,6 +56,10 @@ #include "gc-core.h" +#ifdef AND +#undef AND +#endif + #include "macros.h" #include "file.h" #include "document.h" @@ -94,14 +98,9 @@ #include <extension/input.h> #ifdef WIN32 -//#define REPLACEARGS_ANSI -//#define REPLACEARGS_DEBUG - #include "registrytool.h" - #include "extension/internal/win32.h" using Inkscape::Extension::Internal::PrintWin32; - #endif // WIN32 #include "extension/init.h" @@ -143,6 +142,7 @@ enum { SP_ARG_EXPORT_PS, SP_ARG_EXPORT_EPS, SP_ARG_EXPORT_PDF, + SP_ARG_EXPORT_LATEX, #ifdef WIN32 SP_ARG_EXPORT_EMF, #endif //WIN32 @@ -181,6 +181,7 @@ static gchar *sp_export_dpi = NULL; static gchar *sp_export_area = NULL; static gboolean sp_export_area_drawing = FALSE; static gboolean sp_export_area_page = FALSE; +static gboolean sp_export_latex = FALSE; static gchar *sp_export_width = NULL; static gchar *sp_export_height = NULL; static gchar *sp_export_id = NULL; @@ -224,6 +225,7 @@ static void resetCommandlineGlobals() { sp_export_area = NULL; sp_export_area_drawing = FALSE; sp_export_area_page = FALSE; + sp_export_latex = FALSE; sp_export_width = NULL; sp_export_height = NULL; sp_export_id = NULL; @@ -372,6 +374,11 @@ struct poptOption options[] = { N_("Export document to a PDF file"), N_("FILENAME")}, + {"export-latex", 0, + POPT_ARG_NONE, &sp_export_latex, SP_ARG_EXPORT_LATEX, + N_("Export PDF/PS/EPS without text. Besides the PDF/PS/EPS, a LaTeX file is exported, putting the text on top of the PDF/PS/EPS file. Include the result in LaTeX like: \\input{latexfile.tex}"), + NULL}, + #ifdef WIN32 {"export-emf", 'M', POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF, @@ -646,6 +653,7 @@ main(int argc, char **argv) || !strncmp(argv[i], "--export-eps", 12) || !strcmp(argv[i], "-A") || !strncmp(argv[i], "--export-pdf", 12) + || !strncmp(argv[i], "--export-latex", 14) #ifdef WIN32 || !strcmp(argv[i], "-M") || !strncmp(argv[i], "--export-emf", 12) @@ -1203,11 +1211,11 @@ static void do_query_all_recurse (SPObject *o) { SPItem *item = ((SPItem *) o); - if (o->id && SP_IS_ITEM(item)) { + if (o->getId() && SP_IS_ITEM(item)) { Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item)); if (area) { Inkscape::SVGOStringStream os; - os << o->id; + os << o->getId(); os << "," << area->min()[Geom::X]; os << "," << area->min()[Geom::Y]; os << "," << area->dimensions()[Geom::X]; @@ -1518,6 +1526,12 @@ static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime (*i)->set_param_bool("textToPath", FALSE); } + if (sp_export_latex) { + (*i)->set_param_bool("textToLaTeX", TRUE); + } else { + (*i)->set_param_bool("textToLaTeX", FALSE); + } + if (sp_export_ignore_filters) { (*i)->set_param_bool("blurToBitmap", FALSE); } else { diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index aeb6500bf..581b8b39c 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -269,6 +269,7 @@ static char const menus_skeleton[] = " <verb verb-id=\"TutorialsAdvanced\" />\n" " <verb verb-id=\"TutorialsTracing\" />\n" " <verb verb-id=\"TutorialsCalligraphy\" />\n" +" <verb verb-id=\"TutorialsInterpolate\" />\n" " <verb verb-id=\"TutorialsDesign\" />\n" " <verb verb-id=\"TutorialsTips\" />\n" " </submenu>\n" diff --git a/src/node-context.cpp b/src/node-context.cpp deleted file mode 100644 index 7efa57290..000000000 --- a/src/node-context.cpp +++ /dev/null @@ -1,868 +0,0 @@ -#define __SP_NODE_CONTEXT_C__ - -/* - * Node editing context - * - * Authors: - * Lauris Kaplinski <lauris@kaplinski.com> - * bulia byak <buliabyak@users.sf.net> - * - * This code is in public domain - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif -#include <cstring> -#include <string> -#include <gdk/gdkkeysyms.h> -#include "macros.h" -#include <glibmm/i18n.h> -#include "display/sp-canvas-util.h" -#include "object-edit.h" -#include "sp-path.h" -#include "path-chemistry.h" -#include "rubberband.h" -#include "desktop.h" -#include "desktop-handles.h" -#include "selection.h" -#include "pixmaps/cursor-node.xpm" -#include "message-context.h" -#include "node-context.h" -#include "pixmaps/cursor-node-d.xpm" -#include "preferences.h" -#include "xml/node-event-vector.h" -#include "style.h" -#include "splivarot.h" -#include "shape-editor.h" -#include "live_effects/effect.h" - -#include "sp-lpe-item.h" - -// needed for flash nodepath upon mouseover: -#include "display/canvas-bpath.h" -#include "display/curve.h" - -static void sp_node_context_class_init(SPNodeContextClass *klass); -static void sp_node_context_init(SPNodeContext *node_context); -static void sp_node_context_dispose(GObject *object); - -static void sp_node_context_setup(SPEventContext *ec); -static gint sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event); -static gint sp_node_context_item_handler(SPEventContext *event_context, - SPItem *item, GdkEvent *event); - -static SPEventContextClass *parent_class; - -GType -sp_node_context_get_type() -{ - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof(SPNodeContextClass), - NULL, NULL, - (GClassInitFunc) sp_node_context_class_init, - NULL, NULL, - sizeof(SPNodeContext), - 4, - (GInstanceInitFunc) sp_node_context_init, - NULL, /* value_table */ - }; - type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPNodeContext", &info, (GTypeFlags)0); - } - return type; -} - -static void -sp_node_context_class_init(SPNodeContextClass *klass) -{ - GObjectClass *object_class = (GObjectClass *) klass; - SPEventContextClass *event_context_class = (SPEventContextClass *) klass; - - parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass); - - object_class->dispose = sp_node_context_dispose; - - event_context_class->setup = sp_node_context_setup; - event_context_class->root_handler = sp_node_context_root_handler; - event_context_class->item_handler = sp_node_context_item_handler; -} - -static void -sp_node_context_init(SPNodeContext *node_context) -{ - SPEventContext *event_context = SP_EVENT_CONTEXT(node_context); - - event_context->cursor_shape = cursor_node_xpm; - event_context->hot_x = 1; - event_context->hot_y = 1; - - node_context->leftalt = FALSE; - node_context->rightalt = FALSE; - node_context->leftctrl = FALSE; - node_context->rightctrl = FALSE; - - new (&node_context->sel_changed_connection) sigc::connection(); - - node_context->flash_tempitem = NULL; - node_context->flashed_item = NULL; - node_context->remove_flash_counter = 0; -} - -static void -sp_node_context_dispose(GObject *object) -{ - SPNodeContext *nc = SP_NODE_CONTEXT(object); - SPEventContext *ec = SP_EVENT_CONTEXT(object); - - ec->enableGrDrag(false); - - if (nc->grabbed) { - sp_canvas_item_ungrab(nc->grabbed, GDK_CURRENT_TIME); - nc->grabbed = NULL; - } - - nc->sel_changed_connection.disconnect(); - nc->sel_changed_connection.~connection(); - - delete ec->shape_editor; - - if (nc->_node_message_context) { - delete nc->_node_message_context; - } - - G_OBJECT_CLASS(parent_class)->dispose(object); -} - -static void -sp_node_context_setup(SPEventContext *ec) -{ - SPNodeContext *nc = SP_NODE_CONTEXT(ec); - - if (((SPEventContextClass *) parent_class)->setup) - ((SPEventContextClass *) parent_class)->setup(ec); - - Inkscape::Selection *selection = sp_desktop_selection (ec->desktop); - nc->sel_changed_connection.disconnect(); - nc->sel_changed_connection = - selection->connectChanged(sigc::bind(sigc::ptr_fun(&sp_node_context_selection_changed), (gpointer)nc)); - - SPItem *item = selection->singleItem(); - - ec->shape_editor = new ShapeEditor(ec->desktop); - - nc->rb_escaped = false; - - nc->cursor_drag = false; - - nc->added_node = false; - - nc->current_state = SP_NODE_CONTEXT_INACTIVE; - - if (item) { - ec->shape_editor->set_item(item, SH_NODEPATH); - ec->shape_editor->set_item(item, SH_KNOTHOLDER); - } - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (prefs->getBool("/tools/nodes/selcue")) { - ec->enableSelectionCue(); - } - if (prefs->getBool("/tools/nodes/gradientdrag")) { - ec->enableGrDrag(); - } - - ec->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive - - nc->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); - - ec->shape_editor->update_statusbar(); -} - -static void -sp_node_context_flash_path(SPEventContext *event_context, SPItem *item, guint timeout) { - SPNodeContext *nc = SP_NODE_CONTEXT(event_context); - - nc->remove_flash_counter = 3; // for some reason root_handler is called twice after each item_handler... - if (nc->flashed_item != item) { - // we entered a new item - nc->flashed_item = item; - SPDesktop *desktop = event_context->desktop; - if (nc->flash_tempitem) { - desktop->remove_temporary_canvasitem(nc->flash_tempitem); - nc->flash_tempitem = NULL; - } - - SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, item); - - if (canvasitem) { - nc->flash_tempitem = desktop->add_temporary_canvasitem (canvasitem, timeout); - } - } -} - -/** -\brief Callback that processes the "changed" signal on the selection; -destroys old and creates new nodepath and reassigns listeners to the new selected item's repr -*/ -void -sp_node_context_selection_changed(Inkscape::Selection *selection, gpointer data) -{ - SPEventContext *ec = SP_EVENT_CONTEXT(data); - - // TODO: update ShapeEditorsCollective instead - ec->shape_editor->unset_item(SH_NODEPATH); - ec->shape_editor->unset_item(SH_KNOTHOLDER); - SPItem *item = selection->singleItem(); - ec->shape_editor->set_item(item, SH_NODEPATH); - ec->shape_editor->set_item(item, SH_KNOTHOLDER); - ec->shape_editor->update_statusbar(); -} - -void -sp_node_context_show_modifier_tip(SPEventContext *event_context, GdkEvent *event) -{ - sp_event_show_modifier_tip - (event_context->defaultMessageContext(), event, - _("<b>Ctrl</b>: toggle node type, snap handle angle, move hor/vert; <b>Ctrl+Alt</b>: move along handles"), - _("<b>Shift</b>: toggle node selection, disable snapping, rotate both handles"), - _("<b>Alt</b>: lock handle length; <b>Ctrl+Alt</b>: move along handles")); -} - -static gint -sp_node_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) -{ - gint ret = FALSE; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - SPDesktop *desktop = event_context->desktop; - - switch (event->type) { - case GDK_MOTION_NOTIFY: - { - // find out actual item we're over, disregarding groups - SPItem *actual_item = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), FALSE, TRUE); - if (!actual_item) - break; - - - if (prefs->getBool("/tools/nodes/pathflash_enabled")) { - if (prefs->getBool("/tools/nodes/pathflash_unselected")) { - // do not flash if we have some path selected and a single item in selection (i.e. it - // is the same path that we're editing) - SPDesktop *desktop = event_context->desktop; - ShapeEditor* se = event_context->shape_editor; - Inkscape::Selection *selection = sp_desktop_selection (desktop); - if (se->has_nodepath() && selection->singleItem()) { - break; - } - } - if (SP_IS_LPE_ITEM(actual_item)) { - Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(actual_item)); - if (lpe && (lpe->providesOwnFlashPaths() || - lpe->pathFlashType() == Inkscape::LivePathEffect::SUPPRESS_FLASH)) { - // path should be suppressed or permanent; this is handled in - // sp_node_context_selection_changed() - break; - } - } - guint timeout = prefs->getInt("/tools/nodes/pathflash_timeout", 500); - sp_node_context_flash_path(event_context, actual_item, timeout); - } - } - break; - - default: - break; - } - - if (((SPEventContextClass *) parent_class)->item_handler) - ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event); - - return ret; -} - -static gint -sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) -{ - SPDesktop *desktop = event_context->desktop; - ShapeEditor* se = event_context->shape_editor; - Inkscape::Selection *selection = sp_desktop_selection (desktop); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - SPNodeContext *nc = SP_NODE_CONTEXT(event_context); - double const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); // in px - event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); // read every time, to make prefs changes really live - int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); - double const offset = prefs->getDoubleLimited("/options/defaultscale/value", 2, 0, 1000); - - if ( (nc->flash_tempitem) && (nc->remove_flash_counter <= 0) ) { - desktop->remove_temporary_canvasitem(nc->flash_tempitem); - nc->flash_tempitem = NULL; - nc->flashed_item = NULL; // also reset this one, so the next time the same object is hovered over it shows again the highlight - } else { - nc->remove_flash_counter--; - } - - gint ret = FALSE; - switch (event->type) { - case GDK_BUTTON_PRESS: - if (event->button.button == 1 && !event_context->space_panning) { - // save drag origin - event_context->xp = (gint) event->button.x; - event_context->yp = (gint) event->button.y; - event_context->within_tolerance = true; - se->cancel_hit(); - - if (!(event->button.state & GDK_SHIFT_MASK)) { - if (!nc->drag) { - if (se->has_nodepath() && selection->single() /* && item_over */) { - // save drag origin - bool over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), true); - //only dragging curves - if (over_stroke) { - ret = TRUE; - break; - } - } - } - } - Geom::Point const button_w(event->button.x, - event->button.y); - Geom::Point const button_dt(desktop->w2d(button_w)); - Inkscape::Rubberband::get(desktop)->start(desktop, button_dt); - - if (nc->grabbed) { - sp_canvas_item_ungrab(nc->grabbed, event->button.time); - nc->grabbed = NULL; - } - - sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), - GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK, - NULL, event->button.time); - nc->grabbed = SP_CANVAS_ITEM(desktop->acetate); - - nc->current_state = SP_NODE_CONTEXT_INACTIVE; - desktop->updateNow(); - ret = TRUE; - } - break; - case GDK_MOTION_NOTIFY: - if (event->motion.state & GDK_BUTTON1_MASK && !event_context->space_panning) { - - if ( event_context->within_tolerance - && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance ) - && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) { - break; // do not drag if we're within tolerance from origin - } - - // The path went away while dragging; throw away any further motion - // events until the mouse pointer is released. - - if (se->hits_curve() && !se->has_nodepath()) { - break; - } - - // Once the user has moved farther than tolerance from the original location - // (indicating they intend to move the object, not click), then always process the - // motion notify coordinates as given (no snapping back to origin) - event_context->within_tolerance = false; - - // Once we determine what the user is doing (dragging either a node or the - // selection rubberband), make sure we continue to perform that operation - // until the mouse pointer is lifted. - if (nc->current_state == SP_NODE_CONTEXT_INACTIVE) { - if (se->hits_curve() && se->has_nodepath()) { - nc->current_state = SP_NODE_CONTEXT_NODE_DRAGGING; - } else { - nc->current_state = SP_NODE_CONTEXT_RUBBERBAND_DRAGGING; - } - } - - switch (nc->current_state) { - case SP_NODE_CONTEXT_NODE_DRAGGING: - { - se->curve_drag (event->motion.x, event->motion.y); - - gobble_motion_events(GDK_BUTTON1_MASK); - break; - } - case SP_NODE_CONTEXT_RUBBERBAND_DRAGGING: - if (Inkscape::Rubberband::get(desktop)->is_started()) { - Geom::Point const motion_w(event->motion.x, - event->motion.y); - Geom::Point const motion_dt(desktop->w2d(motion_w)); - Inkscape::Rubberband::get(desktop)->move(motion_dt); - } - break; - } - - nc->drag = TRUE; - ret = TRUE; - } else { - if (!se->has_nodepath() || selection->singleItem() == NULL) { - break; - } - - bool over_stroke = false; - over_stroke = se->is_over_stroke(Geom::Point(event->motion.x, event->motion.y), false); - - if (nc->cursor_drag && !over_stroke) { - event_context->cursor_shape = cursor_node_xpm; - event_context->hot_x = 1; - event_context->hot_y = 1; - sp_event_context_update_cursor(event_context); - nc->cursor_drag = false; - } else if (!nc->cursor_drag && over_stroke) { - event_context->cursor_shape = cursor_node_d_xpm; - event_context->hot_x = 1; - event_context->hot_y = 1; - sp_event_context_update_cursor(event_context); - nc->cursor_drag = true; - } - } - break; - - case GDK_2BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - if ( (event->button.button == 1) && (!nc->drag) && !event_context->space_panning) { - // find out clicked item, disregarding groups, honoring Alt - SPItem *item_clicked = sp_event_context_find_item (desktop, - Geom::Point(event->button.x, event->button.y), - (event->button.state & GDK_MOD1_MASK) && !(event->button.state & GDK_CONTROL_MASK), TRUE); - - event_context->xp = event_context->yp = 0; - - bool over_stroke = false; - if (se->has_nodepath()) { - over_stroke = se->is_over_stroke(Geom::Point(event->button.x, event->button.y), false); - } - - if (item_clicked || over_stroke) { - if (over_stroke || nc->added_node) { - switch (event->type) { - case GDK_BUTTON_RELEASE: - if (event->button.state & GDK_CONTROL_MASK && event->button.state & GDK_MOD1_MASK) { - //add a node - se->add_node_near_point(); - } else { - if (nc->added_node) { // we just received double click, ignore release - nc->added_node = false; - break; - } - //select the segment - if (event->button.state & GDK_SHIFT_MASK) { - se->select_segment_near_point(true); - } else { - se->select_segment_near_point(false); - } - desktop->updateNow(); - } - break; - case GDK_2BUTTON_PRESS: - //add a node - se->add_node_near_point(); - nc->added_node = true; - break; - default: - break; - } - } else if (event->button.state & GDK_SHIFT_MASK) { - selection->toggle(item_clicked); - desktop->updateNow(); - } else { - selection->set(item_clicked); - desktop->updateNow(); - } - Inkscape::Rubberband::get(desktop)->stop(); - if (nc->grabbed) { - sp_canvas_item_ungrab(nc->grabbed, event->button.time); - nc->grabbed = NULL; - } - ret = TRUE; - break; - } - } - if (event->type == GDK_BUTTON_RELEASE) { - event_context->xp = event_context->yp = 0; - if (event->button.button == 1) { - Geom::OptRect b = Inkscape::Rubberband::get(desktop)->getRectangle(); - - if (se->hits_curve() && !event_context->within_tolerance) { //drag curve - se->finish_drag(); - } else if (b && !event_context->within_tolerance) { // drag to select - se->select_rect(*b, event->button.state & GDK_SHIFT_MASK); - } else { - if (!(nc->rb_escaped)) { // unless something was canceled - if (se->has_selection()) - se->deselect(); - else - sp_desktop_selection(desktop)->clear(); - } - } - ret = TRUE; - Inkscape::Rubberband::get(desktop)->stop(); - - if (nc->grabbed) { - sp_canvas_item_ungrab(nc->grabbed, event->button.time); - nc->grabbed = NULL; - } - - desktop->updateNow(); - nc->rb_escaped = false; - nc->drag = FALSE; - se->cancel_hit(); - nc->current_state = SP_NODE_CONTEXT_INACTIVE; - } - } - break; - case GDK_KEY_PRESS: - switch (get_group0_keyval(&event->key)) { - case GDK_Insert: - case GDK_KP_Insert: - // with any modifiers - se->add_node(); - ret = TRUE; - break; - case GDK_I: - case GDK_i: - // apple keyboards have no Insert - if (MOD__SHIFT_ONLY) { - se->add_node(); - ret = TRUE; - } - break; - case GDK_Delete: - case GDK_KP_Delete: - case GDK_BackSpace: - if (MOD__CTRL_ONLY) { - se->delete_nodes(); - } else { - se->delete_nodes_preserving_shape(); - } - ret = TRUE; - break; - case GDK_C: - case GDK_c: - if (MOD__SHIFT_ONLY) { - se->set_node_type(Inkscape::NodePath::NODE_CUSP); - ret = TRUE; - } - break; - case GDK_S: - case GDK_s: - if (MOD__SHIFT_ONLY) { - se->set_node_type(Inkscape::NodePath::NODE_SMOOTH); - ret = TRUE; - } - break; - case GDK_A: - case GDK_a: - if (MOD__SHIFT_ONLY) { - se->set_node_type(Inkscape::NodePath::NODE_AUTO); - ret = TRUE; - } - break; - case GDK_Y: - case GDK_y: - if (MOD__SHIFT_ONLY) { - se->set_node_type(Inkscape::NodePath::NODE_SYMM); - ret = TRUE; - } - break; - case GDK_B: - case GDK_b: - if (MOD__SHIFT_ONLY) { - se->break_at_nodes(); - ret = TRUE; - } - break; - case GDK_J: - case GDK_j: - if (MOD__SHIFT_ONLY) { - se->join_nodes(); - ret = TRUE; - } - break; - case GDK_D: - case GDK_d: - if (MOD__SHIFT_ONLY) { - se->duplicate_nodes(); - ret = TRUE; - } - break; - case GDK_L: - case GDK_l: - if (MOD__SHIFT_ONLY) { - se->set_type_of_segments(NR_LINETO); - ret = TRUE; - } - break; - case GDK_U: - case GDK_u: - if (MOD__SHIFT_ONLY) { - se->set_type_of_segments(NR_CURVETO); - ret = TRUE; - } - break; - case GDK_R: - case GDK_r: - if (MOD__SHIFT_ONLY) { - // FIXME: add top panel button - sp_selected_path_reverse(desktop); - ret = TRUE; - } - break; - case GDK_x: - case GDK_X: - if (MOD__ALT_ONLY) { - desktop->setToolboxFocusTo ("altx-nodes"); - ret = TRUE; - } - break; - case GDK_Left: // move selection left - case GDK_KP_Left: - case GDK_KP_4: - if (!MOD__CTRL) { // not ctrl - gint mul = 1 + gobble_key_events( - get_group0_keyval(&event->key), 0); // with any mask - if (MOD__ALT) { // alt - if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*-10, 0); // shift - else se->move_nodes_screen(desktop, mul*-1, 0); // no shift - } - else { // no alt - if (MOD__SHIFT) se->move_nodes(mul*-10*nudge, 0); // shift - else se->move_nodes(mul*-nudge, 0); // no shift - } - ret = TRUE; - } - break; - case GDK_Up: // move selection up - case GDK_KP_Up: - case GDK_KP_8: - if (!MOD__CTRL) { // not ctrl - gint mul = 1 + gobble_key_events( - get_group0_keyval(&event->key), 0); // with any mask - if (MOD__ALT) { // alt - if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*10); // shift - else se->move_nodes_screen(desktop, 0, mul*1); // no shift - } - else { // no alt - if (MOD__SHIFT) se->move_nodes(0, mul*10*nudge); // shift - else se->move_nodes(0, mul*nudge); // no shift - } - ret = TRUE; - } - break; - case GDK_Right: // move selection right - case GDK_KP_Right: - case GDK_KP_6: - if (!MOD__CTRL) { // not ctrl - gint mul = 1 + gobble_key_events( - get_group0_keyval(&event->key), 0); // with any mask - if (MOD__ALT) { // alt - if (MOD__SHIFT) se->move_nodes_screen(desktop, mul*10, 0); // shift - else se->move_nodes_screen(desktop, mul*1, 0); // no shift - } - else { // no alt - if (MOD__SHIFT) se->move_nodes(mul*10*nudge, 0); // shift - else se->move_nodes(mul*nudge, 0); // no shift - } - ret = TRUE; - } - break; - case GDK_Down: // move selection down - case GDK_KP_Down: - case GDK_KP_2: - if (!MOD__CTRL) { // not ctrl - gint mul = 1 + gobble_key_events( - get_group0_keyval(&event->key), 0); // with any mask - if (MOD__ALT) { // alt - if (MOD__SHIFT) se->move_nodes_screen(desktop, 0, mul*-10); // shift - else se->move_nodes_screen(desktop, 0, mul*-1); // no shift - } - else { // no alt - if (MOD__SHIFT) se->move_nodes(0, mul*-10*nudge); // shift - else se->move_nodes(0, mul*-nudge); // no shift - } - ret = TRUE; - } - break; - case GDK_Escape: - { - Geom::OptRect const b = Inkscape::Rubberband::get(desktop)->getRectangle(); - if (b) { - Inkscape::Rubberband::get(desktop)->stop(); - nc->current_state = SP_NODE_CONTEXT_INACTIVE; - nc->rb_escaped = true; - } else { - if (se->has_selection()) { - se->deselect(); - } else { - sp_desktop_selection(desktop)->clear(); - } - } - ret = TRUE; - break; - } - - case GDK_bracketleft: - if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) { - if (nc->leftctrl) - se->rotate_nodes (M_PI/snaps, -1, false); - if (nc->rightctrl) - se->rotate_nodes (M_PI/snaps, 1, false); - } else if ( MOD__ALT && !MOD__CTRL ) { - if (nc->leftalt && nc->rightalt) - se->rotate_nodes (1, 0, true); - else { - if (nc->leftalt) - se->rotate_nodes (1, -1, true); - if (nc->rightalt) - se->rotate_nodes (1, 1, true); - } - } else if ( snaps != 0 ) { - se->rotate_nodes (M_PI/snaps, 0, false); - } - ret = TRUE; - break; - case GDK_bracketright: - if ( MOD__CTRL && !MOD__ALT && ( snaps != 0 ) ) { - if (nc->leftctrl) - se->rotate_nodes (-M_PI/snaps, -1, false); - if (nc->rightctrl) - se->rotate_nodes (-M_PI/snaps, 1, false); - } else if ( MOD__ALT && !MOD__CTRL ) { - if (nc->leftalt && nc->rightalt) - se->rotate_nodes (-1, 0, true); - else { - if (nc->leftalt) - se->rotate_nodes (-1, -1, true); - if (nc->rightalt) - se->rotate_nodes (-1, 1, true); - } - } else if ( snaps != 0 ) { - se->rotate_nodes (-M_PI/snaps, 0, false); - } - ret = TRUE; - break; - case GDK_less: - case GDK_comma: - if (MOD__CTRL) { - if (nc->leftctrl) - se->scale_nodes(-offset, -1); - if (nc->rightctrl) - se->scale_nodes(-offset, 1); - } else if (MOD__ALT) { - if (nc->leftalt && nc->rightalt) - se->scale_nodes_screen (-1, 0); - else { - if (nc->leftalt) - se->scale_nodes_screen (-1, -1); - if (nc->rightalt) - se->scale_nodes_screen (-1, 1); - } - } else { - se->scale_nodes (-offset, 0); - } - ret = TRUE; - break; - case GDK_greater: - case GDK_period: - if (MOD__CTRL) { - if (nc->leftctrl) - se->scale_nodes (offset, -1); - if (nc->rightctrl) - se->scale_nodes (offset, 1); - } else if (MOD__ALT) { - if (nc->leftalt && nc->rightalt) - se->scale_nodes_screen (1, 0); - else { - if (nc->leftalt) - se->scale_nodes_screen (1, -1); - if (nc->rightalt) - se->scale_nodes_screen (1, 1); - } - } else { - se->scale_nodes (offset, 0); - } - ret = TRUE; - break; - - case GDK_Alt_L: - nc->leftalt = TRUE; - sp_node_context_show_modifier_tip(event_context, event); - break; - case GDK_Alt_R: - nc->rightalt = TRUE; - sp_node_context_show_modifier_tip(event_context, event); - break; - case GDK_Control_L: - nc->leftctrl = TRUE; - sp_node_context_show_modifier_tip(event_context, event); - break; - case GDK_Control_R: - nc->rightctrl = TRUE; - sp_node_context_show_modifier_tip(event_context, event); - break; - case GDK_Shift_L: - case GDK_Shift_R: - case GDK_Meta_L: - case GDK_Meta_R: - sp_node_context_show_modifier_tip(event_context, event); - break; - default: - ret = node_key(event); - break; - } - break; - case GDK_KEY_RELEASE: - switch (get_group0_keyval(&event->key)) { - case GDK_Alt_L: - nc->leftalt = FALSE; - event_context->defaultMessageContext()->clear(); - break; - case GDK_Alt_R: - nc->rightalt = FALSE; - event_context->defaultMessageContext()->clear(); - break; - case GDK_Control_L: - nc->leftctrl = FALSE; - event_context->defaultMessageContext()->clear(); - break; - case GDK_Control_R: - nc->rightctrl = FALSE; - event_context->defaultMessageContext()->clear(); - break; - case GDK_Shift_L: - case GDK_Shift_R: - case GDK_Meta_L: - case GDK_Meta_R: - event_context->defaultMessageContext()->clear(); - break; - } - break; - default: - break; - } - - if (!ret) { - if (((SPEventContextClass *) parent_class)->root_handler) - ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); - } - - return ret; -} - - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/node-context.h b/src/node-context.h deleted file mode 100644 index 2345ffc7e..000000000 --- a/src/node-context.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __SP_NODE_CONTEXT_H__ -#define __SP_NODE_CONTEXT_H__ - -/* - * Node editing context - * - * Authors: - * Lauris Kaplinski <lauris@kaplinski.com> - * bulia byak <buliabyak@users.sf.net> - * - * This code is in public domain - */ - -#include <gtk/gtktypeutils.h> -#include <sigc++/sigc++.h> -#include "event-context.h" -#include "forward.h" -#include "display/display-forward.h" -#include "nodepath.h" -namespace Inkscape { class Selection; } - -#define SP_TYPE_NODE_CONTEXT (sp_node_context_get_type ()) -#define SP_NODE_CONTEXT(obj) (GTK_CHECK_CAST ((obj), SP_TYPE_NODE_CONTEXT, SPNodeContext)) -#define SP_NODE_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), SP_TYPE_NODE_CONTEXT, SPNodeContextClass)) -#define SP_IS_NODE_CONTEXT(obj) (GTK_CHECK_TYPE ((obj), SP_TYPE_NODE_CONTEXT)) -#define SP_IS_NODE_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), SP_TYPE_NODE_CONTEXT)) - -enum { SP_NODE_CONTEXT_INACTIVE, - SP_NODE_CONTEXT_NODE_DRAGGING, - SP_NODE_CONTEXT_RUBBERBAND_DRAGGING }; - -class SPNodeContext; -class SPNodeContextClass; - -struct SPNodeContext { - // FIXME: shouldn't this be a pointer??? - SPEventContext event_context; - - guint drag : 1; - - gboolean leftalt; - gboolean rightalt; - gboolean leftctrl; - gboolean rightctrl; - - /// If true, rubberband was cancelled by esc, so the next button release should not deselect. - bool rb_escaped; - - sigc::connection sel_changed_connection; - - Inkscape::MessageContext *_node_message_context; - - bool cursor_drag; - - bool added_node; - - unsigned int current_state; - - SPItem * flashed_item; - SPCanvasItem *grabbed; - Inkscape::Display::TemporaryItem * flash_tempitem; - int remove_flash_counter; -}; - -struct SPNodeContextClass { - SPEventContextClass parent_class; -}; - -/* Standard Gtk function */ - -GtkType sp_node_context_get_type (void); - -void sp_node_context_selection_changed (Inkscape::Selection * selection, gpointer data); -void sp_node_context_selection_modified (Inkscape::Selection * selection, guint flags, gpointer data); - -#endif - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/nodepath.cpp b/src/nodepath.cpp deleted file mode 100644 index 1881dd72b..000000000 --- a/src/nodepath.cpp +++ /dev/null @@ -1,5146 +0,0 @@ -#define __SP_NODEPATH_C__ - -/** \file - * Path handler in node edit mode - * - * Authors: - * Lauris Kaplinski <lauris@kaplinski.com> - * bulia byak <buliabyak@users.sf.net> - * - * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include <gdk/gdkkeysyms.h> -#include "display/canvas-bpath.h" -#include "display/curve.h" -#include "display/sp-ctrlline.h" -#include "display/sodipodi-ctrl.h" -#include "display/sp-canvas-util.h" -#include <glibmm/i18n.h> -#include "2geom/pathvector.h" -#include "2geom/sbasis-to-bezier.h" -#include "2geom/bezier-curve.h" -#include "2geom/hvlinesegment.h" -#include "helper/units.h" -#include "helper/geom.h" -#include "knot.h" -#include "inkscape.h" -#include "document.h" -#include "sp-namedview.h" -#include "desktop.h" -#include "desktop-handles.h" -#include "snap.h" -#include "message-stack.h" -#include "message-context.h" -#include "node-context.h" -#include "lpe-tool-context.h" -#include "shape-editor.h" -#include "selection-chemistry.h" -#include "selection.h" -#include "xml/repr.h" -#include "preferences.h" -#include "sp-metrics.h" -#include "sp-path.h" -#include "sp-text.h" -#include "sp-shape.h" -#include "libnr/nr-matrix-ops.h" -#include "svg/svg.h" -#include "verbs.h" -#include <2geom/bezier-utils.h> -#include <vector> -#include <algorithm> -#include <cstring> -#include <cmath> -#include "live_effects/lpeobject.h" -#include "live_effects/lpeobject-reference.h" -#include "live_effects/effect.h" -#include "live_effects/parameter/parameter.h" -#include "live_effects/parameter/path.h" -#include "util/mathfns.h" -#include "display/snap-indicator.h" -#include "snapped-point.h" - -namespace Geom { class Matrix; } - -/// \todo -/// evil evil evil. FIXME: conflict of two different Path classes! -/// There is a conflict in the namespace between two classes named Path. -/// #include "sp-flowtext.h" -/// #include "sp-flowregion.h" - -#define SP_TYPE_FLOWREGION (sp_flowregion_get_type ()) -#define SP_IS_FLOWREGION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION)) -GType sp_flowregion_get_type (void); -#define SP_TYPE_FLOWTEXT (sp_flowtext_get_type ()) -#define SP_IS_FLOWTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT)) -GType sp_flowtext_get_type (void); -// end evil workaround - -#include "helper/stlport.h" - - -/// \todo fixme: Implement these via preferences */ - -#define NODE_FILL 0xbfbfbf00 -#define NODE_STROKE 0x000000ff -#define NODE_FILL_HI 0xff000000 -#define NODE_STROKE_HI 0x000000ff -#define NODE_FILL_SEL 0x0000ffff -#define NODE_STROKE_SEL 0x000000ff -#define NODE_FILL_SEL_HI 0xff000000 -#define NODE_STROKE_SEL_HI 0x000000ff -#define KNOT_FILL 0xffffffff -#define KNOT_STROKE 0x000000ff -#define KNOT_FILL_HI 0xff000000 -#define KNOT_STROKE_HI 0x000000ff - -static GMemChunk *nodechunk = NULL; - -/* Creation from object */ - -static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t); -static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length); - -/* Object updating */ - -static void stamp_repr(Inkscape::NodePath::Path *np); -static SPCurve *create_curve(Inkscape::NodePath::Path *np); -static gchar *create_typestr(Inkscape::NodePath::Path *np); - -static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true); - -static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override); - -static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected); - -static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type); - -/* Adjust handle placement, if the node or the other handle is moved */ -static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust); -static void sp_node_adjust_handles(Inkscape::NodePath::Node *node); -static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node); - -/* Node event callbacks */ -static void node_clicked(SPKnot *knot, guint state, gpointer data); -static void node_grabbed(SPKnot *knot, guint state, gpointer data); -static void node_ungrabbed(SPKnot *knot, guint state, gpointer data); -static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data); - -/* Handle event callbacks */ -static void node_handle_clicked(SPKnot *knot, guint state, gpointer data); -static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data); -static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data); -static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data); -static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data); -static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n); - -/* Constructors and destructors */ - -static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath); -static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath); -static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp); -static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n); -static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code, - Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos); -static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node); - -/* Helpers */ - -static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which); -static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me); -static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me); - -static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key); -static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve); - -// active_node indicates mouseover node -Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL; - -static SPCanvasItem * -sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false, guint32 color = 0xff0000ff) { - SPCurve *helper_curve = curve->copy(); - helper_curve->transform(np->i2d); - SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO); - sp_canvas_item_move_to_z(helper_path, 0); - if (show) { - sp_canvas_item_show(helper_path); - } - helper_curve->unref(); - return helper_path; -} - -static void -sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) { - //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec; - if (!SP_IS_LPE_ITEM(np->item)) { - g_print ("Only LPEItems can have helperpaths!\n"); - return; - } - - SPLPEItem *lpeitem = SP_LPE_ITEM(np->item); - PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem); - for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) { - Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i); - Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe(); - if (lpe) { - // create new canvas items from the effect's helper paths - std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem); - for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) { - SPCurve *helper_curve = new SPCurve(*j); - SPCanvasItem * canvasitem = sp_nodepath_make_helper_item(np, helper_curve, true, 0x509050dd); - np->helper_path_vec[lpe].push_back(canvasitem); - helper_curve->unref(); - } - } - } -} - -static void -sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) { - for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) { - for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { - GtkObject *temp = *j; - *j = NULL; - gtk_object_destroy(temp); - } - } - np->helper_path_vec.clear(); -} - -/** updates canvas items from the effect's helper paths */ -void -sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) { - //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec; - if (!SP_IS_LPE_ITEM(np->item)) { - g_print ("Only LPEItems can have helperpaths!\n"); - return; - } - - SPLPEItem *lpeitem = SP_LPE_ITEM(np->item); - PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem); - - /* The number or type or LPEs may have changed, so we need to clear and recreate our - * helper_path_vec to make sure it is in sync */ - sp_nodepath_destroy_helperpaths(np); - sp_nodepath_create_helperpaths(np); - - for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) { - Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe(); - if (lpe) { - std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem); - for (unsigned int j = 0; j < hpaths.size(); ++j) { - SPCurve *curve = new SPCurve(hpaths[j]); - curve->transform(np->i2d); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve); - curve = curve->unref(); - } - } - } -} - -/** - * \brief Creates new nodepath from item - * - * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject ! - * - * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor. - */ -Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item) -{ - if (repr_key_in) { - g_assert(IS_LIVEPATHEFFECT(object)); - } - - Inkscape::XML::Node *repr = object->repr; - - /** \todo - * FIXME: remove this. We don't want to edit paths inside flowtext. - * Instead we will build our flowtext with cloned paths, so that the - * real paths are outside the flowtext and thus editable as usual. - */ - if (SP_IS_FLOWTEXT(object)) { - for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { - if SP_IS_FLOWREGION(child) { - SPObject *grandchild = sp_object_first_child(SP_OBJECT(child)); - if (grandchild && SP_IS_PATH(grandchild)) { - object = SP_ITEM(grandchild); - break; - } - } - } - } - - SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in); - - if (curve == NULL) { - return NULL; - } - - if (curve->get_segment_count() < 1) { - curve->unref(); - return NULL; // prevent crash for one-node paths - } - - //Create new nodepath - Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path(); - if (!np) { - curve->unref(); - return NULL; - } - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - // Set defaults - np->desktop = desktop; - np->object = object; - np->subpaths = NULL; - np->selected = NULL; - np->shape_editor = NULL; //Let the shapeeditor that makes this set it - np->local_change = 0; - np->show_handles = show_handles; - np->helper_path = NULL; - np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff); - np->helperpath_width = 1.0; - np->curve = curve->copy(); - np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath"); - if (SP_IS_LPE_ITEM(object)) { - Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object)); - if (lpe && lpe->isVisible() && lpe->showOrigPath()) { - np->show_helperpath = true; - } - } - np->straight_path = false; - if (IS_LIVEPATHEFFECT(object) && item) { - np->item = item; - } else { - np->item = SP_ITEM(object); - } - - np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE); - - // we need to update item's transform from the repr here, - // because they may be out of sync when we respond - // to a change in repr by regenerating nodepath --bb - sp_object_read_attr(SP_OBJECT(np->item), "transform"); - - np->i2d = sp_item_i2d_affine(np->item); - np->d2i = np->i2d.inverse(); - - np->repr = repr; - if (repr_key_in) { // apparently the object is an LPEObject - np->repr_key = g_strdup(repr_key_in); - np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL); - Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe(); - if (!lpe) { - g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!"); - delete np; - } - Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in); - if (lpeparam) { - lpeparam->param_setup_nodepath(np); - } - } else { - np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes"); - if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) { - np->repr_key = g_strdup("inkscape:original-d"); - - Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object)); - if (lpe) { - lpe->setup_nodepath(np); - } - } else { - np->repr_key = g_strdup("d"); - } - } - - /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice. - * So for example a closed rectangle has a nodetypestring of length 5. - * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */ - Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector()); - np->curve->set_pathvector(pathv_sanitized); - guint length = np->curve->get_segment_count(); - for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) { - length += pit->empty() ? 0 : 1; - } - - gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key); - Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length); - - // create the subpath(s) from the bpath - subpaths_from_pathvector(np, pathv_sanitized, typestr); - - // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed) - np->subpaths = g_list_reverse(np->subpaths); - - delete[] typestr; - curve->unref(); - - // Draw helper curve - if (np->show_helperpath) { - np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba); - } - - sp_nodepath_create_helperpaths(np); - - return np; -} - -/** - * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it. - */ -Inkscape::NodePath::Path::~Path() { - while (this->subpaths) { - sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data); - } - - //Inform the ShapeEditor that made me, if any, that I am gone. - if (this->shape_editor) - this->shape_editor->nodepath_destroyed(); - - g_assert(!this->selected); - - if (this->helper_path) { - GtkObject *temp = this->helper_path; - this->helper_path = NULL; - gtk_object_destroy(temp); - } - if (this->curve) { - this->curve->unref(); - this->curve = NULL; - } - - if (this->repr_key) { - g_free(this->repr_key); - this->repr_key = NULL; - } - if (this->repr_nodetypes_key) { - g_free(this->repr_nodetypes_key); - this->repr_nodetypes_key = NULL; - } - - sp_nodepath_destroy_helperpaths(this); - - this->desktop = NULL; -} - -/** - * Return the node count of a given NodeSubPath. - */ -static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath) -{ - int nodeCount = 0; - - if (subpath) { - nodeCount = g_list_length(subpath->nodes); - } - - return nodeCount; -} - -/** - * Return the node count of a given NodePath. - */ -static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np) -{ - gint nodeCount = 0; - if (np) { - for (GList *item = np->subpaths ; item ; item=item->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data; - nodeCount += g_list_length(subpath->nodes); - } - } - return nodeCount; -} - -/** - * Return the subpath count of a given NodePath. - */ -static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np) -{ - gint nodeCount = 0; - if (np) { - nodeCount = g_list_length(np->subpaths); - } - return nodeCount; -} - -/** - * Return the selected node count of a given NodePath. - */ -static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np) -{ - gint nodeCount = 0; - if (np) { - nodeCount = g_list_length(np->selected); - } - return nodeCount; -} - -/** - * Return the number of subpaths where nodes are selected in a given NodePath. - */ -static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np) -{ - gint nodeCount = 0; - if (np && np->selected) { - if (!np->selected->next) { - nodeCount = 1; - } else { - for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data); - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data); - if (node->selected) { - nodeCount++; - break; - } - } - } - } - } - return nodeCount; -} - -/** - * Clean up a nodepath after editing. - * - * Currently we are deleting trivial subpaths. - */ -static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath) -{ - GList *badSubPaths = NULL; - - //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes - for (GList *l = nodepath->subpaths; l ; l=l->next) { - Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data; - if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed)) - badSubPaths = g_list_append(badSubPaths, sp); - } - - //Delete them. This second step is because sp_nodepath_subpath_destroy() - //also removes the subpath from nodepath->subpaths - for (GList *l = badSubPaths; l ; l=l->next) { - Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data; - sp_nodepath_subpath_destroy(sp); - } - - g_list_free(badSubPaths); -} - -/** - * Create new nodepaths from pathvector, make it subpaths of np. - * \param t The node type array. - */ -static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t) -{ - guint i = 0; // index into node type array - for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { - if (pit->empty()) - continue; // don't add single knot paths - - Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np); - - Geom::Point ppos = pit->initialPoint() * np->i2d; - NRPathcode pcode = NR_MOVETO; - - /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/ - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) { - if( dynamic_cast<Geom::LineSegment const*>(&*cit) || - dynamic_cast<Geom::HLineSegment const*>(&*cit) || - dynamic_cast<Geom::VLineSegment const*>(&*cit) ) - { - Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d; - sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos); - - ppos = cit->finalPoint() * (Geom::Matrix)np->i2d; - pcode = NR_LINETO; - } - else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) { - std::vector<Geom::Point> points = cubic_bezier->points(); - Geom::Point pos = points[0] * (Geom::Matrix)np->i2d; - Geom::Point npos = points[1] * (Geom::Matrix)np->i2d; - sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos); - - ppos = points[2] * (Geom::Matrix)np->i2d; - pcode = NR_CURVETO; - } - } - - if (pit->closed()) { - // Add last knot (because sp_nodepath_subpath_close kills the last knot) - /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already - * If the length is zero, don't add it to the nodepath. */ - Geom::Curve const &closing_seg = pit->back_closed(); - // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289) - if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) { - Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d; - sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos); - } - - sp_nodepath_subpath_close(sp); - } - } -} - -/** - * Convert from sodipodi:nodetypes to new style type array. - */ -static -Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length) -{ - Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1]; - - guint pos = 0; - - if (types) { - for (guint i = 0; types[i] && ( i < length ); i++) { - while ((types[i] > '\0') && (types[i] <= ' ')) i++; - if (types[i] != '\0') { - switch (types[i]) { - case 's': - typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH; - break; - case 'a': - typestr[pos++] =Inkscape::NodePath::NODE_AUTO; - break; - case 'z': - typestr[pos++] =Inkscape::NodePath::NODE_SYMM; - break; - case 'c': - typestr[pos++] =Inkscape::NodePath::NODE_CUSP; - break; - default: - typestr[pos++] =Inkscape::NodePath::NODE_NONE; - break; - } - } - } - } - - while (pos < length) { - typestr[pos++] = Inkscape::NodePath::NODE_NONE; - } - - return typestr; -} - -/** - * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is - * updated but repr is not (for speed). Used during curve and node drag. - */ -static void update_object(Inkscape::NodePath::Path *np) -{ - g_assert(np); - - np->curve->unref(); - np->curve = create_curve(np); - - sp_nodepath_set_curve(np, np->curve); - - if (np->show_helperpath) { - SPCurve * helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve); - helper_curve->unref(); - } - - // updating helperpaths of LPEItems is now done in sp_lpe_item_update(); - //sp_nodepath_update_helperpaths(np); - - // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too - // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder! - np->shape_editor->update_knotholder(); -} - -/** - * Update XML path node with data from path object. - */ -static void update_repr_internal(Inkscape::NodePath::Path *np) -{ - g_assert(np); - - Inkscape::XML::Node *repr = np->object->repr; - - np->curve->unref(); - np->curve = create_curve(np); - - gchar *typestr = create_typestr(np); - gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector()); - - // determine if path has an effect applied and write to correct "d" attribute. - if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed - np->local_change++; - repr->setAttribute(np->repr_key, svgpath); - } - - if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed - np->local_change++; - repr->setAttribute(np->repr_nodetypes_key, typestr); - } - - g_free(svgpath); - g_free(typestr); - - if (np->show_helperpath) { - SPCurve * helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve); - helper_curve->unref(); - } - - // TODO: do we need this call here? after all, update_object() should have been called just before - //sp_nodepath_update_helperpaths(np); -} - -/** - * Update XML path node with data from path object, commit changes forever. - */ -void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation) -{ - //fixme: np can be NULL, so check before proceeding - g_return_if_fail(np != NULL); - - update_repr_internal(np); - sp_canvas_end_forced_full_redraws(np->desktop->canvas); - - sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, - annotation); -} - -/** - * Update XML path node with data from path object, commit changes with undo. - */ -static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation) -{ - update_repr_internal(np); - sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, - annotation); -} - -/** - * Make duplicate of path, replace corresponding XML node in tree, commit. - */ -static void stamp_repr(Inkscape::NodePath::Path *np) -{ - g_assert(np); - - Inkscape::XML::Node *old_repr = np->object->repr; - Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document()); - - // remember the position of the item - gint pos = old_repr->position(); - // remember parent - Inkscape::XML::Node *parent = sp_repr_parent(old_repr); - - SPCurve *curve = create_curve(np); - gchar *typestr = create_typestr(np); - - gchar *svgpath = sp_svg_write_path(curve->get_pathvector()); - - new_repr->setAttribute(np->repr_key, svgpath); - new_repr->setAttribute(np->repr_nodetypes_key, typestr); - - // add the new repr to the parent - parent->appendChild(new_repr); - // move to the saved position - new_repr->setPosition(pos > 0 ? pos : 0); - - sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, - _("Stamp")); - - Inkscape::GC::release(new_repr); - g_free(svgpath); - g_free(typestr); - curve->unref(); -} - -/** - * Create curve from path. - */ -static SPCurve *create_curve(Inkscape::NodePath::Path *np) -{ - SPCurve *curve = new SPCurve(); - - for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data; - curve->moveto(sp->first->pos * np->d2i); - Inkscape::NodePath::Node *n = sp->first->n.other; - while (n) { - Geom::Point const end_pt = n->pos * np->d2i; - if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){ - g_message("niet finite"); - } - switch (n->code) { - case NR_LINETO: - curve->lineto(end_pt); - break; - case NR_CURVETO: - curve->curveto(n->p.other->n.pos * np->d2i, - n->p.pos * np->d2i, - end_pt); - break; - default: - g_assert_not_reached(); - break; - } - if (n != sp->last) { - n = n->n.other; - } else { - n = NULL; - } - } - if (sp->closed) { - curve->closepath(); - } - } - - return curve; -} - -/** - * Convert path type string to sodipodi:nodetypes style. - */ -static gchar *create_typestr(Inkscape::NodePath::Path *np) -{ - gchar *typestr = g_new(gchar, 32); - gint len = 32; - gint pos = 0; - - for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data; - - if (pos >= len) { - typestr = g_renew(gchar, typestr, len + 32); - len += 32; - } - - typestr[pos++] = 'c'; - - Inkscape::NodePath::Node *n; - n = sp->first->n.other; - while (n) { - gchar code; - - switch (n->type) { - case Inkscape::NodePath::NODE_CUSP: - code = 'c'; - break; - case Inkscape::NodePath::NODE_SMOOTH: - code = 's'; - break; - case Inkscape::NodePath::NODE_AUTO: - code = 'a'; - break; - case Inkscape::NodePath::NODE_SYMM: - code = 'z'; - break; - default: - g_assert_not_reached(); - code = '\0'; - break; - } - - if (pos >= len) { - typestr = g_renew(gchar, typestr, len + 32); - len += 32; - } - - typestr[pos++] = code; - - if (n != sp->last) { - n = n->n.other; - } else { - n = NULL; - } - } - } - - if (pos >= len) { - typestr = g_renew(gchar, typestr, len + 1); - len += 1; - } - - typestr[pos++] = '\0'; - - return typestr; -} - -// Returns different message contexts depending on the current context. This function should only -// be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all -// other cases. -static Inkscape::MessageContext * -get_message_context(SPEventContext *ec) -{ - Inkscape::MessageContext *mc = 0; - - if (SP_IS_NODE_CONTEXT(ec)) { - mc = SP_NODE_CONTEXT(ec)->_node_message_context; - } else if (SP_IS_LPETOOL_CONTEXT(ec)) { - mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context; - } else { - g_warning ("Nodepath should only be present in Node tool or Geometric tool."); - } - - return mc; -} - -/** - \brief Fills node and handle positions for three nodes, splitting line - marked by end at distance t. - */ -static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t) -{ - g_assert(new_path != NULL); - g_assert(end != NULL); - - g_assert(end->p.other == new_path); - Inkscape::NodePath::Node *start = new_path->p.other; - g_assert(start); - - if (end->code == NR_LINETO) { - new_path->type =Inkscape::NodePath::NODE_CUSP; - new_path->code = NR_LINETO; - new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos); - } else { - new_path->type =Inkscape::NodePath::NODE_SMOOTH; - new_path->code = NR_CURVETO; - gdouble s = 1 - t; - for (int dim = 0; dim < 2; dim++) { - Geom::Coord const f000 = start->pos[dim]; - Geom::Coord const f001 = start->n.pos[dim]; - Geom::Coord const f011 = end->p.pos[dim]; - Geom::Coord const f111 = end->pos[dim]; - Geom::Coord const f00t = s * f000 + t * f001; - Geom::Coord const f01t = s * f001 + t * f011; - Geom::Coord const f11t = s * f011 + t * f111; - Geom::Coord const f0tt = s * f00t + t * f01t; - Geom::Coord const f1tt = s * f01t + t * f11t; - Geom::Coord const fttt = s * f0tt + t * f1tt; - start->n.pos[dim] = f00t; - new_path->p.pos[dim] = f0tt; - new_path->pos[dim] = fttt; - new_path->n.pos[dim] = f1tt; - end->p.pos[dim] = f11t; - } - } -} - -/** - * Adds new node on direct line between two nodes, activates handles of all - * three nodes. - */ -static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t) -{ - g_assert(end); - g_assert(end->subpath); - g_assert(g_list_find(end->subpath->nodes, end)); - - Inkscape::NodePath::Node *start = end->p.other; - g_assert( start->n.other == end ); - Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath, - end, - (NRPathcode)end->code == NR_LINETO? - Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH, - (NRPathcode)end->code, - &start->pos, &start->pos, &start->n.pos); - sp_nodepath_line_midpoint(newnode, end, t); - - sp_node_adjust_handles(start); - sp_node_update_handles(start); - sp_node_update_handles(newnode); - sp_node_adjust_handles(end); - sp_node_update_handles(end); - - return newnode; -} - -/** -\brief Break the path at the node: duplicate the argument node, start a new subpath with the duplicate, and copy all nodes after the argument node to it -*/ -static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node) -{ - g_assert(node); - g_assert(node->subpath); - g_assert(g_list_find(node->subpath->nodes, node)); - - Inkscape::NodePath::Node* result = 0; - Inkscape::NodePath::SubPath *sp = node->subpath; - Inkscape::NodePath::Path *np = sp->nodepath; - - if (sp->closed) { - sp_nodepath_subpath_open(sp, node); - result = sp->first; - } else if ( (node == sp->first) || (node == sp->last ) ){ - // no break for end nodes - result = 0; - } else { - // create a new subpath - Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np); - - // duplicate the break node as start of the new subpath - Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, - static_cast<Inkscape::NodePath::NodeType>(node->type), - NR_MOVETO, &node->pos, &node->pos, &node->n.pos); - - // attach rest of curve to new node - g_assert(node->n.other); - newnode->n.other = node->n.other; node->n.other = NULL; - newnode->n.other->p.other = newnode; - newsubpath->last = sp->last; - sp->last = node; - node = newnode; - while (node->n.other) { - node = node->n.other; - node->subpath = newsubpath; - sp->nodes = g_list_remove(sp->nodes, node); - newsubpath->nodes = g_list_prepend(newsubpath->nodes, node); - } - - - result = newnode; - } - return result; -} - -/** - * Duplicate node and connect to neighbours. - */ -static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node) -{ - g_assert(node); - g_assert(node->subpath); - g_assert(g_list_find(node->subpath->nodes, node)); - - Inkscape::NodePath::SubPath *sp = node->subpath; - - NRPathcode code = (NRPathcode) node->code; - if (code == NR_MOVETO) { // if node is the endnode, - node->code = NR_LINETO; // new one is inserted before it, so change that to line - } - - Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos); - - if (!node->n.other || !node->p.other) { // if node is an endnode, select it - return node; - } else { - return newnode; // otherwise select the newly created node - } -} - -static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node) -{ - node->p.pos = (node->pos + (node->pos - node->n.pos)); -} - -static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node) -{ - node->n.pos = (node->pos + (node->pos - node->p.pos)); -} - -/** - * Change line type at node, with side effects on neighbours. - */ -static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code) -{ - g_assert(end); - g_assert(end->subpath); - g_assert(end->p.other); - - if (end->code != static_cast<guint>(code) ) { - Inkscape::NodePath::Node *start = end->p.other; - - end->code = code; - - if (code == NR_LINETO) { - if (start->code == NR_LINETO) { - sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP); - } - if (end->n.other) { - if (end->n.other->code == NR_LINETO) { - sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP); - } - } - - if (start->type == Inkscape::NodePath::NODE_AUTO) - start->type = Inkscape::NodePath::NODE_SMOOTH; - if (end->type == Inkscape::NodePath::NODE_AUTO) - end->type = Inkscape::NodePath::NODE_SMOOTH; - - start->n.pos = start->pos; - end->p.pos = end->pos; - - sp_node_adjust_handle(start, -1); - sp_node_adjust_handle(end, 1); - - } else { - Geom::Point delta = end->pos - start->pos; - start->n.pos = start->pos + delta / 3; - end->p.pos = end->pos - delta / 3; - sp_node_adjust_handle(start, 1); - sp_node_adjust_handle(end, -1); - } - - sp_node_update_handles(start); - sp_node_update_handles(end); - } -} - -static void -sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node) -{ - if (node->type == Inkscape::NodePath::NODE_CUSP) { - node->knot->setShape (SP_KNOT_SHAPE_DIAMOND); - node->knot->setSize (node->selected? 11 : 9); - sp_knot_update_ctrl(node->knot); - } else if (node->type == Inkscape::NodePath::NODE_AUTO) { - node->knot->setShape (SP_KNOT_SHAPE_CIRCLE); - node->knot->setSize (node->selected? 11 : 9); - sp_knot_update_ctrl(node->knot); - } else { - node->knot->setShape (SP_KNOT_SHAPE_SQUARE); - node->knot->setSize (node->selected? 9 : 7); - sp_knot_update_ctrl(node->knot); - } -} - - -/** - * Change node type, and its handles accordingly. - */ -static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type) -{ - g_assert(node); - g_assert(node->subpath); - - if ((node->p.other != NULL) && (node->n.other != NULL)) { - if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) { - type =Inkscape::NodePath::NODE_CUSP; - } - } - - node->type = type; - - sp_nodepath_update_node_knot(node); - - // if one of handles is mouseovered, preserve its position - if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) { - sp_node_adjust_handle(node, 1); - } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) { - sp_node_adjust_handle(node, -1); - } else { - sp_node_adjust_handles(node); - } - - sp_node_update_handles(node); - - sp_nodepath_update_statusbar(node->subpath->nodepath); - - return node; -} - -bool -sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side) -{ -// TODO clean up multiple returns - Inkscape::NodePath::Node *othernode = side->other; - if (!othernode) - return false; - NRPathcode const code = sp_node_path_code_from_side(node, side); - if (code == NR_LINETO) - return true; - Inkscape::NodePath::NodeSide *other_to_me = NULL; - if (&node->p == side) { - other_to_me = &othernode->n; - } else if (&node->n == side) { - other_to_me = &othernode->p; - } - if (!other_to_me) - return false; - bool is_line = - (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 && - Geom::L2(node->pos - side->pos) < 1e-6); - return is_line; -} - -/** - * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from - * lines to curves. If adjacent to one line segment, pulls out or rotates opposite handle to align - * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. - * If already cusp and set to cusp, retracts handles. -*/ -void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type) -{ - if (type == Inkscape::NodePath::NODE_AUTO) { - if (node->p.other != NULL) - node->code = NR_CURVETO; - if (node->n.other != NULL) - node->n.other->code = NR_CURVETO; - } - - if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) { - -/* - Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode: - - if (two_handles) { - // do nothing, adjust_handles called via set_node_type will line them up - } else if (one_handle) { - if (opposite_to_handle_is_line) { - if (lined_up) { - // already half-smooth; pull opposite handle too making it fully smooth - } else { - // do nothing, adjust_handles will line the handle up, producing a half-smooth node - } - } else { - // pull opposite handle in line with the existing one - } - } else if (no_handles) { - if (both_segments_are_lines - OR both_segments_are_curves - OR one_is_line_but_the_curveside_node_is_selected_and_has_two_handles) { - //pull both handles - } else { - // pull the handle opposite to line segment, making node half-smooth - } - } -*/ - bool p_has_handle = (Geom::L2(node->pos - node->p.pos) > 1e-6); - bool n_has_handle = (Geom::L2(node->pos - node->n.pos) > 1e-6); - bool p_is_line = sp_node_side_is_line(node, &node->p); - bool n_is_line = sp_node_side_is_line(node, &node->n); - -#define NODE_HAS_BOTH_HANDLES(node) ((Geom::L2(node->pos - node->n.pos) > 1e-6) && (Geom::L2(node->pos - node->p.pos) > 1e-6)) - - if (p_has_handle && n_has_handle) { - // do nothing, adjust_handles will line them up - } else if (p_has_handle || n_has_handle) { - if (p_has_handle && n_is_line) { - Radial line (node->n.other->pos - node->pos); - Radial handle (node->pos - node->p.pos); - if (fabs(line.a - handle.a) < 1e-3) { // lined up - // already half-smooth; pull opposite handle too making it fully smooth - node->n.other->code = NR_CURVETO; - node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3; - } else { - // do nothing, adjust_handles will line the handle up, producing a half-smooth node - } - } else if (n_has_handle && p_is_line) { - Radial line (node->p.other->pos - node->pos); - Radial handle (node->pos - node->n.pos); - if (fabs(line.a - handle.a) < 1e-3) { // lined up - // already half-smooth; pull opposite handle too making it fully smooth - node->code = NR_CURVETO; - node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3; - } else { - // do nothing, adjust_handles will line the handle up, producing a half-smooth node - } - } else if (p_has_handle && node->n.other) { - // pull n handle - node->n.other->code = NR_CURVETO; - double len = (type == Inkscape::NodePath::NODE_SYMM)? - Geom::L2(node->p.pos - node->pos) : - Geom::L2(node->n.other->pos - node->pos) / 3; - node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos); - } else if (n_has_handle && node->p.other) { - // pull p handle - node->code = NR_CURVETO; - double len = (type == Inkscape::NodePath::NODE_SYMM)? - Geom::L2(node->n.pos - node->pos) : - Geom::L2(node->p.other->pos - node->pos) / 3; - node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos); - } - } else if (!p_has_handle && !n_has_handle) { - if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other) || - (n_is_line && node->p.other && node->p.other->selected && NODE_HAS_BOTH_HANDLES(node->p.other)) || - (p_is_line && node->n.other && node->n.other->selected && NODE_HAS_BOTH_HANDLES(node->n.other)) - ) { - // no handles, but: both segments are either lines or curves; or: one is line and the - // node at the other side is selected (so it was just smoothed too!) and now has both - // handles: then pull both handles here - - // convert both to curves: - node->code = NR_CURVETO; - node->n.other->code = NR_CURVETO; - - sp_node_adjust_handles_auto(node); - } else { - // pull the handle opposite to line segment, making it half-smooth - if (p_is_line && node->n.other) { - if (type != Inkscape::NodePath::NODE_SYMM) { - // pull n handle - node->n.other->code = NR_CURVETO; - double len = Geom::L2(node->n.other->pos - node->pos) / 3; - node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos); - } - } else if (n_is_line && node->p.other) { - if (type != Inkscape::NodePath::NODE_SYMM) { - // pull p handle - node->code = NR_CURVETO; - double len = Geom::L2(node->p.other->pos - node->pos) / 3; - node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos); - } - } - } - } - } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) { - // cusping a cusp: retract nodes - node->p.pos = node->pos; - node->n.pos = node->pos; - } - - sp_nodepath_set_node_type (node, type); -} - -/** - * Move node to point, and adjust its and neighbouring handles. - */ -void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p) -{ - if (node->type == Inkscape::NodePath::NODE_AUTO) { - node->pos = p; - sp_node_adjust_handles_auto(node); - } else { - Geom::Point delta = p - node->pos; - node->pos = p; - - node->p.pos += delta; - node->n.pos += delta; - } - - Inkscape::NodePath::Node *node_p = NULL; - Inkscape::NodePath::Node *node_n = NULL; - - if (node->p.other) { - if (sp_node_side_is_line(node, &node->p)) { - sp_node_adjust_handle(node, 1); - sp_node_adjust_handle(node->p.other, -1); - node_p = node->p.other; - } - if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) { - sp_node_adjust_handles_auto(node->p.other); - node_p = node->p.other; - } - } - if (node->n.other) { - if (sp_node_side_is_line(node, &node->n)) { - sp_node_adjust_handle(node, -1); - sp_node_adjust_handle(node->n.other, 1); - node_n = node->n.other; - } - if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) { - sp_node_adjust_handles_auto(node->n.other); - node_n = node->n.other; - } - } - - // this function is only called from batch movers that will update display at the end - // themselves, so here we just move all the knots without emitting move signals, for speed - sp_node_update_handles(node, false); - if (node_n) { - sp_node_update_handles(node_n, false); - } - if (node_p) { - sp_node_update_handles(node_p, false); - } -} - -/** - * Call sp_node_moveto() for node selection and handle possible snapping. - */ -static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy, - bool const snap, bool constrained = false, - Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point()) -{ - Geom::Point delta(dx, dy); - Geom::Point best_pt = delta; - Inkscape::SnappedPoint best; - - if (snap) { - /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and - * not to itself. The snapper however can not tell which nodes are selected and which are not, so we - * must provide that information. */ - - // Build a list of the unselected nodes to which the snapper should snap - std::vector<std::pair<Geom::Point, int> > unselected_nodes; - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (!node->selected) { - unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP)); - } - } - } - - SnapManager &m = nodepath->desktop->namedview->snap_manager; - - // When only the node closest to the mouse pointer is to be snapped - // then we will not even try to snap to other points and discard those immediately - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool closest_only = prefs->getBool("/options/snapclosestonly/value", false); - - Inkscape::NodePath::Node *closest_node = NULL; - Geom::Coord closest_dist = NR_HUGE; - - if (closest_only) { - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin); - if (dist < closest_dist) { - closest_node = n; - closest_dist = dist; - } - } - } - - // Iterate through all selected nodes - m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes); - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one - Inkscape::SnappedPoint s; - Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); - if (constrained) { - Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - dedicated_constraint.setPoint(n->pos); - s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false); - } else { - s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type); - } - - if (s.getSnapped()) { - s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin)); - if (!s.isOtherSnapBetter(best, true)) { - best = s; - best_pt = from_2geom(s.getPoint()) - n->pos; - } - } - } - } - - if (best.getSnapped()) { - nodepath->desktop->snapindicator->set_new_snaptarget(best); - } else { - nodepath->desktop->snapindicator->remove_snaptarget(); - } - } - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - sp_node_moveto(n, n->pos + best_pt); - } - - // do not update repr here so that node dragging is acceptably fast - update_object(nodepath); -} - -/** -Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like -curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve -near x = 0. - */ -double -sculpt_profile (double x, double alpha, guint profile) -{ - double result = 1; - - if (x >= 1) { - result = 0; - } else if (x <= 0) { - result = 1; - } else { - switch (profile) { - case SCULPT_PROFILE_LINEAR: - result = 1 - x; - break; - case SCULPT_PROFILE_BELL: - result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5); - break; - case SCULPT_PROFILE_ELLIPTIC: - result = sqrt(1 - x*x); - break; - default: - g_assert_not_reached(); - } - } - - return result; -} - -double -bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b) -{ - // extremely primitive for now, don't have time to look for the real one - double lower = Geom::L2(b - a); - double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b); - return (lower + upper)/2; -} - -void -sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p) -{ - n->pos = n->origin + delta; - n->n.pos = n->n.origin + delta_n; - n->p.pos = n->p.origin + delta_p; - sp_node_adjust_handles(n); - sp_node_update_handles(n, false); -} - -/** - * Displace selected nodes and their handles by fractions of delta (from their origins), depending - * on how far they are from the dragged node n. - */ -static void -sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta) -{ - g_assert (n); - g_assert (nodepath); - g_assert (n->subpath->nodepath == nodepath); - - double pressure = n->knot->pressure; - if (pressure == 0) - pressure = 0.5; // default - pressure = CLAMP (pressure, 0.2, 0.8); - - // map pressure to alpha = 1/5 ... 5 - double alpha = 1 - 2 * fabs(pressure - 0.5); - if (pressure > 0.5) - alpha = 1/alpha; - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL); - - if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) { - // Only one subpath has selected nodes: - // use linear mode, where the distance from n to node being dragged is calculated along the path - - double n_sel_range = 0, p_sel_range = 0; - guint n_nodes = 0, p_nodes = 0; - guint n_sel_nodes = 0, p_sel_nodes = 0; - - // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging) - { - double n_range = 0, p_range = 0; - bool n_going = true, p_going = true; - Inkscape::NodePath::Node *n_node = n; - Inkscape::NodePath::Node *p_node = n; - do { - // Do one step in both directions from n, until reaching the end of subpath or bumping into each other - if (n_node && n_going) - n_node = n_node->n.other; - if (n_node == NULL) { - n_going = false; - } else { - n_nodes ++; - n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin); - if (n_node->selected) { - n_sel_nodes ++; - n_sel_range = n_range; - } - if (n_node == p_node) { - n_going = false; - p_going = false; - } - } - if (p_node && p_going) - p_node = p_node->p.other; - if (p_node == NULL) { - p_going = false; - } else { - p_nodes ++; - p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin); - if (p_node->selected) { - p_sel_nodes ++; - p_sel_range = p_range; - } - if (p_node == n_node) { - n_going = false; - p_going = false; - } - } - } while (n_going || p_going); - } - - // Second pass: actually move nodes in this subpath - sp_nodepath_move_node_and_handles (n, delta, delta, delta); - { - double n_range = 0, p_range = 0; - bool n_going = true, p_going = true; - Inkscape::NodePath::Node *n_node = n; - Inkscape::NodePath::Node *p_node = n; - do { - // Do one step in both directions from n, until reaching the end of subpath or bumping into each other - if (n_node && n_going) - n_node = n_node->n.other; - if (n_node == NULL) { - n_going = false; - } else { - n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin); - if (n_node->selected) { - sp_nodepath_move_node_and_handles (n_node, - sculpt_profile (n_range / n_sel_range, alpha, profile) * delta, - sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta, - sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta); - } - if (n_node == p_node) { - n_going = false; - p_going = false; - } - } - if (p_node && p_going) - p_node = p_node->p.other; - if (p_node == NULL) { - p_going = false; - } else { - p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin); - if (p_node->selected) { - sp_nodepath_move_node_and_handles (p_node, - sculpt_profile (p_range / p_sel_range, alpha, profile) * delta, - sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta, - sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta); - } - if (p_node == n_node) { - n_going = false; - p_going = false; - } - } - } while (n_going || p_going); - } - - } else { - // Multiple subpaths have selected nodes: - // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2. - // TODO: correct these distances taking into account their angle relative to the bisector, so as to - // fix the pear-like shape when sculpting e.g. a ring - - // First pass: calculate range - gdouble direct_range = 0; - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (node->selected) { - direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin)); - } - } - } - - // Second pass: actually move nodes - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (node->selected) { - if (direct_range > 1e-6) { - sp_nodepath_move_node_and_handles (node, - sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta, - sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta, - sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta); - } else { - sp_nodepath_move_node_and_handles (node, delta, delta, delta); - } - - } - } - } - } - - // do not update repr here so that node dragging is acceptably fast - update_object(nodepath); -} - - -/** - * Move node selection to point, adjust its and neighbouring handles, - * handle possible snapping, and commit the change with possible undo. - */ -void -sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy) -{ - if (!nodepath) return; - - sp_nodepath_selected_nodes_move(nodepath, dx, dy, false); - - if (dx == 0) { - sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically")); - } else if (dy == 0) { - sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally")); - } else { - sp_nodepath_update_repr(nodepath, _("Move nodes")); - } -} - -/** - * Move node selection off screen and commit the change. - */ -void -sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy) -{ - // borrowed from sp_selection_move_screen in selection-chemistry.c - // we find out the current zoom factor and divide deltas by it - - gdouble zoom = desktop->current_zoom(); - gdouble zdx = dx / zoom; - gdouble zdy = dy / zoom; - - if (!nodepath) return; - - sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false); - - if (dx == 0) { - sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically")); - } else if (dy == 0) { - sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally")); - } else { - sp_nodepath_update_repr(nodepath, _("Move nodes")); - } -} - -/** - * Move selected nodes to the absolute position given - */ -void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis) -{ - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]); - sp_node_moveto(n, npos); - } - - sp_nodepath_update_repr(nodepath, _("Move nodes")); -} - -/** - * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing - */ -boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) -{ - boost::optional<Geom::Coord> no_coord; - g_return_val_if_fail(nodepath->selected, no_coord); - - // determine coordinate of first selected node - GList *nsel = nodepath->selected; - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data; - Geom::Coord coord = n->pos[axis]; - bool coincide = true; - - // compare it to the coordinates of all the other selected nodes - for (GList *l = nsel->next; l != NULL; l = l->next) { - n = (Inkscape::NodePath::Node *) l->data; - if (n->pos[axis] != coord) { - coincide = false; - } - } - if (coincide) { - return coord; - } else { - Geom::Rect bbox = sp_node_selected_bbox(nodepath); - // currently we return the coordinate of the bounding box midpoint because I don't know how - // to erase the spin button entry field :), but maybe this can be useful behaviour anyway - return bbox.midpoint()[axis]; - } -} - -/** If they don't yet exist, creates knot and line for the given side of the node */ -static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side) -{ - if (!side->knot) { - side->knot = sp_knot_new(desktop, _("<b>Node handle</b>: drag to shape the curve; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles")); - - side->knot->setShape (SP_KNOT_SHAPE_CIRCLE); - side->knot->setSize (7); - side->knot->setAnchor (GTK_ANCHOR_CENTER); - side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI); - side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI); - sp_knot_update_ctrl(side->knot); - - g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node); - g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node); - g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node); - g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node); - g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node); - g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node); - } - - if (!side->line) { - side->line = sp_canvas_item_new(sp_desktop_controls(desktop), - SP_TYPE_CTRLLINE, NULL); - } -} - -/** - * Ensure the given handle of the node is visible/invisible, update its screen position - */ -static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals) -{ - g_assert(node != NULL); - - Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which); - NRPathcode code = sp_node_path_code_from_side(node, side); - - show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6); - - if (show_handle) { - if (!side->knot) { // No handle knot at all - sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side); - // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly - side->knot->pos = side->pos; - if (side->knot->item) - SP_CTRL(side->knot->item)->moveto(side->pos); - sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos); - sp_knot_show(side->knot); - } else { - if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved - if (fire_move_signals) { - sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well - } else { - sp_knot_moveto(side->knot, side->pos); - sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos); - } - } - if (!SP_KNOT_IS_VISIBLE(side->knot)) { - sp_knot_show(side->knot); - } - } - sp_canvas_item_show(side->line); - } else { - if (side->knot) { - if (SP_KNOT_IS_VISIBLE(side->knot)) { - sp_knot_hide(side->knot); - } - } - if (side->line) { - sp_canvas_item_hide(side->line); - } - } -} - -/** - * Ensure the node itself is visible, its handles and those of the neighbours of the node are - * visible if selected, update their screen positions. If fire_move_signals, move the node and its - * handles so that the corresponding signals are fired, callbacks are activated, and curve is - * updated; otherwise, just move the knots silently (used in batch moves). - */ -static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals) -{ - g_assert(node != NULL); - - if (!SP_KNOT_IS_VISIBLE(node->knot)) { - sp_knot_show(node->knot); - } - - if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update - if (fire_move_signals) - sp_knot_set_position(node->knot, node->pos, 0); - else - sp_knot_moveto(node->knot, node->pos); - } - - gboolean show_handles = node->selected; - if (node->p.other != NULL) { - if (node->p.other->selected) show_handles = TRUE; - } - if (node->n.other != NULL) { - if (node->n.other->selected) show_handles = TRUE; - } - - if (node->subpath->nodepath->show_handles == false) - show_handles = FALSE; - - sp_node_update_handle(node, -1, show_handles, fire_move_signals); - sp_node_update_handle(node, 1, show_handles, fire_move_signals); -} - -/** - * Call sp_node_update_handles() for all nodes on subpath. - */ -static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath) -{ - g_assert(subpath != NULL); - - for (GList *l = subpath->nodes; l != NULL; l = l->next) { - sp_node_update_handles((Inkscape::NodePath::Node *) l->data); - } -} - -/** - * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath. - */ -static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath) -{ - g_assert(nodepath != NULL); - - for (GList *l = nodepath->subpaths; l != NULL; l = l->next) { - sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data); - } -} - -void -sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show) -{ - if (nodepath) { - nodepath->show_handles = show; - sp_nodepath_update_handles(nodepath); - } -} - -/** - * Adds all selected nodes in nodepath to list. - */ -void Inkscape::NodePath::Path::selection(std::list<Node *> &l) -{ - StlConv<Node *>::list(l, selected); -/// \todo this adds a copying, rework when the selection becomes a stl list -} - -/** - * Align selected nodes on the specified axis. - */ -void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) -{ - if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected - return; - } - - if ( !nodepath->selected->next ) { // only one node selected - return; - } - Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data); - Geom::Point dest(pNode->pos); - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data); - if (pNode) { - dest[axis] = pNode->pos[axis]; - sp_node_moveto(pNode, dest); - } - } - - sp_nodepath_update_repr(nodepath, _("Align nodes")); -} - -/// Helper struct. -struct NodeSort -{ - Inkscape::NodePath::Node *_node; - Geom::Coord _coord; - /// \todo use vectorof pointers instead of calling copy ctor - NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) : - _node(node), _coord(node->pos[axis]) - {} - -}; - -static bool operator<(NodeSort const &a, NodeSort const &b) -{ - return (a._coord < b._coord); -} - -/** - * Distribute selected nodes on the specified axis. - */ -void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) -{ - if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected - return; - } - - if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected - return; - } - - Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data); - std::vector<NodeSort> sorted; - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data); - if (pNode) { - NodeSort n(pNode, axis); - sorted.push_back(n); - //dest[axis] = pNode->pos[axis]; - //sp_node_moveto(pNode, dest); - } - } - std::sort(sorted.begin(), sorted.end()); - unsigned int len = sorted.size(); - //overall bboxes span - float dist = (sorted.back()._coord - - sorted.front()._coord); - //new distance between each bbox - float step = (dist) / (len - 1); - float pos = sorted.front()._coord; - for ( std::vector<NodeSort> ::iterator it(sorted.begin()); - it < sorted.end(); - it ++ ) - { - Geom::Point dest((*it)._node->pos); - dest[axis] = pos; - sp_node_moveto((*it)._node, dest); - pos += step; - } - - sp_nodepath_update_repr(nodepath, _("Distribute nodes")); -} - - -/** - * Call sp_nodepath_line_add_node() for all selected segments. - */ -void -sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) { - return; - } - - GList *nl = NULL; - - int n_added = 0; - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data; - g_assert(t->selected); - if (t->p.other && t->p.other->selected) { - nl = g_list_prepend(nl, t); - } - } - - while (nl) { - Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data; - Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5); - sp_nodepath_node_select(n, TRUE, FALSE); - n_added ++; - nl = g_list_remove(nl, t); - } - - /** \todo fixme: adjust ? */ - sp_nodepath_update_handles(nodepath); - - if (n_added > 1) { - sp_nodepath_update_repr(nodepath, _("Add nodes")); - } else if (n_added > 0) { - sp_nodepath_update_repr(nodepath, _("Add node")); - } - - sp_nodepath_update_statusbar(nodepath); -} - -/** - * Select segment nearest to point - */ -void -sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle) -{ - if (!nodepath) { - return; - } - - SPCurve *curve = create_curve(nodepath); // perhaps we can use nodepath->curve here instead? - Geom::PathVector const &pathv = curve->get_pathvector(); - boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p); - if (!pvpos) { - g_print ("Possible error?\n"); - return; - } - - // calculate index for nodepath's representation. - unsigned int segment_index = floor(pvpos->t) + 1; - for (unsigned int i = 0; i < pvpos->path_nr; ++i) { - segment_index += pathv[i].size() + 1; - if (pathv[i].closed()) { - segment_index += 1; - } - } - - curve->unref(); - - //find segment to segment - Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index); - - //fixme: this can return NULL, so check before proceeding. - g_return_if_fail(e != NULL); - - gboolean force = FALSE; - if (!(e->selected && (!e->p.other || e->p.other->selected))) { - force = TRUE; - } - sp_nodepath_node_select(e, (gboolean) toggle, force); - if (e->p.other) - sp_nodepath_node_select(e->p.other, TRUE, force); - - sp_nodepath_update_handles(nodepath); - - sp_nodepath_update_statusbar(nodepath); -} - -/** - * Add a node nearest to point - */ -void -sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p) -{ - if (!nodepath) { - return; - } - - SPCurve *curve = create_curve(nodepath); // perhaps we can use nodepath->curve here instead? - Geom::PathVector const &pathv = curve->get_pathvector(); - boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p); - if (!pvpos) { - g_print ("Possible error?\n"); - return; - } - - // calculate index for nodepath's representation. - double int_part; - double t = std::modf(pvpos->t, &int_part); - unsigned int segment_index = (unsigned int)int_part + 1; - for (unsigned int i = 0; i < pvpos->path_nr; ++i) { - segment_index += pathv[i].size() + 1; - if (pathv[i].closed()) { - segment_index += 1; - } - } - - curve->unref(); - - //find segment to split - Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index); - if (!e) { - return; - } - - //don't know why but t seems to flip for lines - if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) { - t = 1.0 - t; - } - - Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t); - sp_nodepath_node_select(n, FALSE, TRUE); - - /* fixme: adjust ? */ - sp_nodepath_update_handles(nodepath); - - sp_nodepath_update_repr(nodepath, _("Add node")); - - sp_nodepath_update_statusbar(nodepath); -} - -/* - * Adjusts a segment so that t moves by a certain delta for dragging - * converts lines to curves - * - * method and idea borrowed from Simon Budig <simon@gimp.org> and the GIMP - * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative() - */ -void -sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta) -{ - Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node); - - //fixme: e and e->p can be NULL, so check for those before proceeding - g_return_if_fail(e != NULL); - g_return_if_fail(&e->p != NULL); - - if (e->type == Inkscape::NodePath::NODE_AUTO) { - e->type = Inkscape::NodePath::NODE_SMOOTH; - sp_nodepath_update_node_knot (e); - } - if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) { - e->p.other->type = Inkscape::NodePath::NODE_SMOOTH; - sp_nodepath_update_node_knot (e->p.other); - } - - /* feel good is an arbitrary parameter that distributes the delta between handles - * if t of the drag point is less than 1/6 distance form the endpoint only - * the corresponding hadle is adjusted. This matches the behavior in GIMP - */ - double feel_good; - if (t <= 1.0 / 6.0) - feel_good = 0; - else if (t <= 0.5) - feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2; - else if (t <= 5.0 / 6.0) - feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5; - else - feel_good = 1; - - //if we're dragging a line convert it to a curve - if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) { - sp_nodepath_set_line_type(e, NR_CURVETO); - } - - Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta; - Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta; - e->p.other->n.pos += offsetcoord0; - e->p.pos += offsetcoord1; - - // adjust handles of adjacent nodes where necessary - sp_node_adjust_handle(e,1); - sp_node_adjust_handle(e->p.other,-1); - - sp_nodepath_update_handles(e->subpath->nodepath); - - update_object(e->subpath->nodepath); - - sp_nodepath_update_statusbar(e->subpath->nodepath); -} - - -/** - * Call sp_nodepath_break() for all selected segments. - */ -void sp_node_selected_break(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) return; - - GList *tempin = g_list_copy(nodepath->selected); - GList *temp = NULL; - for (GList *l = tempin; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n); - if (nn == NULL) continue; // no break, no new node - temp = g_list_prepend(temp, nn); - } - g_list_free(tempin); - - if (temp) { - sp_nodepath_deselect(nodepath); - } - for (GList *l = temp; l != NULL; l = l->next) { - sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE); - } - - sp_nodepath_update_handles(nodepath); - - sp_nodepath_update_repr(nodepath, _("Break path")); -} - -/** - * Duplicate the selected node(s). - */ -void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) { - return; - } - - GList *temp = NULL; - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n); - if (nn == NULL) continue; // could not duplicate - temp = g_list_prepend(temp, nn); - } - - if (temp) { - sp_nodepath_deselect(nodepath); - } - for (GList *l = temp; l != NULL; l = l->next) { - sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE); - } - - sp_nodepath_update_handles(nodepath); - - sp_nodepath_update_repr(nodepath, _("Duplicate node")); -} - -/** - * Internal function to join two nodes by merging them into one. - */ -static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b) -{ - /* a and b are endpoints */ - - // if one of the two nodes is mouseovered, fix its position - Geom::Point c; - if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) { - c = a->pos; - } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) { - c = b->pos; - } else { - // otherwise, move joined node to the midpoint - c = (a->pos + b->pos) / 2; - } - - if (a->subpath == b->subpath) { - Inkscape::NodePath::SubPath *sp = a->subpath; - sp_nodepath_subpath_close(sp); - sp_node_moveto (sp->first, c); - - sp_nodepath_update_handles(sp->nodepath); - sp_nodepath_update_repr(nodepath, _("Close subpath")); - return; - } - - /* a and b are separate subpaths */ - Inkscape::NodePath::SubPath *sa = a->subpath; - Inkscape::NodePath::SubPath *sb = b->subpath; - Geom::Point p; - Inkscape::NodePath::Node *n; - NRPathcode code; - if (a == sa->first) { - // we will now reverse sa, so that a is its last node, not first, and drop that node - p = sa->first->n.pos; - code = (NRPathcode)sa->first->n.other->code; - // create new subpath - Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath); - // create a first moveto node on it - n = sa->last; - sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos); - n = n->p.other; - if (n == sa->first) n = NULL; - while (n) { - // copy the rest of the nodes from sa to t, going backwards - sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos); - n = n->p.other; - if (n == sa->first) n = NULL; - } - // replace sa with t - sp_nodepath_subpath_destroy(sa); - sa = t; - } else if (a == sa->last) { - // a is already last, just drop it - p = sa->last->p.pos; - code = (NRPathcode)sa->last->code; - sp_nodepath_node_destroy(sa->last); - } else { - code = NR_END; - g_assert_not_reached(); - } - - if (b == sb->first) { - // copy all nodes from b to a, forward - sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos); - for (n = sb->first->n.other; n != NULL; n = n->n.other) { - sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos); - } - } else if (b == sb->last) { - // copy all nodes from b to a, backward - sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos); - for (n = sb->last->p.other; n != NULL; n = n->p.other) { - sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos); - } - } else { - g_assert_not_reached(); - } - /* and now destroy sb */ - - sp_nodepath_subpath_destroy(sb); - - sp_nodepath_update_handles(sa->nodepath); - - sp_nodepath_update_repr(nodepath, _("Join nodes")); - - sp_nodepath_update_statusbar(nodepath); -} - -/** - * Internal function to join two nodes by adding a segment between them. - */ -static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b) -{ - if (a->subpath == b->subpath) { - Inkscape::NodePath::SubPath *sp = a->subpath; - - /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/ - sp->closed = TRUE; - - sp->first->p.other = sp->last; - sp->last->n.other = sp->first; - - sp_node_handle_mirror_p_to_n(sp->last); - sp_node_handle_mirror_n_to_p(sp->first); - - sp->first->code = sp->last->code; - sp->first = sp->last; - - sp_nodepath_update_handles(sp->nodepath); - - sp_nodepath_update_repr(nodepath, _("Close subpath by segment")); - - return; - } - - /* a and b are separate subpaths */ - Inkscape::NodePath::SubPath *sa = a->subpath; - Inkscape::NodePath::SubPath *sb = b->subpath; - - Inkscape::NodePath::Node *n; - Geom::Point p; - NRPathcode code; - if (a == sa->first) { - code = (NRPathcode) sa->first->n.other->code; - Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath); - n = sa->last; - sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos); - for (n = n->p.other; n != NULL; n = n->p.other) { - sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos); - } - sp_nodepath_subpath_destroy(sa); - sa = t; - } else if (a == sa->last) { - code = (NRPathcode)sa->last->code; - } else { - code = NR_END; - g_assert_not_reached(); - } - - if (b == sb->first) { - n = sb->first; - sp_node_handle_mirror_p_to_n(sa->last); - sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos); - sp_node_handle_mirror_n_to_p(sa->last); - for (n = n->n.other; n != NULL; n = n->n.other) { - sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos); - } - } else if (b == sb->last) { - n = sb->last; - sp_node_handle_mirror_p_to_n(sa->last); - sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos); - sp_node_handle_mirror_n_to_p(sa->last); - for (n = n->p.other; n != NULL; n = n->p.other) { - sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos); - } - } else { - g_assert_not_reached(); - } - /* and now destroy sb */ - - sp_nodepath_subpath_destroy(sb); - - sp_nodepath_update_handles(sa->nodepath); - - sp_nodepath_update_repr(nodepath, _("Join nodes by segment")); -} - -enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT }; - -/** - * Internal function to handle joining two nodes. - */ -static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode) -{ - if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses - - if (g_list_length(nodepath->selected) != 2) { - nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected.")); - return; - } - - Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data; - Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data; - - g_assert(a != b); - if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { - // someone tried to join an orphan node (i.e. a single-node subpath). - // this is not worth an error message, just fail silently. - return; - } - - if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) { - nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected.")); - return; - } - - switch(mode) { - case NODE_JOIN_ENDPOINTS: - do_node_selected_join(nodepath, a, b); - break; - case NODE_JOIN_SEGMENT: - do_node_selected_join_segment(nodepath, a, b); - break; - } -} - -/** - * Join two nodes by merging them into one. - */ -void sp_node_selected_join(Inkscape::NodePath::Path *nodepath) -{ - node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS); -} - -/** - * Join two nodes by adding a segment between them. - */ -void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath) -{ - node_do_selected_join(nodepath, NODE_JOIN_SEGMENT); -} - -/** - * Delete one or more selected nodes and preserve the shape of the path as much as possible. - */ -void sp_node_delete_preserve(GList *nodes_to_delete) -{ - GSList *nodepaths = NULL; - - while (nodes_to_delete) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data; - Inkscape::NodePath::SubPath *sp = node->subpath; - Inkscape::NodePath::Path *nodepath = sp->nodepath; - Inkscape::NodePath::Node *sample_cursor = NULL; - Inkscape::NodePath::Node *sample_end = NULL; - Inkscape::NodePath::Node *delete_cursor = node; - bool just_delete = false; - - //find the start of this contiguous selection - //move left to the first node that is not selected - //or the start of the non-closed path - for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) { - delete_cursor = curr; - } - - //just delete at the beginning of an open path - if (!delete_cursor->p.other) { - sample_cursor = delete_cursor; - just_delete = true; - } else { - sample_cursor = delete_cursor->p.other; - } - - //calculate points for each segment - int rate = 5; - float period = 1.0 / rate; - std::vector<Geom::Point> data; - if (!just_delete) { - data.push_back(sample_cursor->pos); - for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) { - //just delete at the end of an open path - if (!sp->closed && curr == sp->last) { - just_delete = true; - break; - } - - //sample points on the contiguous selected segment - Geom::Point *bez; - bez = new Geom::Point [4]; - bez[0] = curr->pos; - bez[1] = curr->n.pos; - bez[2] = curr->n.other->p.pos; - bez[3] = curr->n.other->pos; - for (int i=1; i<rate; i++) { - gdouble t = i * period; - Geom::Point p = bezier_pt(3, bez, t); - data.push_back(p); - } - data.push_back(curr->n.other->pos); - - sample_end = curr->n.other; - //break if we've come full circle or hit the end of the selection - if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) { - break; - } - } - } - - if (!just_delete) { - //calculate the best fitting single segment and adjust the endpoints - Geom::Point *adata; - adata = new Geom::Point [data.size()]; - copy(data.begin(), data.end(), adata); - - Geom::Point *bez; - bez = new Geom::Point [4]; - //would decreasing error create a better fitting approximation? - gdouble error = 1.0; - gint ret; - ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error); - - //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync. - //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved, - //the resulting nodes behave as expected. - if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP) - sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP); - if (sample_end->type != Inkscape::NodePath::NODE_CUSP) - sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP); - - //adjust endpoints - sample_cursor->n.pos = bez[1]; - sample_end->p.pos = bez[2]; - } - - //destroy this contiguous selection - while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) { - Inkscape::NodePath::Node *temp = delete_cursor; - if (delete_cursor->n.other == delete_cursor) { - // delete_cursor->n points to itself, which means this is the last node on a closed subpath - delete_cursor = NULL; - } else { - delete_cursor = delete_cursor->n.other; - } - nodes_to_delete = g_list_remove(nodes_to_delete, temp); - sp_nodepath_node_destroy(temp); - } - - sp_nodepath_update_handles(nodepath); - - if (!g_slist_find(nodepaths, nodepath)) - nodepaths = g_slist_prepend (nodepaths, nodepath); - } - - for (GSList *i = nodepaths; i; i = i->next) { - // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from - // different nodepaths will give us one undo event per nodepath - Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data; - - // if the entire nodepath is removed, delete the selected object. - if (nodepath->subpaths == NULL || - //FIXME: a closed path CAN legally have one node, it's only an open one which must be - //at least 2 - sp_nodepath_get_node_count(nodepath) < 2) { - SPDocument *document = sp_desktop_document (nodepath->desktop); - //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to - //delete this nodepath's object, not the entire selection! (though at this time, this - //does not matter) - sp_selection_delete(nodepath->desktop); - sp_document_done (document, SP_VERB_CONTEXT_NODE, - _("Delete nodes")); - } else { - sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape")); - sp_nodepath_update_statusbar(nodepath); - } - } - - g_slist_free (nodepaths); -} - -/** - * Delete one or more selected nodes. - */ -void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) return; - if (!nodepath->selected) return; - - /** \todo fixme: do it the right way */ - while (nodepath->selected) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data; - sp_nodepath_node_destroy(node); - } - - - //clean up the nodepath (such as for trivial subpaths) - sp_nodepath_cleanup(nodepath); - - sp_nodepath_update_handles(nodepath); - - // if the entire nodepath is removed, delete the selected object. - if (nodepath->subpaths == NULL || - sp_nodepath_get_node_count(nodepath) < 2) { - SPDocument *document = sp_desktop_document (nodepath->desktop); - sp_selection_delete(nodepath->desktop); - sp_document_done (document, SP_VERB_CONTEXT_NODE, - _("Delete nodes")); - return; - } - - sp_nodepath_update_repr(nodepath, _("Delete nodes")); - - sp_nodepath_update_statusbar(nodepath); -} - -/** - * Delete one or more segments between two selected nodes. - * This is the code for 'split'. - */ -void -sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath) -{ - Inkscape::NodePath::Node *start, *end; //Start , end nodes. not inclusive - Inkscape::NodePath::Node *curr, *next; //Iterators - - if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses - - if (g_list_length(nodepath->selected) != 2) { - nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, - _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments.")); - return; - } - - //Selected nodes, not inclusive - Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data; - Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data; - - if ( ( a==b) || //same node - (a->subpath != b->subpath ) || //not the same path - (!a->p.other || !a->n.other) || //one of a's sides does not have a segment - (!b->p.other || !b->n.other) ) //one of b's sides does not have a segment - { - nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, - _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments.")); - return; - } - - //########################################### - //# BEGIN EDITS - //########################################### - //################################## - //# CLOSED PATH - //################################## - if (a->subpath->closed) { - - - gboolean reversed = FALSE; - - //Since we can go in a circle, we need to find the shorter distance. - // a->b or b->a - start = end = NULL; - int distance = 0; - int minDistance = 0; - for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) { - if (curr==b) { - //printf("a to b:%d\n", distance); - start = a;//go from a to b - end = b; - minDistance = distance; - //printf("A to B :\n"); - break; - } - distance++; - } - - //try again, the other direction - distance = 0; - for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) { - if (curr==a) { - //printf("b to a:%d\n", distance); - if (distance < minDistance) { - start = b; //we go from b to a - end = a; - reversed = TRUE; - //printf("B to A\n"); - } - break; - } - distance++; - } - - - //Copy everything from 'end' to 'start' to a new subpath - Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath); - for (curr=end ; curr ; curr=curr->n.other) { - NRPathcode code = (NRPathcode) curr->code; - if (curr == end) - code = NR_MOVETO; - sp_nodepath_node_new(t, NULL, - (Inkscape::NodePath::NodeType)curr->type, code, - &curr->p.pos, &curr->pos, &curr->n.pos); - if (curr == start) - break; - } - sp_nodepath_subpath_destroy(a->subpath); - - - } - - - - //################################## - //# OPEN PATH - //################################## - else { - - //We need to get the direction of the list between A and B - //Can we walk from a to b? - start = end = NULL; - for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) { - if (curr==b) { - start = a; //did it! we go from a to b - end = b; - //printf("A to B\n"); - break; - } - } - if (!start) {//didn't work? let's try the other direction - for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) { - if (curr==a) { - start = b; //did it! we go from b to a - end = a; - //printf("B to A\n"); - break; - } - } - } - if (!start) { - nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, - _("Cannot find path between nodes.")); - return; - } - - - - //Copy everything after 'end' to a new subpath - Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath); - for (curr=end ; curr ; curr=curr->n.other) { - NRPathcode code = (NRPathcode) curr->code; - if (curr == end) - code = NR_MOVETO; - sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code, - &curr->p.pos, &curr->pos, &curr->n.pos); - } - - //Now let us do our deletion. Since the tail has been saved, go all the way to the end of the list - for (curr = start->n.other ; curr ; curr=next) { - next = curr->n.other; - sp_nodepath_node_destroy(curr); - } - - } - //########################################### - //# END EDITS - //########################################### - - //clean up the nodepath (such as for trivial subpaths) - sp_nodepath_cleanup(nodepath); - - sp_nodepath_update_handles(nodepath); - - sp_nodepath_update_repr(nodepath, _("Delete segment")); - - sp_nodepath_update_statusbar(nodepath); -} - -/** - * Call sp_nodepath_set_line() for all selected segments. - */ -void -sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code) -{ - if (nodepath == NULL) return; - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - g_assert(n->selected); - if (n->p.other && n->p.other->selected) { - sp_nodepath_set_line_type(n, code); - } - } - - sp_nodepath_update_repr(nodepath, _("Change segment type")); -} - -/** - * Call sp_nodepath_convert_node_type() for all selected nodes. - */ -void -sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type) -{ - if (nodepath == NULL) return; - - if (nodepath->straight_path) return; // don't change type when it is a straight path! - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type); - } - - sp_nodepath_update_repr(nodepath, _("Change node type")); -} - -/** - * Change select status of node, update its own and neighbour handles. - */ -static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected) -{ - node->selected = selected; - - if (selected) { - node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9); - node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI); - node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI); - sp_knot_update_ctrl(node->knot); - } else { - node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7); - node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI); - node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI); - sp_knot_update_ctrl(node->knot); - } - - sp_node_update_handles(node); - if (node->n.other) sp_node_update_handles(node->n.other); - if (node->p.other) sp_node_update_handles(node->p.other); -} - -/** -\brief Select a node -\param node The node to select -\param incremental If true, add to selection, otherwise deselect others -\param override If true, always select this node, otherwise toggle selected status -*/ -static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override) -{ - Inkscape::NodePath::Path *nodepath = node->subpath->nodepath; - - if (incremental) { - if (override) { - if (!g_list_find(nodepath->selected, node)) { - nodepath->selected = g_list_prepend(nodepath->selected, node); - } - sp_node_set_selected(node, TRUE); - } else { // toggle - if (node->selected) { - g_assert(g_list_find(nodepath->selected, node)); - nodepath->selected = g_list_remove(nodepath->selected, node); - } else { - g_assert(!g_list_find(nodepath->selected, node)); - nodepath->selected = g_list_prepend(nodepath->selected, node); - } - sp_node_set_selected(node, !node->selected); - } - } else { - sp_nodepath_deselect(nodepath); - nodepath->selected = g_list_prepend(nodepath->selected, node); - sp_node_set_selected(node, TRUE); - } - - sp_nodepath_update_statusbar(nodepath); -} - - -/** -\brief Deselect all nodes in the nodepath -*/ -void -sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses - - while (nodepath->selected) { - sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE); - nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data); - } - sp_nodepath_update_statusbar(nodepath); -} - -/** -\brief Select or invert selection of all nodes in the nodepath -*/ -void -sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert) -{ - if (!nodepath) return; - - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE); - } - } -} - -/** - * If nothing selected, does the same as sp_nodepath_select_all(); - * otherwise selects/inverts all nodes in all subpaths that have selected nodes - * (i.e., similar to "select all in layer", with the "selected" subpaths - * being treated as "layers" in the path). - */ -void -sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert) -{ - if (!nodepath) return; - - if (g_list_length (nodepath->selected) == 0) { - sp_nodepath_select_all (nodepath, invert); - return; - } - - GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us - GSList *subpaths = NULL; - - for (GList *l = copy; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - Inkscape::NodePath::SubPath *subpath = n->subpath; - if (!g_slist_find (subpaths, subpath)) - subpaths = g_slist_prepend (subpaths, subpath); - } - - for (GSList *sp = subpaths; sp != NULL; sp = sp->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE); - } - } - - g_slist_free (subpaths); - g_list_free (copy); -} - -/** - * \brief Select the node after the last selected; if none is selected, - * select the first within path. - */ -void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses - - Inkscape::NodePath::Node *last = NULL; - if (nodepath->selected) { - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath, *subpath_next; - subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (node->selected) { - if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) { - if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath - if (spl->next) { // there's a next subpath - subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data; - last = subpath_next->first; - } else if (spl->prev) { // there's a previous subpath - last = NULL; // to be set later to the first node of first subpath - } else { - last = node->n.other; - } - } else { - last = node->n.other; - } - } else { - if (node->n.other) { - last = node->n.other; - } else { - if (spl->next) { // there's a next subpath - subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data; - last = subpath_next->first; - } else if (spl->prev) { // there's a previous subpath - last = NULL; // to be set later to the first node of first subpath - } else { - last = (Inkscape::NodePath::Node *) subpath->first; - } - } - } - } - } - } - sp_nodepath_deselect(nodepath); - } - - if (last) { // there's at least one more node after selected - sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE); - } else { // no more nodes, select the first one in first subpath - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data; - sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE); - } -} - -/** - * \brief Select the node before the first selected; if none is selected, - * select the last within path - */ -void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath) -{ - if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses - - Inkscape::NodePath::Node *last = NULL; - if (nodepath->selected) { - for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (node->selected) { - if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) { - if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath - if (spl->prev) { // there's a prev subpath - Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data; - last = subpath_prev->last; - } else if (spl->next) { // there's a next subpath - last = NULL; // to be set later to the last node of last subpath - } else { - last = node->p.other; - } - } else { - last = node->p.other; - } - } else { - if (node->p.other) { - last = node->p.other; - } else { - if (spl->prev) { // there's a prev subpath - Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data; - last = subpath_prev->last; - } else if (spl->next) { // there's a next subpath - last = NULL; // to be set later to the last node of last subpath - } else { - last = (Inkscape::NodePath::Node *) subpath->last; - } - } - } - } - } - } - sp_nodepath_deselect(nodepath); - } - - if (last) { // there's at least one more node before selected - sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE); - } else { // no more nodes, select the last one in last subpath - GList *spl = g_list_last(nodepath->subpaths); - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE); - } -} - -/** - * \brief Select all nodes that are within the rectangle. - */ -void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental) -{ - if (!incremental) { - sp_nodepath_deselect(nodepath); - } - - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - - if (b.contains(node->pos)) { - sp_nodepath_node_select(node, TRUE, TRUE); - } - } - } -} - - -void -nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow) -{ - g_assert (n); - g_assert (nodepath); - g_assert (n->subpath->nodepath == nodepath); - - if (g_list_length (nodepath->selected) == 0) { - if (grow > 0) { - sp_nodepath_node_select(n, TRUE, TRUE); - } - return; - } - - if (g_list_length (nodepath->selected) == 1) { - if (grow < 0) { - sp_nodepath_deselect (nodepath); - return; - } - } - - double n_sel_range = 0, p_sel_range = 0; - Inkscape::NodePath::Node *farthest_n_node = n; - Inkscape::NodePath::Node *farthest_p_node = n; - - // Calculate ranges - { - double n_range = 0, p_range = 0; - bool n_going = true, p_going = true; - Inkscape::NodePath::Node *n_node = n; - Inkscape::NodePath::Node *p_node = n; - do { - // Do one step in both directions from n, until reaching the end of subpath or bumping into each other - if (n_node && n_going) - n_node = n_node->n.other; - if (n_node == NULL) { - n_going = false; - } else { - n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos); - if (n_node->selected) { - n_sel_range = n_range; - farthest_n_node = n_node; - } - if (n_node == p_node) { - n_going = false; - p_going = false; - } - } - if (p_node && p_going) - p_node = p_node->p.other; - if (p_node == NULL) { - p_going = false; - } else { - p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos); - if (p_node->selected) { - p_sel_range = p_range; - farthest_p_node = p_node; - } - if (p_node == n_node) { - n_going = false; - p_going = false; - } - } - } while (n_going || p_going); - } - - if (grow > 0) { - if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) { - sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE); - } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) { - sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE); - } - } else { - if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) { - sp_nodepath_node_select(farthest_n_node, TRUE, FALSE); - } else if (farthest_p_node && farthest_p_node->selected) { - sp_nodepath_node_select(farthest_p_node, TRUE, FALSE); - } - } -} - -void -nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow) -{ - g_assert (n); - g_assert (nodepath); - g_assert (n->subpath->nodepath == nodepath); - - if (g_list_length (nodepath->selected) == 0) { - if (grow > 0) { - sp_nodepath_node_select(n, TRUE, TRUE); - } - return; - } - - if (g_list_length (nodepath->selected) == 1) { - if (grow < 0) { - sp_nodepath_deselect (nodepath); - return; - } - } - - Inkscape::NodePath::Node *farthest_selected = NULL; - double farthest_dist = 0; - - Inkscape::NodePath::Node *closest_unselected = NULL; - double closest_dist = NR_HUGE; - - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - if (node == n) - continue; - if (node->selected) { - if (Geom::L2(node->pos - n->pos) > farthest_dist) { - farthest_dist = Geom::L2(node->pos - n->pos); - farthest_selected = node; - } - } else { - if (Geom::L2(node->pos - n->pos) < closest_dist) { - closest_dist = Geom::L2(node->pos - n->pos); - closest_unselected = node; - } - } - } - } - - if (grow > 0) { - if (closest_unselected) { - sp_nodepath_node_select(closest_unselected, TRUE, TRUE); - } - } else { - if (farthest_selected) { - sp_nodepath_node_select(farthest_selected, TRUE, FALSE); - } - } -} - - -/** -\brief Saves all nodes' and handles' current positions in their origin members -*/ -void -sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath) -{ - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data; - n->origin = n->pos; - n->p.origin = n->p.pos; - n->n.origin = n->n.pos; - } - } -} - -/** -\brief Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes -*/ -GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath) -{ - GList *r = NULL; - if (nodepath->selected) { - guint i = 0; - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - i++; - if (node->selected) { - r = g_list_append(r, GINT_TO_POINTER(i)); - } - } - } - } - return r; -} - -/** -\brief Restores selection by selecting nodes whose positions are in the list -*/ -void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r) -{ - sp_nodepath_deselect(nodepath); - - guint i = 0; - for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; - for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { - Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; - i++; - if (g_list_find(r, GINT_TO_POINTER(i))) { - sp_nodepath_node_select(node, TRUE, TRUE); - } - } - } -} - - -/** -\brief Adjusts handle according to node type and line code. -*/ -static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust) -{ - g_assert(node); - - // nothing to do for auto nodes (sp_node_adjust_handles() does the job) - if (node->type == Inkscape::NodePath::NODE_AUTO) - return; - - Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust); - Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me); - - // nothing to do if we are an end node - if (me->other == NULL) return; - if (other->other == NULL) return; - - // nothing to do if we are a cusp node - if (node->type == Inkscape::NodePath::NODE_CUSP) return; - - // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust) - NRPathcode mecode; - if (which_adjust == 1) { - mecode = (NRPathcode)me->other->code; - } else { - mecode = (NRPathcode)node->code; - } - if (mecode == NR_LINETO) return; - - if (sp_node_side_is_line(node, other)) { - // other is a line, and we are either smooth or symm - Inkscape::NodePath::Node *othernode = other->other; - double len = Geom::L2(me->pos - node->pos); - Geom::Point delta = node->pos - othernode->pos; - double linelen = Geom::L2(delta); - if (linelen < 1e-18) - return; - me->pos = node->pos + (len / linelen)*delta; - return; - } - - if (node->type == Inkscape::NodePath::NODE_SYMM) { - // symmetrize - me->pos = 2 * node->pos - other->pos; - return; - } else { - // smoothify - double len = Geom::L2(me->pos - node->pos); - Geom::Point delta = other->pos - node->pos; - double otherlen = Geom::L2(delta); - if (otherlen < 1e-18) return; - me->pos = node->pos - (len / otherlen) * delta; - } -} - -/** - \brief Adjusts both handles according to node type and line code - */ -static void sp_node_adjust_handles(Inkscape::NodePath::Node *node) -{ - g_assert(node); - - if (node->type == Inkscape::NodePath::NODE_CUSP) return; - - /* we are either smooth or symm */ - - if (node->p.other == NULL) return; - if (node->n.other == NULL) return; - - if (node->type == Inkscape::NodePath::NODE_AUTO) { - sp_node_adjust_handles_auto(node); - return; - } - - if (sp_node_side_is_line(node, &node->p)) { - sp_node_adjust_handle(node, 1); - return; - } - - if (sp_node_side_is_line(node, &node->n)) { - sp_node_adjust_handle(node, -1); - return; - } - - /* both are curves */ - Geom::Point const delta( node->n.pos - node->p.pos ); - - if (node->type == Inkscape::NodePath::NODE_SYMM) { - node->p.pos = node->pos - delta / 2; - node->n.pos = node->pos + delta / 2; - return; - } - - /* We are smooth */ - double plen = Geom::L2(node->p.pos - node->pos); - if (plen < 1e-18) return; - double nlen = Geom::L2(node->n.pos - node->pos); - if (nlen < 1e-18) return; - node->p.pos = node->pos - (plen / (plen + nlen)) * delta; - node->n.pos = node->pos + (nlen / (plen + nlen)) * delta; -} - -static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node) -{ - if (node->p.other == NULL || node->n.other == NULL) { - node->p.pos = node->pos; - node->n.pos = node->pos; - return; - } - - Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos); - Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos); - - double norm_leg_prev = Geom::L2(leg_prev); - double norm_leg_next = Geom::L2(leg_next); - - Geom::Point delta; - if (norm_leg_next > 0.0) { - delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev; - delta.normalize(); - } - - node->p.pos = node->pos - norm_leg_prev / 3 * delta; - node->n.pos = node->pos + norm_leg_next / 3 * delta; -} - -/** - * Node event callback. - */ -static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n) -{ - gboolean ret = FALSE; - switch (event->type) { - case GDK_ENTER_NOTIFY: - Inkscape::NodePath::Path::active_node = n; - break; - case GDK_LEAVE_NOTIFY: - Inkscape::NodePath::Path::active_node = NULL; - break; - case GDK_SCROLL: - if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1); - break; - case GDK_SCROLL_DOWN: - nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1); - break; - default: - break; - } - ret = TRUE; - } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially - switch (event->scroll.direction) { - case GDK_SCROLL_UP: - nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1); - break; - case GDK_SCROLL_DOWN: - nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1); - break; - default: - break; - } - ret = TRUE; - } - break; - case GDK_KEY_PRESS: - switch (get_group0_keyval (&event->key)) { - case GDK_space: - if (event->key.state & GDK_BUTTON1_MASK) { - Inkscape::NodePath::Path *nodepath = n->subpath->nodepath; - stamp_repr(nodepath); - ret = TRUE; - } - break; - case GDK_Page_Up: - if (event->key.state & GDK_CONTROL_MASK) { - nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1); - } else { - nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1); - } - break; - case GDK_Page_Down: - if (event->key.state & GDK_CONTROL_MASK) { - nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1); - } else { - nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1); - } - break; - default: - break; - } - break; - default: - break; - } - - return ret; -} - -/** - * Handle keypress on node; directly called. - */ -gboolean node_key(GdkEvent *event) -{ - Inkscape::NodePath::Path *np; - - // there is no way to verify nodes so set active_node to nil when deleting!! - if (Inkscape::NodePath::Path::active_node == NULL) return FALSE; - - if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) { - gint ret = FALSE; - switch (get_group0_keyval (&event->key)) { - /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts! - case GDK_BackSpace: - np = Inkscape::NodePath::Path::active_node->subpath->nodepath; - sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node); - sp_nodepath_update_repr(np, _("Delete node")); - Inkscape::NodePath::Path::active_node = NULL; - ret = TRUE; - break; - case GDK_c: - sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP); - ret = TRUE; - break; - case GDK_s: - sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH); - ret = TRUE; - break; - case GDK_a: - sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO); - ret = TRUE; - break; - case GDK_y: - sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM); - ret = TRUE; - break; - case GDK_b: - sp_nodepath_node_break(Inkscape::NodePath::Path::active_node); - ret = TRUE; - break; - } - return ret; - } - return FALSE; -} - -/** - * Mouseclick on node callback. - */ -static void node_clicked(SPKnot */*knot*/, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - if (state & GDK_CONTROL_MASK) { - Inkscape::NodePath::Path *nodepath = n->subpath->nodepath; - - if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type - if (n->type == Inkscape::NodePath::NODE_CUSP) { - sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH); - } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) { - sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM); - } else if (n->type == Inkscape::NodePath::NODE_SYMM) { - sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO); - } else { - sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP); - } - sp_nodepath_update_repr(nodepath, _("Change node type")); - sp_nodepath_update_statusbar(nodepath); - - } else { //ctrl+alt+click: delete node - GList *node_to_delete = NULL; - node_to_delete = g_list_append(node_to_delete, n); - sp_node_delete_preserve(node_to_delete); - } - - } else { - sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE); - } -} - -/** - * Mouse grabbed node callback. - */ -static void node_grabbed(SPKnot *knot, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - if (!n->selected) { - sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE); - } - - n->is_dragging = true; - // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping) - n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin; - - sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5); - - sp_nodepath_remember_origins (n->subpath->nodepath); -} - -/** - * Mouse ungrabbed node callback. - */ -static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - n->dragging_out = NULL; - n->is_dragging = false; - n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE); - sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas); - - sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes")); -} - -/** - * The point on a line, given by its angle, closest to the given point. - * \param p A point. - * \param a Angle of the line; it is assumed to go through coordinate origin. - * \param closest Pointer to the point struct where the result is stored. - * \todo FIXME: use dot product perhaps? - */ -static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest) -{ - if (a == HUGE_VAL) { // vertical - *closest = Geom::Point(0, (*p)[Geom::Y]); - } else { - (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1); - (*closest)[Geom::Y] = a * (*closest)[Geom::X]; - } -} - -/** - * Distance from the point to a line given by its angle. - * \param p A point. - * \param a Angle of the line; it is assumed to go through coordinate origin. - */ -static double point_line_distance(Geom::Point *p, double a) -{ - Geom::Point c; - point_line_closest(p, a, &c); - return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y])); -} - -/** - * Callback for node "request" signal. - * \todo fixme: This goes to "moved" event? (lauris) - */ -static gboolean -node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data) -{ - double yn, xn, yp, xp; - double an, ap, na, pa; - double d_an, d_ap, d_na, d_pa; - gboolean collinear = FALSE; - Geom::Point c; - Geom::Point pr; - - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - n->subpath->nodepath->desktop->snapindicator->remove_snaptarget(); - - // If either (Shift and some handle retracted), or (we're already dragging out a handle) - if ( (!n->subpath->nodepath->straight_path) && - ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) - || n->dragging_out ) ) - { - Geom::Point mouse = p; - - if (!n->dragging_out) { - // This is the first drag-out event; find out which handle to drag out - double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL); - double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL); - - if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node? - return FALSE; - - Inkscape::NodePath::NodeSide *opposite; - if (appr_p > appr_n) { // closer to p - n->dragging_out = &n->p; - opposite = &n->n; - n->code = NR_CURVETO; - } else if (appr_p < appr_n) { // closer to n - n->dragging_out = &n->n; - opposite = &n->p; - n->n.other->code = NR_CURVETO; - } else { // p and n nodes are the same - if (n->n.pos != n->pos) { // n handle already dragged, drag p - n->dragging_out = &n->p; - opposite = &n->n; - n->code = NR_CURVETO; - } else if (n->p.pos != n->pos) { // p handle already dragged, drag n - n->dragging_out = &n->n; - opposite = &n->p; - n->n.other->code = NR_CURVETO; - } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other - double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - p) : -HUGE_VAL); - double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - p) : -HUGE_VAL); - if (appr_other_p > appr_other_n) { // closer to other's p handle - n->dragging_out = &n->n; - opposite = &n->p; - n->n.other->code = NR_CURVETO; - } else { // closer to other's n handle - n->dragging_out = &n->p; - opposite = &n->n; - n->code = NR_CURVETO; - } - } - } - - // if there's another handle, make sure the one we drag out starts parallel to it - if (opposite->pos != n->pos) { - mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos); - } - - // knots might not be created yet! - sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out); - sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite); - } - - // pass this on to the handle-moved callback - node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n); - sp_node_update_handles(n); - return TRUE; - } - - if (state & GDK_CONTROL_MASK) { // constrained motion - - // calculate relative distances of handles - // n handle: - yn = n->n.pos[Geom::Y] - n->pos[Geom::Y]; - xn = n->n.pos[Geom::X] - n->pos[Geom::X]; - // if there's no n handle (straight line), see if we can use the direction to the next point on path - if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) { - if (n->n.other) { // if there is the next point - if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either - yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag - xn = n->n.other->origin[Geom::X] - n->origin[Geom::X]; - } - } - if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi - if (yn < 0) { xn = -xn; yn = -yn; } - - // p handle: - yp = n->p.pos[Geom::Y] - n->pos[Geom::Y]; - xp = n->p.pos[Geom::X] - n->pos[Geom::X]; - // if there's no p handle (straight line), see if we can use the direction to the prev point on path - if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) { - if (n->p.other) { - if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6) - yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y]; - xp = n->p.other->origin[Geom::X] - n->origin[Geom::X]; - } - } - if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi - if (yp < 0) { xp = -xp; yp = -yp; } - - if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) { - // sliding on handles, only if at least one of the handles is non-vertical - // (otherwise it's the same as ctrl+drag anyway) - - // calculate angles of the handles - if (xn == 0) { - if (yn == 0) { // no handle, consider it the continuation of the other one - an = 0; - collinear = TRUE; - } - else an = 0; // vertical; set the angle to horizontal - } else an = yn/xn; - - if (xp == 0) { - if (yp == 0) { // no handle, consider it the continuation of the other one - ap = an; - } - else ap = 0; // vertical; set the angle to horizontal - } else ap = yp/xp; - - if (collinear) an = ap; - - // angles of the perpendiculars; HUGE_VAL means vertical - if (an == 0) na = HUGE_VAL; else na = -1/an; - if (ap == 0) pa = HUGE_VAL; else pa = -1/ap; - - // mouse point relative to the node's original pos - pr = p - n->origin; - - // distances to the four lines (two handles and two perpendiculars) - d_an = point_line_distance(&pr, an); - d_na = point_line_distance(&pr, na); - d_ap = point_line_distance(&pr, ap); - d_pa = point_line_distance(&pr, pa); - - // find out which line is the closest, save its closest point in c - if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) { - point_line_closest(&pr, an, &c); - } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) { - point_line_closest(&pr, ap, &c); - } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) { - point_line_closest(&pr, na, &c); - } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) { - point_line_closest(&pr, pa, &c); - } - - // move the node to the closest point - sp_nodepath_selected_nodes_move(n->subpath->nodepath, - n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X], - n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], - true); - - } else { // constraining to hor/vert - - if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor - sp_nodepath_selected_nodes_move(n->subpath->nodepath, - p[Geom::X] - n->pos[Geom::X], - n->origin[Geom::Y] - n->pos[Geom::Y], - true, - true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X])); - } else { // snap to vert - sp_nodepath_selected_nodes_move(n->subpath->nodepath, - n->origin[Geom::X] - n->pos[Geom::X], - p[Geom::Y] - n->pos[Geom::Y], - true, - true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y])); - } - } - } else { // move freely - if (n->is_dragging) { - if (state & GDK_MOD1_MASK) { // sculpt - sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin); - } else { - sp_nodepath_selected_nodes_move(n->subpath->nodepath, - p[Geom::X] - n->pos[Geom::X], - p[Geom::Y] - n->pos[Geom::Y], - (state & GDK_SHIFT_MASK) == 0); - } - } - } - - n->subpath->nodepath->desktop->scroll_to_point(p); - - return TRUE; -} - -/** - * Node handle clicked callback. - */ -static void node_handle_clicked(SPKnot *knot, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - if (state & GDK_CONTROL_MASK) { // "delete" handle - if (n->p.knot == knot) { - n->p.pos = n->pos; - } else if (n->n.knot == knot) { - n->n.pos = n->pos; - } - sp_node_update_handles(n); - Inkscape::NodePath::Path *nodepath = n->subpath->nodepath; - sp_nodepath_update_repr(nodepath, _("Retract handle")); - sp_nodepath_update_statusbar(nodepath); - - } else { // just select or add to selection, depending in Shift - sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE); - } -} - -/** - * Node handle grabbed callback. - */ -static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - // convert auto -> smooth when dragging handle - if (n->type == Inkscape::NodePath::NODE_AUTO) { - n->type = Inkscape::NodePath::NODE_SMOOTH; - sp_nodepath_update_node_knot (n); - } - - if (!n->selected) { - sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE); - } - - // remember the origin point of the handle - if (n->p.knot == knot) { - n->p.origin_radial = n->p.pos - n->pos; - } else if (n->n.knot == knot) { - n->n.origin_radial = n->n.pos - n->pos; - } else { - g_assert_not_reached(); - } - - sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5); -} - -/** - * Node handle ungrabbed callback. - */ -static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - // forget origin and set knot position once more (because it can be wrong now due to restrictions) - if (n->p.knot == knot) { - n->p.origin_radial.a = 0; - sp_knot_set_position(knot, n->p.pos, state); - } else if (n->n.knot == knot) { - n->n.origin_radial.a = 0; - sp_knot_set_position(knot, n->n.pos, state); - } else { - g_assert_not_reached(); - } - - sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle")); -} - -/** - * Node handle "request" signal callback. - */ -static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - - Inkscape::NodePath::NodeSide *me, *opposite; - gint which; - if (n->p.knot == knot) { - me = &n->p; - opposite = &n->n; - which = -1; - } else if (n->n.knot == knot) { - me = &n->n; - opposite = &n->p; - which = 1; - } else { - me = opposite = NULL; - which = 0; - g_assert_not_reached(); - } - - SPDesktop *desktop = n->subpath->nodepath->desktop; - SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop, true, n->subpath->nodepath->item); - Inkscape::SnappedPoint s; - - if ((state & GDK_SHIFT_MASK) != 0) { - // We will not try to snap when the shift-key is pressed - // so remove the old snap indicator and don't wait for it to time-out - desktop->snapindicator->remove_snaptarget(); - } - - Inkscape::NodePath::Node *othernode = opposite->other; - Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); - if (othernode) { - if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) { - /* We are smooth node adjacent with line */ - Geom::Point const delta = p - n->pos; - Geom::Coord const len = Geom::L2(delta); - Inkscape::NodePath::Node *othernode = opposite->other; - Geom::Point const ndelta = n->pos - othernode->pos; - Geom::Coord const linelen = Geom::L2(ndelta); - if (len > NR_EPSILON && linelen > NR_EPSILON) { - Geom::Coord const scal = dot(delta, ndelta) / linelen; - p = n->pos + (scal / linelen) * ndelta; - } - if ((state & GDK_SHIFT_MASK) == 0) { - s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false); - } - } else { - if ((state & GDK_SHIFT_MASK) == 0) { - s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type); - } - } - } else { - if ((state & GDK_SHIFT_MASK) == 0) { - s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type); - } - } - - s.getPoint(p); - - sp_node_adjust_handle(n, -which); - - return FALSE; -} - -/** - * Node handle moved callback. - */ -static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data) -{ - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - Inkscape::NodePath::NodeSide *me; - Inkscape::NodePath::NodeSide *other; - if (n->p.knot == knot) { - me = &n->p; - other = &n->n; - } else if (n->n.knot == knot) { - me = &n->n; - other = &n->p; - } else { - me = NULL; - other = NULL; - g_assert_not_reached(); - } - - // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point - Radial rme(me->pos - n->pos); - Radial rother(other->pos - n->pos); - Radial rnew(p - n->pos); - - if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) { - int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); - /* 0 interpreted as "no snapping". */ - - // 1. Snap to the closest PI/snaps angle, starting from zero. - double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps); - - // 2. Snap to the original angle, its opposite and perpendiculars - if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length - /* The closest PI/2 angle, starting from original angle */ - double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2); - - // Snap to the closest. - a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a) - ? a_snapped - : a_ortho ); - } - - // 3. Snap to the angle of the opposite line, if any - Inkscape::NodePath::Node *othernode = other->other; - if (othernode) { - Geom::Point other_to_snap(0,0); - if (sp_node_side_is_line(n, other)) { - other_to_snap = othernode->pos - n->pos; - } else { - other_to_snap = other->pos - n->pos; - } - if (Geom::L2(other_to_snap) > 1e-3) { - Radial rother_to_snap(other_to_snap); - /* The closest PI/2 angle, starting from the angle of the opposite line segment */ - double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2); - - // Snap to the closest. - a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a) - ? a_snapped - : a_oppo ); - } - } - - rnew.a = a_snapped; - } - - if (state & GDK_MOD1_MASK) { - // lock handle length - rnew.r = me->origin_radial.r; - } - - if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK))) - && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) { - // rotate the other handle correspondingly, if both old and new angles exist and are not the same - rother.a += rnew.a - rme.a; - other->pos = Geom::Point(rother) + n->pos; - if (other->knot) { - sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos); - sp_knot_moveto(other->knot, other->pos); - } - } - - me->pos = Geom::Point(rnew) + n->pos; - sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos); - - // move knot, but without emitting the signal: - // we cannot emit a "moved" signal because we're now processing it - sp_knot_moveto(me->knot, me->pos); - - update_object(n->subpath->nodepath); - - /* status text */ - SPDesktop *desktop = n->subpath->nodepath->desktop; - if (!desktop) return; - SPEventContext *ec = desktop->event_context; - if (!ec) return; - - Inkscape::MessageContext *mc = get_message_context(ec); - - if (!mc) return; - - double degrees = 180 / M_PI * rnew.a; - if (degrees > 180) degrees -= 360; - if (degrees < -180) degrees += 360; - if (prefs->getBool("/options/compassangledisplay/value")) - degrees = angle_to_compass (degrees); - - GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric()); - - mc->setF(Inkscape::IMMEDIATE_MESSAGE, - _("<b>Node handle</b>: angle %0.2f°, length %s; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"), degrees, length->str); - - g_string_free(length, TRUE); -} - -/** - * Node handle event callback. - */ -static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n) -{ - gboolean ret = FALSE; - switch (event->type) { - case GDK_KEY_PRESS: - switch (get_group0_keyval (&event->key)) { - case GDK_space: - if (event->key.state & GDK_BUTTON1_MASK) { - Inkscape::NodePath::Path *nodepath = n->subpath->nodepath; - stamp_repr(nodepath); - ret = TRUE; - } - break; - default: - break; - } - break; - case GDK_ENTER_NOTIFY: - // we use an experimentally determined threshold that seems to work fine - if (Geom::L2(n->pos - knot->pos) < 0.75) - Inkscape::NodePath::Path::active_node = n; - break; - case GDK_LEAVE_NOTIFY: - // we use an experimentally determined threshold that seems to work fine - if (Geom::L2(n->pos - knot->pos) < 0.75) - Inkscape::NodePath::Path::active_node = NULL; - break; - default: - break; - } - - return ret; -} - -static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle, - Radial &rme, Radial &rother, gboolean const both) -{ - rme.a += angle; - if ( both - || ( n.type == Inkscape::NodePath::NODE_SMOOTH ) - || ( n.type == Inkscape::NodePath::NODE_SYMM ) ) - { - rother.a += angle; - } -} - -static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle, - Radial &rme, Radial &rother, gboolean const both) -{ - gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom(); - - gdouble r; - if ( both - || ( n.type == Inkscape::NodePath::NODE_SMOOTH ) - || ( n.type == Inkscape::NodePath::NODE_SYMM ) ) - { - r = MAX(rme.r, rother.r); - } else { - r = rme.r; - } - - gdouble const weird_angle = atan2(norm_angle, r); -/* Bulia says norm_angle is just the visible distance that the - * object's end must travel on the screen. Left as 'angle' for want of - * a better name.*/ - - rme.a += weird_angle; - if ( both - || ( n.type == Inkscape::NodePath::NODE_SMOOTH ) - || ( n.type == Inkscape::NodePath::NODE_SYMM ) ) - { - rother.a += weird_angle; - } -} - -/** - * Rotate one node. - */ -static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen) -{ - Inkscape::NodePath::NodeSide *me, *other; - bool both = false; - - double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X]; - double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X]; - - if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which" - me = &(n->p); - other = &(n->n); - } else if (!n->p.other) { - me = &(n->n); - other = &(n->p); - } else { - if (which > 0) { // right handle - if (xn > xp) { - me = &(n->n); - other = &(n->p); - } else { - me = &(n->p); - other = &(n->n); - } - } else if (which < 0){ // left handle - if (xn <= xp) { - me = &(n->n); - other = &(n->p); - } else { - me = &(n->p); - other = &(n->n); - } - } else { // both handles - me = &(n->n); - other = &(n->p); - both = true; - } - } - - Radial rme(me->pos - n->pos); - Radial rother(other->pos - n->pos); - - if (screen) { - node_rotate_one_internal_screen (*n, angle, rme, rother, both); - } else { - node_rotate_one_internal (*n, angle, rme, rother, both); - } - - me->pos = n->pos + Geom::Point(rme); - - if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) { - other->pos = n->pos + Geom::Point(rother); - } - - // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end, - // so here we just move all the knots without emitting move signals, for speed - sp_node_update_handles(n, false); -} - -/** - * Rotate selected nodes. - */ -void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen) -{ - if (!nodepath || !nodepath->selected) return; - - if (g_list_length(nodepath->selected) == 1) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data; - node_rotate_one (n, angle, which, screen); - } else { - // rotate as an object: - - Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; - Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - box.expandTo (n->pos); // contain all selected nodes - } - - gdouble rot; - if (screen) { - gdouble const zoom = nodepath->desktop->current_zoom(); - gdouble const zmove = angle / zoom; - gdouble const r = Geom::L2(box.max() - box.midpoint()); - rot = atan2(zmove, r); - } else { - rot = angle; - } - - Geom::Point rot_center; - if (Inkscape::NodePath::Path::active_node == NULL) - rot_center = box.midpoint(); - else - rot_center = Inkscape::NodePath::Path::active_node->pos; - - Geom::Matrix t = - Geom::Matrix (Geom::Translate(-rot_center)) * - Geom::Matrix (Geom::Rotate(rot)) * - Geom::Matrix (Geom::Translate(rot_center)); - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - n->pos *= t; - n->n.pos *= t; - n->p.pos *= t; - sp_node_update_handles(n, false); - } - } - - sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes")); -} - -/** - * Scale one node. - */ -static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which) -{ - bool both = false; - Inkscape::NodePath::NodeSide *me, *other; - - double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X]; - double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X]; - - if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which" - me = &(n->p); - other = &(n->n); - n->code = NR_CURVETO; - } else if (!n->p.other) { - me = &(n->n); - other = &(n->p); - if (n->n.other) - n->n.other->code = NR_CURVETO; - } else { - if (which > 0) { // right handle - if (xn > xp) { - me = &(n->n); - other = &(n->p); - if (n->n.other) - n->n.other->code = NR_CURVETO; - } else { - me = &(n->p); - other = &(n->n); - n->code = NR_CURVETO; - } - } else if (which < 0){ // left handle - if (xn <= xp) { - me = &(n->n); - other = &(n->p); - if (n->n.other) - n->n.other->code = NR_CURVETO; - } else { - me = &(n->p); - other = &(n->n); - n->code = NR_CURVETO; - } - } else { // both handles - me = &(n->n); - other = &(n->p); - both = true; - n->code = NR_CURVETO; - if (n->n.other) - n->n.other->code = NR_CURVETO; - } - } - - Radial rme(me->pos - n->pos); - Radial rother(other->pos - n->pos); - - rme.r += grow; - if (rme.r < 0) rme.r = 0; - if (rme.a == HUGE_VAL) { - if (me->other) { // if direction is unknown, initialize it towards the next node - Radial rme_next(me->other->pos - n->pos); - rme.a = rme_next.a; - } else { // if there's no next, initialize to 0 - rme.a = 0; - } - } - if (both || n->type == Inkscape::NodePath::NODE_SYMM) { - rother.r += grow; - if (rother.r < 0) rother.r = 0; - if (rother.a == HUGE_VAL) { - rother.a = rme.a + M_PI; - } - } - - me->pos = n->pos + Geom::Point(rme); - - if (both || n->type == Inkscape::NodePath::NODE_SYMM) { - other->pos = n->pos + Geom::Point(rother); - } - - // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end, - // so here we just move all the knots without emitting move signals, for speed - sp_node_update_handles(n, false); -} - -/** - * Scale selected nodes. - */ -void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which) -{ - if (!nodepath || !nodepath->selected) return; - - if (g_list_length(nodepath->selected) == 1) { - // scale handles of the single selected node - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data; - node_scale_one (n, grow, which); - } else { - // scale nodes as an "object": - - Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; - Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - box.expandTo (n->pos); // contain all selected nodes - } - - if ( Geom::are_near(box.maxExtent(), 0) ) { - SPEventContext *ec = nodepath->desktop->event_context; - if (!ec) return; - Inkscape::MessageContext *mc = get_message_context(ec); - if (!mc) return; - mc->setF(Inkscape::WARNING_MESSAGE, - _("Cannot scale nodes when all are at the same location.")); - return; - } - double scale = (box.maxExtent() + grow)/box.maxExtent(); - - - Geom::Point scale_center; - if (Inkscape::NodePath::Path::active_node == NULL) - scale_center = box.midpoint(); - else - scale_center = Inkscape::NodePath::Path::active_node->pos; - - Geom::Matrix t = - Geom::Translate(-scale_center) * - Geom::Scale(scale, scale) * - Geom::Translate(scale_center); - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - n->pos *= t; - n->n.pos *= t; - n->p.pos *= t; - sp_node_update_handles(n, false); - } - } - - sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes")); -} - -void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which) -{ - if (!nodepath) return; - sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which); -} - -/** - * Flip selected nodes horizontally/vertically. - */ -void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center) -{ - if (!nodepath || !nodepath->selected) return; - - if (g_list_length(nodepath->selected) == 1 && !center) { - // flip handles of the single selected node - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data; - double temp = n->p.pos[axis]; - n->p.pos[axis] = n->n.pos[axis]; - n->n.pos[axis] = temp; - sp_node_update_handles(n, false); - } else { - // scale nodes as an "object": - - Geom::Rect box = sp_node_selected_bbox (nodepath); - if (!center) { - center = box.midpoint(); - } - Geom::Matrix t = - Geom::Matrix (Geom::Translate(- *center)) * - Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) * - Geom::Matrix (Geom::Translate(*center)); - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - n->pos *= t; - n->n.pos *= t; - n->p.pos *= t; - sp_node_update_handles(n, false); - } - } - - sp_nodepath_update_repr(nodepath, _("Flip nodes")); -} - -Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath) -{ - g_assert (nodepath->selected); - - Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; - Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node - for (GList *l = nodepath->selected; l != NULL; l = l->next) { - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - box.expandTo (n->pos); // contain all selected nodes - } - return box; -} - -//----------------------------------------------- -/** - * Return new subpath under given nodepath. - */ -static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath) -{ - g_assert(nodepath); - g_assert(nodepath->desktop); - - Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1); - - s->nodepath = nodepath; - s->closed = FALSE; - s->nodes = NULL; - s->first = NULL; - s->last = NULL; - - // using prepend here saves up to 10% of time on paths with many subpaths, but requires that - // the caller reverses the list after it's ready (this is done in sp_nodepath_new) - nodepath->subpaths = g_list_prepend (nodepath->subpaths, s); - - return s; -} - -/** - * Destroy nodes in subpath, then subpath itself. - */ -static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath) -{ - g_assert(subpath); - g_assert(subpath->nodepath); - g_assert(g_list_find(subpath->nodepath->subpaths, subpath)); - - while (subpath->nodes) { - sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data); - } - - subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath); - - g_free(subpath); -} - -/** - * Link head to tail in subpath. - */ -static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp) -{ - g_assert(!sp->closed); - g_assert(sp->last != sp->first); - g_assert(sp->first->code == NR_MOVETO); - - sp->closed = TRUE; - - //Link the head to the tail - sp->first->p.other = sp->last; - sp->last->n.other = sp->first; - sp->last->n.pos = sp->last->pos + (sp->first->n.pos - sp->first->pos); - sp->first = sp->last; - - //Remove the extra end node - sp_nodepath_node_destroy(sp->last->n.other); -} - -/** - * Open closed (loopy) subpath at node. - */ -static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n) -{ - g_assert(sp->closed); - g_assert(n->subpath == sp); - g_assert(sp->first == sp->last); - - /* We create new startpoint, current node will become last one */ - - Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, - &n->pos, &n->pos, &n->n.pos); - - - sp->closed = FALSE; - - //Unlink to make a head and tail - sp->first = new_path; - sp->last = n; - n->n.other = NULL; - new_path->p.other = NULL; -} - -/** - * Return new node in subpath with given properties. - * \param pos Position of node. - * \param ppos Handle position in previous direction - * \param npos Handle position in previous direction - */ -Inkscape::NodePath::Node * -sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos) -{ - g_assert(sp); - g_assert(sp->nodepath); - g_assert(sp->nodepath->desktop); - - if (nodechunk == NULL) - nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE); - - Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk); - - n->subpath = sp; - - if (type != Inkscape::NodePath::NODE_NONE) { - // use the type from sodipodi:nodetypes - n->type = type; - } else { - if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) { - // points are (almost) collinear - if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) { - // endnode, or a node with a retracted handle - n->type = Inkscape::NodePath::NODE_CUSP; - } else { - n->type = Inkscape::NodePath::NODE_SMOOTH; - } - } else { - n->type = Inkscape::NodePath::NODE_CUSP; - } - } - - n->code = code; - n->selected = FALSE; - n->pos = *pos; - n->p.pos = *ppos; - n->n.pos = *npos; - - n->dragging_out = NULL; - - Inkscape::NodePath::Node *prev; - if (next) { - //g_assert(g_list_find(sp->nodes, next)); - prev = next->p.other; - } else { - prev = sp->last; - } - - if (prev) - prev->n.other = n; - else - sp->first = n; - - if (next) - next->p.other = n; - else - sp->last = n; - - n->p.other = prev; - n->n.other = next; - - n->knot = sp_knot_new(sp->nodepath->desktop, _("<b>Node</b>: drag to edit the path; with <b>Ctrl</b> to snap to horizontal/vertical; with <b>Ctrl+Alt</b> to snap to handles' directions")); - sp_knot_set_position(n->knot, *pos, 0); - - n->knot->setAnchor (GTK_ANCHOR_CENTER); - n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI); - n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI); - - sp_nodepath_update_node_knot(n); - - g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n); - g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n); - g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n); - g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n); - g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n); - sp_knot_show(n->knot); - - // We only create handle knots and lines on demand - n->p.knot = NULL; - n->p.line = NULL; - n->n.knot = NULL; - n->n.line = NULL; - - sp->nodes = g_list_prepend(sp->nodes, n); - - return n; -} - -/** - * Destroy node and its knots, link neighbors in subpath. - */ -static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node) -{ - g_assert(node); - g_assert(node->subpath); - g_assert(SP_IS_KNOT(node->knot)); - - Inkscape::NodePath::SubPath *sp = node->subpath; - - if (node->selected) { // first, deselect - g_assert(g_list_find(node->subpath->nodepath->selected, node)); - node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node); - } - - node->subpath->nodes = g_list_remove(node->subpath->nodes, node); - - g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node); - g_object_unref(G_OBJECT(node->knot)); - - if (node->p.knot) { - g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node); - g_object_unref(G_OBJECT(node->p.knot)); - node->p.knot = NULL; - } - - if (node->n.knot) { - g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node); - g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node); - g_object_unref(G_OBJECT(node->n.knot)); - node->n.knot = NULL; - } - - if (node->p.line) - gtk_object_destroy(GTK_OBJECT(node->p.line)); - if (node->n.line) - gtk_object_destroy(GTK_OBJECT(node->n.line)); - - if (sp->nodes) { // there are others nodes on the subpath - if (sp->closed) { - if (sp->first == node) { - g_assert(sp->last == node); - sp->first = node->n.other; - sp->last = sp->first; - } - node->p.other->n.other = node->n.other; - node->n.other->p.other = node->p.other; - } else { - if (sp->first == node) { - sp->first = node->n.other; - sp->first->code = NR_MOVETO; - } - if (sp->last == node) sp->last = node->p.other; - if (node->p.other) node->p.other->n.other = node->n.other; - if (node->n.other) node->n.other->p.other = node->p.other; - } - } else { // this was the last node on subpath - sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp); - } - - g_mem_chunk_free(nodechunk, node); -} - -/** - * Returns one of the node's two sides. - * \param which Indicates which side. - * \return Pointer to previous node side if which==-1, next if which==1. - */ -static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which) -{ - g_assert(node); - Inkscape::NodePath::NodeSide * result = 0; - switch (which) { - case -1: - result = &node->p; - break; - case 1: - result = &node->n; - break; - default: - g_assert_not_reached(); - } - - return result; -} - -/** - * Return the other side of the node, given one of its sides. - */ -static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me) -{ - g_assert(node); - Inkscape::NodePath::NodeSide *result = 0; - - if (me == &node->p) { - result = &node->n; - } else if (me == &node->n) { - result = &node->p; - } else { - g_assert_not_reached(); - } - - return result; -} - -/** - * Return NRPathcode on the given side of the node. - */ -static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me) -{ - g_assert(node); - - NRPathcode result = NR_END; - if (me == &node->p) { - if (node->p.other) { - result = (NRPathcode)node->code; - } else { - result = NR_MOVETO; - } - } else if (me == &node->n) { - if (node->n.other) { - result = (NRPathcode)node->n.other->code; - } else { - result = NR_MOVETO; - } - } else { - g_assert_not_reached(); - } - - return result; -} - -/** - * Return node with the given index - */ -Inkscape::NodePath::Node * -sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index) -{ - Inkscape::NodePath::Node *e = NULL; - - if (!nodepath) { - return e; - } - - //find segment - for (GList *l = nodepath->subpaths; l ; l=l->next) { - - Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data; - int n = g_list_length(sp->nodes); - if (sp->closed) { - n++; - } - - //if the piece belongs to this subpath grab it - //otherwise move onto the next subpath - if (index < n) { - e = sp->first; - for (int i = 0; i < index; ++i) { - e = e->n.other; - } - break; - } else { - if (sp->closed) { - index -= (n+1); - } else { - index -= n; - } - } - } - - return e; -} - -/** - * Returns plain text meaning of node type. - */ -static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node) -{ - unsigned retracted = 0; - bool endnode = false; - - for (int which = -1; which <= 1; which += 2) { - Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which); - if (side->other && Geom::L2(side->pos - node->pos) < 1e-6) - retracted ++; - if (!side->other) - endnode = true; - } - - if (retracted == 0) { - if (endnode) { - // TRANSLATORS: "end" is an adjective here (NOT a verb) - return _("end node"); - } else { - switch (node->type) { - case Inkscape::NodePath::NODE_CUSP: - // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial - return _("cusp"); - case Inkscape::NodePath::NODE_SMOOTH: - // TRANSLATORS: "smooth" is an adjective here - return _("smooth"); - case Inkscape::NodePath::NODE_AUTO: - return _("auto"); - case Inkscape::NodePath::NODE_SYMM: - return _("symmetric"); - } - } - } else if (retracted == 1) { - if (endnode) { - // TRANSLATORS: "end" is an adjective here (NOT a verb) - return _("end node, handle retracted (drag with <b>Shift</b> to extend)"); - } else { - return _("one handle retracted (drag with <b>Shift</b> to extend)"); - } - } else { - return _("both handles retracted (drag with <b>Shift</b> to extend)"); - } - - return NULL; -} - -/** - * Handles content of statusbar as long as node tool is active. - */ -void -sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection -{ - gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>< ></b> to scale, <b>[ ]</b> to rotate"); - gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node"); - - gint total_nodes = sp_nodepath_get_node_count(nodepath); - gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath); - gint total_subpaths = sp_nodepath_get_subpath_count(nodepath); - gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath); - - SPDesktop *desktop = NULL; - if (nodepath) { - desktop = nodepath->desktop; - } else { - desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above - } - - SPEventContext *ec = desktop->event_context; - if (!ec) return; - - Inkscape::MessageContext *mc = get_message_context(ec); - if (!mc) return; - - inkscape_active_desktop()->emitToolSubselectionChanged(NULL); - - if (selected_nodes == 0) { - Inkscape::Selection *sel = desktop->selection; - if (!sel || sel->isEmpty()) { - mc->setF(Inkscape::NORMAL_MESSAGE, - _("Select a single object to edit its nodes or handles.")); - } else { - if (nodepath) { - mc->setF(Inkscape::NORMAL_MESSAGE, - ngettext("<b>0</b> out of <b>%i</b> node selected. <b>Click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select.", - "<b>0</b> out of <b>%i</b> nodes selected. <b>Click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select.", - total_nodes), - total_nodes); - } else { - if (g_slist_length((GSList *)sel->itemList()) == 1) { - mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it.")); - } else { - mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles.")); - } - } - } - } else if (nodepath && selected_nodes == 1) { - mc->setF(Inkscape::NORMAL_MESSAGE, - ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.", - "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.", - total_nodes), - selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one); - } else { - if (selected_subpaths > 1) { - mc->setF(Inkscape::NORMAL_MESSAGE, - ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.", - "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.", - total_nodes), - selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected); - } else { - mc->setF(Inkscape::NORMAL_MESSAGE, - ngettext("<b>%i</b> of <b>%i</b> node selected. %s.", - "<b>%i</b> of <b>%i</b> nodes selected. %s.", - total_nodes), - selected_nodes, total_nodes, when_selected); - } - } -} - -/* - * returns a *copy* of the curve of that object. - */ -SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) { - if (!object) - return NULL; - - SPCurve *curve = NULL; - if (SP_IS_PATH(object)) { - SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object)); - curve = curve_new->copy(); - } else if ( IS_LIVEPATHEFFECT(object) && key) { - const gchar *svgd = object->repr->attribute(key); - if (svgd) { - Geom::PathVector pv = sp_svg_read_pathv(svgd); - SPCurve *curve_new = new SPCurve(pv); - if (curve_new) { - curve = curve_new; // don't do curve_copy because curve_new is already only created for us! - } - } - } - - return curve; -} - -void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) { - if (!np || !np->object || !curve) - return; - - if (SP_IS_PATH(np->object)) { - if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) { - sp_path_set_original_curve(SP_PATH(np->object), curve, true, false); - } else { - sp_shape_set_curve(SP_SHAPE(np->object), curve, true); - } - } else if ( IS_LIVEPATHEFFECT(np->object) ) { - Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe(); - if (lpe) { - Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) ); - if (pathparam) { - pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG - np->object->requestModified(SP_OBJECT_MODIFIED_FLAG); - } - } - } -} - -/* -SPCanvasItem * -sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) { - return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path)); -} -*/ - - -/// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file -SPCanvasItem * -sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) { - SPCurve *flash_curve = curve->copy(); - flash_curve->transform(i2d); - SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve); - // would be nice if its color could be XORed or something, now it is invisible for red stroked objects... - // unless we also flash the nodes... - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO); - sp_canvas_item_show(canvasitem); - flash_curve->unref(); - return canvasitem; -} - -SPCanvasItem * -sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) { - if (!item || !desktop) { - return NULL; - } - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff); - - Geom::Matrix i2d = sp_item_i2d_affine(item); - - SPCurve *curve = NULL; - if (SP_IS_PATH(item)) { - curve = sp_path_get_curve_for_edit(SP_PATH(item)); - } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) { - curve = sp_shape_get_curve (SP_SHAPE(item)); - } else if ( SP_IS_TEXT(item) ) { - // do not display helperpath for text - we cannot do anything with it in Node tool anyway - // curve = SP_TEXT(item)->getNormalizedBpath(); - return NULL; - } else { - g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n"); - return NULL; - } - - SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color); - - curve->unref(); - - return helperpath; -} - - -// TODO: Merge this with sp_nodepath_make_helper_item()! -void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) { - np->show_helperpath = show; - - if (show) { - SPCurve *helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d); - if (!np->helper_path) { - //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!! - - np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO); - sp_canvas_item_move_to_z(np->helper_path, 0); - sp_canvas_item_show(np->helper_path); - } else { - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve); - } - helper_curve->unref(); - } else { - if (np->helper_path) { - GtkObject *temp = np->helper_path; - np->helper_path = NULL; - gtk_object_destroy(temp); - } - } -} - -/* sp_nodepath_make_straight_path: - * Prevents user from curving the path by dragging a segment or activating handles etc. - * The resulting path is a linear interpolation between nodal points, with only straight segments. - * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved - */ -void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) { - np->straight_path = true; - np->show_handles = false; - g_message("add code to make the path straight."); - // do sp_nodepath_convert_node_type on all nodes? - // coding tip: search for this text : "Make selected segments lines" -} - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/nodepath.h b/src/nodepath.h deleted file mode 100644 index 1dcb4527c..000000000 --- a/src/nodepath.h +++ /dev/null @@ -1,345 +0,0 @@ -/** @file - * @brief Path handler in node edit mode - */ -/* Authors: - * Lauris Kaplinski <lauris@kaplinski.com> - * - * This code is in public domain - */ - -#ifndef SEEN_SP_NODEPATH_H -#define SEEN_SP_NODEPATH_H - -#include "libnr/nr-path-code.h" -#include <glibmm/ustring.h> -#include <gdk/gdkevents.h> -#include <list> -#include <2geom/point.h> -#include <2geom/matrix.h> -#include <boost/optional.hpp> - - -struct SPCanvasItem; -class SPCurve; -struct SPItem; -class SPObject; -class SPDesktop; -class SPPath; -class SPKnot; -class LivePathEffectObject; - -namespace Inkscape { - namespace XML { - class Node; - } - - namespace LivePathEffect { - class Effect; - } -} - -typedef std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > HelperPathList; - -/** - * Radial objects are represented by an angle and a distance from - * 0,0. 0,0 is represented by a == big_num. - */ -class Radial{ - public: -/** Radius */ - double r; -/** Amplitude */ - double a; - Radial() {} - // Radial(Geom::Point const &p); // Convert a point to radial coordinates - Radial(Radial &p) : r(p.r),a(p.a) {} - // operator Geom::Point() const; - -/** - * Construct Radial from Geom::Point. - */ -Radial(Geom::Point const &p) -{ - r = Geom::L2(p); - if (r > 0) { - a = Geom::atan2 (p); - } else { - a = HUGE_VAL; //undefined - } -} - -/** - * Cast Radial to cartesian Geom::Point. - */ -operator Geom::Point() const -{ - if (a == HUGE_VAL) { - return Geom::Point(0,0); - } else { - return r*Geom::Point(cos(a), sin(a)); - } -} - -}; - -class ShapeEditor; - -namespace Inkscape { -namespace NodePath { - -/** - * The entire nodepath, containing multiple subpaths - */ -class Path; - -/** - * A subpath is a continuous chain of linked nodes - */ -class SubPath; - -/** - * One side of a node, i.e. prev or next - */ -class NodeSide; - -/** - * A node on a subpath - */ -class Node; - - -/** - * This is the lowest list item, a simple list of nodes. - */ -class SubPath { - public: -/** The parent of this subpath */ - Path * nodepath; -/** Is this path closed (no endpoints) or not?*/ - gboolean closed; -/** The nodes in this subpath. */ - GList * nodes; -/** The first node of the subpath (does not imply open/closed)*/ - Node * first; -/** The last node of the subpath */ - Node * last; -}; - - - -/** - * What kind of node is this? This is the value for the node->type - * field. NodeType indicates the degree of continuity required for - * the node. I think that the corresponding integer indicates which - * derivate is connected. (Thus 2 means that the node is continuous - * to the second derivative, i.e. has matching endpoints and tangents) - */ -typedef enum { -/** A normal node */ - NODE_NONE, -/** This node non-continuously joins two segments.*/ - NODE_CUSP, -/** This node continuously joins two segments. */ - NODE_SMOOTH, -/** This node has automatic handles. */ - NODE_AUTO, -/** This node is symmetric. */ - NODE_SYMM -} NodeType; - - - -/** - * A NodeSide is a datarecord which may be on either side (n or p) of a node, - * which describes the segment going to the next node. - */ -class NodeSide{ - public: -/** Pointer to the next node, */ - Node * other; -/** Position */ - Geom::Point pos; -/** Origin (while dragging) in radial notation */ - Radial origin_radial; -/** Origin (while dragging) in x/y notation */ - Geom::Point origin; -/** Knots are Inkscape's way of providing draggable points. This - * Knot is the point on the curve representing the control point in a - * bezier curve.*/ - SPKnot * knot; -/** What kind of rendering? */ - SPCanvasItem * line; -}; - -/** - * A node along a NodePath - */ -class Node { - public: -/** The parent subpath of this node */ - SubPath * subpath; -/** Type is selected from NodeType.*/ - guint type : 4; -/** Code refers to which ArtCode is used to represent the segment - * (which segment?).*/ - guint code : 4; -/** Boolean. Am I currently selected or not? */ - guint selected : 1; -/** */ - Geom::Point pos; -/** */ - Geom::Point origin; -/** Knots are Inkscape's way of providing draggable points. This - * Knot is the point on the curve representing the endpoint.*/ - SPKnot * knot; -/** The NodeSide in the 'next' direction */ - NodeSide n; -/** The NodeSide in the 'previous' direction */ - NodeSide p; - - /** The pointer to the nodeside which we are dragging out with Shift */ - NodeSide *dragging_out; - - /** Boolean. Am I being dragged? */ - guint is_dragging : 1; -}; - -/** - * This is a collection of subpaths which contain nodes - * - * In the following data model. Nodepaths are made up of subpaths which - * are comprised of nodes. - * - * Nodes are linked thus: - * \verbatim - n other - node -----> nodeside ------> node \endverbatim - */ -class Path { - public: - /** Constructor should private, people should create new nodepaths using sp_nodepath_new - * But for some reason I cannot make sp_nodepath_new a friend :-( - */ - Path() {}; - /** Destructor */ - ~Path(); - -/** Pointer to the current desktop, for reporting purposes */ - SPDesktop * desktop; -/** The parent path of this nodepath */ - SPObject * object; -/** The parent livepatheffect of this nodepath, if applicable */ - SPItem * item; -/** The context which created this nodepath. Important if this nodepath is deleted */ - ShapeEditor *shape_editor; -/** The subpaths which comprise this NodePath */ - GList * subpaths; -/** A list of nodes which are currently selected */ - GList * selected; -/** Transforms (userspace <---> virtual space? someone please describe ) - njh: I'd be guessing that these are item <-> desktop transforms.*/ - Geom::Matrix i2d, d2i; -/** The DOM node which describes this NodePath */ - Inkscape::XML::Node *repr; - gchar *repr_key; - gchar *repr_nodetypes_key; - //STL compliant method to get the selected nodes - void selection(std::list<Node *> &l); - - guint numSelected() {return (selected? g_list_length(selected) : 0);} - Geom::Point& singleSelectedCoords() {return (((Node *) selected->data)->pos);} - - /// draw a "sketch" of the path by using these variables - SPCanvasItem *helper_path; - SPCurve *curve; - bool show_helperpath; - guint32 helperpath_rgba; - gdouble helperpath_width; - - // the helperpaths provided by all LPEs (and their paramaters) of the current item - HelperPathList helper_path_vec; - - /// true if we changed repr, to tell this change from an external one such as from undo, simplify, or another desktop - unsigned int local_change; - - /// true if we're showing selected nodes' handles - bool show_handles; - - /// true if the path cannot contain curves, just straight lines - bool straight_path; - - /// active_node points to the node that is currently mouseovered (= NULL if - /// there isn't any); we also consider the node mouseovered if it is covered - /// by one of its handles and the latter is mouseovered - static Node *active_node; - - /// Location of mouse pointer when we started dragging, needed for snapping - Geom::Point drag_origin_mouse; - -}; - -} // namespace NodePath -} // namespace Inkscape - -enum { - SCULPT_PROFILE_LINEAR, - SCULPT_PROFILE_BELL, - SCULPT_PROFILE_ELLIPTIC -}; - -// Do function documentation in nodepath.cpp -Inkscape::NodePath::Path * sp_nodepath_new (SPDesktop * desktop, SPObject *object, bool show_handles, const char * repr_key = NULL, SPItem *item = NULL); -void sp_nodepath_deselect (Inkscape::NodePath::Path *nodepath); -void sp_nodepath_select_all (Inkscape::NodePath::Path *nodepath, bool invert); -void sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert); -void sp_nodepath_select_next (Inkscape::NodePath::Path *nodepath); -void sp_nodepath_select_prev (Inkscape::NodePath::Path *nodepath); -void sp_nodepath_select_rect (Inkscape::NodePath::Path * nodepath, Geom::Rect const &b, gboolean incremental); -GList *save_nodepath_selection (Inkscape::NodePath::Path *nodepath); -void restore_nodepath_selection (Inkscape::NodePath::Path *nodepath, GList *r); -gboolean nodepath_repr_d_changed (Inkscape::NodePath::Path * np, const char *newd); -gboolean nodepath_repr_typestr_changed (Inkscape::NodePath::Path * np, const char *newtypestr); -gboolean node_key (GdkEvent * event); -void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation); -void sp_nodepath_update_statusbar (Inkscape::NodePath::Path *nodepath); -void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis); -void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis); -void sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle); -void sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p); -void sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta); -Inkscape::NodePath::Node * sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *np, int index); -bool sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side); - -/* possibly private functions */ - -void sp_node_selected_add_node (Inkscape::NodePath::Path *nodepath); -void sp_node_selected_break (Inkscape::NodePath::Path *nodepath); -void sp_node_selected_duplicate (Inkscape::NodePath::Path *nodepath); -void sp_node_selected_join (Inkscape::NodePath::Path *nodepath); -void sp_node_selected_join_segment (Inkscape::NodePath::Path *nodepath); -void sp_node_delete_preserve (GList *nodes_to_delete); -void sp_node_selected_delete (Inkscape::NodePath::Path *nodepath); -void sp_node_selected_delete_segment (Inkscape::NodePath::Path *nodepath); -void sp_node_selected_set_type (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type); -void sp_node_selected_set_line_type (Inkscape::NodePath::Path *nodepath, NRPathcode code); -void sp_node_selected_move (Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy); -void sp_node_selected_move_screen (SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy); -void sp_node_selected_move_absolute (Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis); -Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath); -boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis); - -void sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show); -SPCanvasItem *sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color); -SPCanvasItem *sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item); -void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *nodepath, bool show); -void sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np); -void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np); - -void sp_nodepath_selected_nodes_rotate (Inkscape::NodePath::Path * nodepath, gdouble angle, int which, bool screen); - -void sp_nodepath_selected_nodes_scale (Inkscape::NodePath::Path * nodepath, gdouble grow, int which); -void sp_nodepath_selected_nodes_scale_screen (Inkscape::NodePath::Path * nodepath, gdouble grow, int which); - -void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center); - -#endif diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 6b83413e4..1d81aa7f5 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -194,7 +194,7 @@ RectKnotHolderEntityRY::knot_set(Geom::Point const &p, Geom::Point const &/*orig Geom::Point const s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed), Geom::Point(0, 1))); if (state & GDK_CONTROL_MASK) { // When holding control then rx will be kept equal to ry, - // resulting in a perfect circle (and not an ellipse) + // resulting in a perfect circle (and not an ellipse) gdouble temp = MIN(rect->height.computed, rect->width.computed) / 2.0; rect->rx.computed = rect->ry.computed = CLAMP(s[Geom::Y] - rect->y.computed, 0.0, temp); rect->ry._set = rect->rx._set = true; @@ -277,19 +277,19 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or Geom::Point p_handle(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed); if (fabs(minx) > fabs(miny)) { - // snap to horizontal or diagonal + // snap to horizontal or diagonal if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->height.computed = MAX(h_orig + minx / ratio, 0); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->height.computed = MAX(h_orig + minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->height.computed = MAX(h_orig, 0); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->height.computed = MAX(h_orig, 0); } rect->width.computed = MAX(w_orig + minx, 0); @@ -297,16 +297,16 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or // snap to vertical or diagonal if (miny != 0 && fabs(minx/miny) > 0.5 * ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->width.computed = MAX(w_orig + miny * ratio, 0); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->width.computed = MAX(w_orig + miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->width.computed = MAX(w_orig, 0); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->width.computed = MAX(w_orig, 0); } rect->height.computed = MAX(h_orig + miny, 0); @@ -316,8 +316,8 @@ RectKnotHolderEntityWH::set_internal(Geom::Point const &p, Geom::Point const &or } else { // move freely - s = snap_knot_position(p); - rect->width.computed = MAX(s[Geom::X] - rect->x.computed, 0); + s = snap_knot_position(p); + rect->width.computed = MAX(s[Geom::X] - rect->x.computed, 0); rect->height.computed = MAX(s[Geom::Y] - rect->y.computed, 0); rect->width._set = rect->height._set = true; } @@ -367,54 +367,54 @@ RectKnotHolderEntityXY::knot_set(Geom::Point const &p, Geom::Point const &origin gdouble ratio = (w_orig / h_orig); if (fabs(minx) > fabs(miny)) { - // snap to horizontal or diagonal + // snap to horizontal or diagonal if (minx != 0 && fabs(miny/minx) > 0.5 * 1/ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->y.computed = MIN(origin[Geom::Y] + minx / ratio, opposite_y); rect->height.computed = MAX(h_orig - minx / ratio, 0); } else { // closer to the horizontal, change only width, height is h_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->y.computed = MIN(origin[Geom::Y], opposite_y); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-1, 0))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->y.computed = MIN(origin[Geom::Y], opposite_y); rect->height.computed = MAX(h_orig, 0); } rect->x.computed = MIN(s[Geom::X], opposite_x); rect->width.computed = MAX(w_orig - minx, 0); } else { // snap to vertical or diagonal - if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) { + if (miny != 0 && fabs(minx/miny) > 0.5 *ratio && (SGN(minx) == SGN(miny))) { // closer to the diagonal and in same-sign quarters, change both using ratio - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(-ratio, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x.computed = MIN(origin[Geom::X] + miny * ratio, opposite_x); rect->width.computed = MAX(w_orig - miny * ratio, 0); } else { // closer to the vertical, change only height, width is w_orig - s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; - rect->x.computed = MIN(origin[Geom::X], opposite_x); + s = snap_knot_position_constrained(p, Inkscape::Snapper::ConstraintLine(p_handle, Geom::Point(0, -1))); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; + rect->x.computed = MIN(origin[Geom::X], opposite_x); rect->width.computed = MAX(w_orig, 0); } rect->y.computed = MIN(s[Geom::Y], opposite_y); - rect->height.computed = MAX(h_orig - miny, 0); + rect->height.computed = MAX(h_orig - miny, 0); } rect->width._set = rect->height._set = rect->x._set = rect->y._set = true; } else { // move freely - s = snap_knot_position(p); - minx = s[Geom::X] - origin[Geom::X]; - miny = s[Geom::Y] - origin[Geom::Y]; + s = snap_knot_position(p); + minx = s[Geom::X] - origin[Geom::X]; + miny = s[Geom::Y] - origin[Geom::Y]; - rect->x.computed = MIN(s[Geom::X], opposite_x); + rect->x.computed = MIN(s[Geom::X], opposite_x); rect->width.computed = MAX(w_orig - minx, 0); rect->y.computed = MIN(s[Geom::Y], opposite_y); rect->height.computed = MAX(h_orig - miny, 0); diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 88c260782..7d79a5e91 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -33,21 +33,12 @@ #include "helper/geom-curves.h" #include "desktop.h" -Inkscape::SnapCandidate::SnapCandidate(SPItem* item, bool clip_or_mask, Geom::Matrix additional_affine) - : item(item), clip_or_mask(clip_or_mask), additional_affine(additional_affine) -{ -} - -Inkscape::SnapCandidate::~SnapCandidate() -{ -} - Inkscape::ObjectSnapper::ObjectSnapper(SnapManager *sm, Geom::Coord const d) : Snapper(sm, d) { - _candidates = new std::vector<SnapCandidate>; - _points_to_snap_to = new std::vector<std::pair<Geom::Point, int> >; - _paths_to_snap_to = new std::vector<std::pair<Geom::PathVector*, SnapTargetType> >; + _candidates = new std::vector<SnapCandidateItem>; + _points_to_snap_to = new std::vector<Inkscape::SnapCandidatePoint>; + _paths_to_snap_to = new std::vector<Inkscape::SnapCandidatePath >; } Inkscape::ObjectSnapper::~ObjectSnapper() @@ -158,7 +149,7 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, // See if the item is within range if (bbox_to_snap_incl.intersects(*bbox_of_item)) { // This item is within snapping range, so record it as a candidate - _candidates->push_back(SnapCandidate(item, clip_or_mask, additional_affine)); + _candidates->push_back(SnapCandidateItem(item, clip_or_mask, additional_affine)); // For debugging: print the id of the candidate to the console //SPObject *obj = (SPObject*)item; //std::cout << "Snap candidate added: " << obj->id << std::endl; @@ -171,7 +162,7 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, } -void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType const &t, +void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapSourceType const &t, bool const &first_point) const { // Now, let's first collect all points to snap to. If we have a whole bunch of points to snap, @@ -183,12 +174,12 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; - bool p_is_a_node = t & Inkscape::SnapPreferences::SNAPPOINT_NODE; - bool p_is_a_bbox = t & Inkscape::SnapPreferences::SNAPPOINT_BBOX; - bool p_is_a_guide = t & Inkscape::SnapPreferences::SNAPPOINT_GUIDE; + bool p_is_a_node = t & Inkscape::SNAPSOURCE_NODE_CATEGORY; + bool p_is_a_bbox = t & Inkscape::SNAPSOURCE_BBOX_CATEGORY; + bool p_is_other = t & Inkscape::SNAPSOURCE_OTHER_CATEGORY; // A point considered for snapping should be either a node, a bbox corner or a guide. Pick only ONE! - g_assert(!((p_is_a_node && p_is_a_bbox) || (p_is_a_bbox && p_is_a_guide) || (p_is_a_node && p_is_a_guide))); + g_assert(!((p_is_a_node && p_is_a_bbox) || (p_is_a_bbox && p_is_other) || (p_is_a_node && p_is_other))); if (_snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints()) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -202,7 +193,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType _getBorderNodes(_points_to_snap_to); } - for (std::vector<SnapCandidate>::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) { + for (std::vector<SnapCandidateItem>::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) { //Geom::Matrix i2doc(Geom::identity()); SPItem *root_item = (*i).item; if (SP_IS_USE((*i).item)) { @@ -211,7 +202,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType g_return_if_fail(root_item); //Collect all nodes so we can snap to them - if (p_is_a_node || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node) || p_is_a_guide) { + if (p_is_a_node || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node) || p_is_other) { // Note: there are two ways in which intersections are considered: // Method 1: Intersections are calculated for each shape individually, for both the // snap source and snap target (see sp_shape_snappoints) @@ -236,7 +227,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType _snapmanager->snapprefs.setSnapIntersectionCS(false); } - sp_item_snappoints(root_item, true, *_points_to_snap_to, &_snapmanager->snapprefs); + sp_item_snappoints(root_item, *_points_to_snap_to, &_snapmanager->snapprefs); if (_snapmanager->snapprefs.getSnapToItemPath()) { _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); @@ -244,7 +235,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType } //Collect the bounding box's corners so we can snap to them - if (p_is_a_bbox || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_bbox) || p_is_a_guide) { + if (p_is_a_bbox || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_bbox) || p_is_other) { // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox // of the item AND the bbox of the clipping path at the same time if (!(*i).clip_or_mask) { @@ -257,15 +248,12 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType } void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const + Inkscape::SnapCandidatePoint const &p, + std::vector<SnapCandidatePoint> *unselected_nodes) const { // Iterate through all nodes, find out which one is the closest to p, and snap to it! - _collectNodes(t, source_num == 0); + _collectNodes(p.getSourceType(), p.getSourceNum() == 0); if (unselected_nodes != NULL) { _points_to_snap_to->insert(_points_to_snap_to->end(), unselected_nodes->begin(), unselected_nodes->end()); @@ -274,10 +262,10 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, SnappedPoint s; bool success = false; - for (std::vector<std::pair<Geom::Point, int> >::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { - Geom::Coord dist = Geom::L2((*k).first - p); + for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { + Geom::Coord dist = Geom::L2((*k).getPoint() - p.getPoint()); if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { - s = SnappedPoint((*k).first, source_type, source_num, static_cast<Inkscape::SnapTargetType>((*k).second), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + s = SnappedPoint((*k).getPoint(), p.getSourceType(), p.getSourceNum(), (*k).getTargetType(), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, (*k).getTargetBBox()); success = true; } } @@ -288,17 +276,16 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, } void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, Geom::Point const &guide_normal) const { // Iterate through all nodes, find out which one is the closest to this guide, and snap to it! - _collectNodes(t, true); + _collectNodes(SNAPSOURCE_GUIDE, true); // Although we won't snap to paths here (which would give us under constrained snaps) we can still snap to intersections of paths. if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { - _collectPaths(t, true); - _snapPaths(sc, t, p, SNAPSOURCE_GUIDE, 0, NULL, NULL); + _collectPaths(Inkscape::SnapCandidatePoint(p, SNAPSOURCE_GUIDE), true); + _snapPaths(sc, Inkscape::SnapCandidatePoint(p, SNAPSOURCE_GUIDE), NULL, NULL); // The paths themselves should be discarded in findBestSnap(), as we should only snap to their intersections } @@ -306,13 +293,13 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s Geom::Coord tol = getSnapperTolerance(); - for (std::vector<std::pair<Geom::Point, int> >::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { + for (std::vector<SnapCandidatePoint>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { // Project each node (*k) on the guide line (running through point p) - Geom::Point p_proj = Geom::projection((*k).first, Geom::Line(p, p + Geom::rot90(guide_normal))); - Geom::Coord dist = Geom::L2((*k).first - p_proj); // distance from node to the guide + Geom::Point p_proj = Geom::projection((*k).getPoint(), Geom::Line(p, p + Geom::rot90(guide_normal))); + Geom::Coord dist = Geom::L2((*k).getPoint() - p_proj); // distance from node to the guide Geom::Coord dist2 = Geom::L2(p - p_proj); // distance from projection of node on the guide, to the mouse location if ((dist < tol && dist2 < tol) || getSnapperAlwaysSnap()) { - s = SnappedPoint((*k).first, SNAPSOURCE_GUIDE, 0, static_cast<Inkscape::SnapTargetType>((*k).second), dist, tol, getSnapperAlwaysSnap(), true); + s = SnappedPoint((*k).getPoint(), SNAPSOURCE_GUIDE, 0, (*k).getTargetType(), dist, tol, getSnapperAlwaysSnap(), true, (*k).getTargetBBox()); sc.points.push_back(s); } } @@ -323,7 +310,7 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s * Returns index of first NR_END bpath in array. */ -void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType const &t, +void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapCandidatePoint const &p, bool const &first_point) const { // Now, let's first collect all paths to snap to. If we have a whole bunch of points to snap, @@ -335,8 +322,8 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; - bool p_is_a_node = t & Inkscape::SnapPreferences::SNAPPOINT_NODE; - bool p_is_a_guide = t & Inkscape::SnapPreferences::SNAPPOINT_GUIDE; + bool p_is_a_node = p.getSourceType() & Inkscape::SNAPSOURCE_NODE_CATEGORY; + bool p_is_other = p.getSourceType() & Inkscape::SNAPSOURCE_OTHER_CATEGORY; if (_snapmanager->snapprefs.getSnapToBBoxPath()) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -349,11 +336,11 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType if (_snapmanager->snapprefs.getSnapToPageBorder()) { Geom::PathVector *border_path = _getBorderPathv(); if (border_path != NULL) { - _paths_to_snap_to->push_back(std::make_pair(border_path, SNAPTARGET_PAGE_BORDER)); + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(border_path, SNAPTARGET_PAGE_BORDER, Geom::OptRect())); } } - for (std::vector<SnapCandidate>::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) { + for (std::vector<SnapCandidateItem>::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) { /* Transform the requested snap point to this item's coordinates */ Geom::Matrix i2doc(Geom::identity()); @@ -372,7 +359,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType //Add the item's path to snap to if (_snapmanager->snapprefs.getSnapToItemPath()) { - if (p_is_a_guide || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node)) { + if (p_is_other || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node)) { // Snapping to the path of characters is very cool, but for a large // chunk of text this will take ages! So limit snapping to text paths // containing max. 240 characters. Snapping the bbox will not be affected @@ -397,7 +384,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType if (curve) { // We will get our own copy of the path, which must be freed at some point Geom::PathVector *borderpathv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); - _paths_to_snap_to->push_back(std::make_pair(borderpathv, SNAPTARGET_PATH)); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(borderpathv, SNAPTARGET_PATH, Geom::OptRect())); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it. curve->unref(); } } @@ -406,7 +393,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType //Add the item's bounding box to snap to if (_snapmanager->snapprefs.getSnapToBBoxPath()) { - if (p_is_a_guide || !(_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) { + if (p_is_other || !(_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) { // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox // of the item AND the bbox of the clipping path at the same time if (!(*i).clip_or_mask) { @@ -414,7 +401,8 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType sp_item_invoke_bbox(root_item, rect, i2doc, TRUE, bbox_type); if (rect) { Geom::PathVector *path = _getPathvFromRect(*rect); - _paths_to_snap_to->push_back(std::make_pair(path, SNAPTARGET_BBOX_EDGE)); + rect = sp_item_bbox_desktop(root_item, bbox_type); + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(path, SNAPTARGET_BBOX_EDGE, rect)); } } } @@ -424,48 +412,40 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType } void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes, + Inkscape::SnapCandidatePoint const &p, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes, SPPath const *selected_path) const { - _collectPaths(t, source_num == 0); + _collectPaths(p, p.getSourceNum() == 0); // Now we can finally do the real snapping, using the paths collected above g_assert(_snapmanager->getDesktop() != NULL); - Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p); + Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p.getPoint()); bool const node_tool_active = _snapmanager->snapprefs.getSnapToItemPath() && selected_path != NULL; - if (source_num == 0) { + if (p.getSourceNum() == 0) { /* findCandidates() is used for snapping to both paths and nodes. It ignores the path that is * currently being edited, because that path requires special care: when snapping to nodes * only the unselected nodes of that path should be considered, and these will be passed on separately. * This path must not be ignored however when snapping to the paths, so we add it here * manually when applicable. - * - * Note that this path must be the last in line! * */ if (node_tool_active) { SPCurve *curve = curve_for_item(SP_ITEM(selected_path)); if (curve) { Geom::PathVector *pathv = pathvector_for_curve(SP_ITEM(selected_path), curve, true, true, Geom::identity(), Geom::identity()); // We will get our own copy of the path, which must be freed at some point - _paths_to_snap_to->push_back(std::make_pair(pathv, SNAPTARGET_PATH)); + _paths_to_snap_to->push_back(Inkscape::SnapCandidatePath(pathv, SNAPTARGET_PATH, Geom::OptRect(), true)); curve->unref(); } } } - for (std::vector<std::pair<Geom::PathVector*, SnapTargetType> >::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { - bool const being_edited = (node_tool_active && (*it_p) == _paths_to_snap_to->back()); + for (std::vector<Inkscape::SnapCandidatePath >::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { + bool const being_edited = node_tool_active && (*it_p).currently_being_edited; //if true then this pathvector it_pv is currently being edited in the node tool - // char * svgd = sp_svg_write_path(**it_p->first); - // std::cout << "Dumping the pathvector: " << svgd << std::endl; - - for(Geom::PathVector::iterator it_pv = (it_p->first)->begin(); it_pv != (it_p->first)->end(); ++it_pv) { + for(Geom::PathVector::iterator it_pv = (it_p->path_vector)->begin(); it_pv != (it_p->path_vector)->end(); ++it_pv) { // Find a nearest point for each curve within this path // n curves will return n time values with 0 <= t <= 1 std::vector<double> anp = (*it_pv).nearestPointPerCurve(p_doc); @@ -502,7 +482,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, if (!being_edited || (c1 && c2)) { Geom::Coord const dist = Geom::distance(sp_doc, p_doc); if (dist < getSnapperTolerance()) { - sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, source_type, source_num, it_p->second)); + sc.curves.push_back(Inkscape::SnappedCurve(sp_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve, p.getSourceType(), p.getSourceNum(), it_p->target_type, it_p->target_bbox)); } } } @@ -511,7 +491,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, } /* Returns true if point is coincident with one of the unselected nodes */ -bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::vector<std::pair<Geom::Point, int> > const *unselected_nodes) const +bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::vector<Inkscape::SnapCandidatePoint> const *unselected_nodes) const { if (unselected_nodes == NULL) { return false; @@ -521,8 +501,8 @@ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::ve return false; } - for (std::vector<std::pair<Geom::Point, int> >::const_iterator i = unselected_nodes->begin(); i != unselected_nodes->end(); i++) { - if (Geom::L2(point - (*i).first) < 1e-4) { + for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = unselected_nodes->begin(); i != unselected_nodes->end(); i++) { + if (Geom::L2(point - (*i).getPoint()) < 1e-4) { return true; } } @@ -531,19 +511,16 @@ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::ve } void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, ConstraintLine const &c) const { - _collectPaths(t, source_num == 0); + _collectPaths(p, p.getSourceNum() == 0); // Now we can finally do the real snapping, using the paths collected above g_assert(_snapmanager->getDesktop() != NULL); - Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p); + Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p.getPoint()); Geom::Point direction_vector = c.getDirection(); if (!is_zero(direction_vector)) { @@ -553,7 +530,7 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, // The intersection point of the constraint line with any path, // must lie within two points on the constraintline: p_min_on_cl and p_max_on_cl // The distance between those points is twice the snapping tolerance - Geom::Point const p_proj_on_cl = p; // projection has already been taken care of in constrainedSnap in the snapmanager; + Geom::Point const p_proj_on_cl = p.getPoint(); // projection has already been taken care of in constrainedSnap in the snapmanager; Geom::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector); Geom::Point const p_max_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector); @@ -563,9 +540,9 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, cl.appendNew<Geom::LineSegment>(p_max_on_cl); clv.push_back(cl); - for (std::vector<std::pair<Geom::PathVector*, SnapTargetType> >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { - if (k->first) { - Geom::CrossingSet cs = Geom::crossings(clv, *(k->first)); + for (std::vector<Inkscape::SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { + if (k->path_vector) { + Geom::CrossingSet cs = Geom::crossings(clv, *(k->path_vector)); if (cs.size() > 0) { // We need only the first element of cs, because cl is only a single straight linesegment // This first element contains a vector filled with crossings of cl with k->first @@ -576,7 +553,7 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, // When it's within snapping range, then return it // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1) Geom::Coord dist = Geom::L2(_snapmanager->getDesktop()->dt2doc(p_proj_on_cl) - p_inters); - SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), source_type, source_num, k->second, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), p.getSourceType(), p.getSourceNum(), k->target_type, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true, k->target_bbox); sc.points.push_back(s); } } @@ -587,22 +564,19 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, std::vector<SPItem const *> const *it, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const + std::vector<SnapCandidatePoint> *unselected_nodes) const { - if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false ) { + if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false ) { return; } /* Get a list of all the SPItems that we will try to snap to */ - if (source_num == 0) { - Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p, p); - _findCandidates(sp_document_root(_snapmanager->getDocument()), it, source_num == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); + if (p.getSourceNum() == 0) { + Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint()); + _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); } if (_snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() @@ -610,7 +584,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() || _snapmanager->snapprefs.getIncludeItemCenter()) { - _snapNodes(sc, t, p, source_type, source_num, unselected_nodes); + _snapNodes(sc, p, unselected_nodes); } if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { @@ -628,30 +602,27 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, } // else: *it->begin() might be a SPGroup, e.g. when editing a LPE of text that has been converted to a group of paths // as reported in bug #356743. In that case we can just ignore it, i.e. not snap to this item } - _snapPaths(sc, t, p, source_type, source_num, unselected_nodes, path); + _snapPaths(sc, p, unselected_nodes, path); } else { - _snapPaths(sc, t, p, source_type, source_num, NULL, NULL); + _snapPaths(sc, p, NULL, NULL); } } } void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, ConstraintLine const &c, std::vector<SPItem const *> const *it) const { - if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false) { + if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(p.getSourceType()) == false) { return; } /* Get a list of all the SPItems that we will try to snap to */ - if (source_num == 0) { - Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p, p); - _findCandidates(sp_document_root(_snapmanager->getDocument()), it, source_num == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); + if (p.getSourceNum() == 0) { + Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint()); + _findCandidates(sp_document_root(_snapmanager->getDocument()), it, p.getSourceNum() == 0, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); } // A constrained snap, is a snap in only one degree of freedom (specified by the constraint line). @@ -664,7 +635,7 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, // so we will more or less snap to them anyhow. if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { - _snapPathsConstrained(sc, t, p, source_type, 0, c); + _snapPathsConstrained(sc, p, c); } } @@ -688,7 +659,7 @@ void Inkscape::ObjectSnapper::guideFreeSnap(SnappedConstraints &sc, } _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), snap_dim, false, Geom::identity()); - _snapTranslatingGuideToNodes(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, guide_normal); + _snapTranslatingGuideToNodes(sc, p, guide_normal); } @@ -712,7 +683,7 @@ void Inkscape::ObjectSnapper::guideConstrainedSnap(SnappedConstraints &sc, } _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), snap_dim, false, Geom::identity()); - _snapTranslatingGuideToNodes(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, guide_normal); + _snapTranslatingGuideToNodes(sc, p, guide_normal); } @@ -748,8 +719,8 @@ bool Inkscape::ObjectSnapper::GuidesMightSnap() const // almost the same as This void Inkscape::ObjectSnapper::_clear_paths() const { - for (std::vector<std::pair<Geom::PathVector*, SnapTargetType> >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { - g_free(k->first); + for (std::vector<Inkscape::SnapCandidatePath >::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { + delete k->path_vector; } _paths_to_snap_to->clear(); } @@ -771,31 +742,36 @@ Geom::PathVector* Inkscape::ObjectSnapper::_getPathvFromRect(Geom::Rect const re } } -void Inkscape::ObjectSnapper::_getBorderNodes(std::vector<std::pair<Geom::Point, int> > *points) const +void Inkscape::ObjectSnapper::_getBorderNodes(std::vector<SnapCandidatePoint> *points) const { Geom::Coord w = sp_document_width(_snapmanager->getDocument()); Geom::Coord h = sp_document_height(_snapmanager->getDocument()); - points->push_back(std::make_pair(Geom::Point(0,0), SNAPTARGET_PAGE_CORNER)); - points->push_back(std::make_pair(Geom::Point(0,h), SNAPTARGET_PAGE_CORNER)); - points->push_back(std::make_pair(Geom::Point(w,h), SNAPTARGET_PAGE_CORNER)); - points->push_back(std::make_pair(Geom::Point(w,0), SNAPTARGET_PAGE_CORNER)); + points->push_back(Inkscape::SnapCandidatePoint(Geom::Point(0,0), SNAPSOURCE_UNDEFINED, SNAPTARGET_PAGE_CORNER)); + points->push_back(Inkscape::SnapCandidatePoint(Geom::Point(0,h), SNAPSOURCE_UNDEFINED, SNAPTARGET_PAGE_CORNER)); + points->push_back(Inkscape::SnapCandidatePoint(Geom::Point(w,h), SNAPSOURCE_UNDEFINED, SNAPTARGET_PAGE_CORNER)); + points->push_back(Inkscape::SnapCandidatePoint(Geom::Point(w,0), SNAPSOURCE_UNDEFINED, SNAPTARGET_PAGE_CORNER)); } -void Inkscape::getBBoxPoints(Geom::OptRect const bbox, std::vector<std::pair<Geom::Point, int> > *points, bool const isTarget, bool const includeCorners, bool const includeLineMidpoints, bool const includeObjectMidpoints) +void Inkscape::getBBoxPoints(Geom::OptRect const bbox, + std::vector<SnapCandidatePoint> *points, + bool const /*isTarget*/, + bool const includeCorners, + bool const includeLineMidpoints, + bool const includeObjectMidpoints) { if (bbox) { // collect the corners of the bounding box for ( unsigned k = 0 ; k < 4 ; k++ ) { if (includeCorners) { - points->push_back(std::make_pair((bbox->corner(k)), isTarget ? int(Inkscape::SNAPTARGET_BBOX_CORNER) : int(Inkscape::SNAPSOURCE_BBOX_CORNER))); + points->push_back(Inkscape::SnapCandidatePoint(bbox->corner(k), Inkscape::SNAPSOURCE_BBOX_CORNER, 0, Inkscape::SNAPTARGET_BBOX_CORNER, *bbox)); } // optionally, collect the midpoints of the bounding box's edges too if (includeLineMidpoints) { - points->push_back(std::make_pair((bbox->corner(k) + bbox->corner((k+1) % 4))/2, isTarget ? int(Inkscape::SNAPTARGET_BBOX_EDGE_MIDPOINT) : int(Inkscape::SNAPSOURCE_BBOX_EDGE_MIDPOINT))); + points->push_back(Inkscape::SnapCandidatePoint((bbox->corner(k) + bbox->corner((k+1) % 4))/2, Inkscape::SNAPSOURCE_BBOX_EDGE_MIDPOINT, 0, Inkscape::SNAPTARGET_BBOX_EDGE_MIDPOINT, *bbox)); } } if (includeObjectMidpoints) { - points->push_back(std::make_pair(bbox->midpoint(), isTarget ? int(Inkscape::SNAPTARGET_BBOX_MIDPOINT) : int(Inkscape::SNAPSOURCE_BBOX_MIDPOINT))); + points->push_back(Inkscape::SnapCandidatePoint(bbox->midpoint(), Inkscape::SNAPSOURCE_BBOX_MIDPOINT, 0, Inkscape::SNAPTARGET_BBOX_MIDPOINT, *bbox)); } } } diff --git a/src/object-snapper.h b/src/object-snapper.h index 2fcafb79a..caf643f73 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -17,6 +17,7 @@ #include "snapper.h" #include "sp-path.h" #include "splivarot.h" +#include "snap-candidate.h" struct SPNamedView; struct SPItem; @@ -25,23 +26,6 @@ struct SPObject; namespace Inkscape { -class SnapCandidate - -{ -public: - SnapCandidate(SPItem* item, bool clip_or_mask, Geom::Matrix _additional_affine); - ~SnapCandidate(); - - SPItem* item; // An item that is to be considered for snapping to - bool clip_or_mask; // If true, then item refers to a clipping path or a mask - - /* To find out the absolute position of a clipping path or mask, we not only need to know - * the transformation of the clipping path or mask itself, but also the transformation of - * the object to which the clip or mask is being applied; that transformation is stored here - */ - Geom::Matrix additional_affine; -}; - class ObjectSnapper : public Snapper { @@ -71,28 +55,22 @@ public: bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance void freeSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, std::vector<SPItem const *> const *it, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; + std::vector<SnapCandidatePoint> *unselected_nodes) const; void constrainedSnap(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, - SnapSourceType const &source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, ConstraintLine const &c, std::vector<SPItem const *> const *it) const; private: //store some lists of candidates, points and paths, so we don't have to rebuild them for each point we want to snap - std::vector<SnapCandidate> *_candidates; - std::vector<std::pair<Geom::Point, int> > *_points_to_snap_to; - std::vector<std::pair<Geom::PathVector*, SnapTargetType> > *_paths_to_snap_to; + std::vector<SnapCandidateItem> *_candidates; + std::vector<SnapCandidatePoint> *_points_to_snap_to; + std::vector<SnapCandidatePath > *_paths_to_snap_to; void _findCandidates(SPObject* parent, std::vector<SPItem const *> const *it, @@ -103,48 +81,38 @@ private: Geom::Matrix const additional_affine) const; void _snapNodes(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, // in desktop coordinates - SnapSourceType const &source_type, - long source_num, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes) const; // in desktop coordinates + Inkscape::SnapCandidatePoint const &p, + std::vector<SnapCandidatePoint> *unselected_nodes) const; // in desktop coordinates void _snapTranslatingGuideToNodes(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, Geom::Point const &guide_normal) const; - void _collectNodes(Inkscape::SnapPreferences::PointType const &t, + void _collectNodes(Inkscape::SnapSourceType const &t, bool const &first_point) const; void _snapPaths(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, // in desktop coordinates - SnapSourceType const &source_type, - long source_num, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes, // in desktop coordinates + Inkscape::SnapCandidatePoint const &p, // in desktop coordinates + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes, // in desktop coordinates SPPath const *selected_path) const; void _snapPathsConstrained(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, - Geom::Point const &p, // in desktop coordinates - SnapSourceType const source_type, - long source_num, + Inkscape::SnapCandidatePoint const &p, // in desktop coordinates ConstraintLine const &c) const; - bool isUnselectedNode(Geom::Point const &point, std::vector<std::pair<Geom::Point, int> > const *unselected_nodes) const; + bool isUnselectedNode(Geom::Point const &point, std::vector<Inkscape::SnapCandidatePoint> const *unselected_nodes) const; - void _collectPaths(Inkscape::SnapPreferences::PointType const &t, + void _collectPaths(Inkscape::SnapCandidatePoint const &p, bool const &first_point) const; void _clear_paths() const; Geom::PathVector* _getBorderPathv() const; Geom::PathVector* _getPathvFromRect(Geom::Rect const rect) const; - void _getBorderNodes(std::vector<std::pair<Geom::Point, int> > *points) const; + void _getBorderNodes(std::vector<SnapCandidatePoint> *points) const; }; // end of ObjectSnapper class -void getBBoxPoints(Geom::OptRect const bbox, std::vector<std::pair<Geom::Point, int> > *points, bool const isTarget, bool const includeCorners, bool const includeLineMidpoints, bool const includeObjectMidpoints); +void getBBoxPoints(Geom::OptRect const bbox, std::vector<SnapCandidatePoint> *points, bool const isTarget, bool const includeCorners, bool const includeLineMidpoints, bool const includeObjectMidpoints); } // end of namespace Inkscape diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 204c82aed..c44ab5bc6 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -132,6 +132,7 @@ sp_selected_path_combine(SPDesktop *desktop) g_slist_free(items); if (did) { + SP_OBJECT(first)->deleteObject(false); // delete the topmost. Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc()); @@ -322,8 +323,33 @@ sp_selected_path_to_curves(SPDesktop *desktop, bool interactive) } } +/** Converts the selected items to LPEItems if they are not already so; e.g. SPRects) */ +void sp_selected_to_lpeitems(SPDesktop *desktop) +{ + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + if (selection->isEmpty()) { + return; + } + + bool did = false; + + GSList *selected = g_slist_copy((GSList *) selection->itemList()); + GSList *to_select = NULL; + selection->clear(); + GSList *items = g_slist_copy(selected); + + did = sp_item_list_to_curves(items, &selected, &to_select, true); + + g_slist_free (items); + selection->setReprList(to_select); + selection->addList(selected); + g_slist_free (to_select); + g_slist_free (selected); +} + bool -sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select) +sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select, bool skip_all_lpeitems) { bool did = false; @@ -334,6 +360,13 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec SPItem *item = SP_ITEM(items->data); SPDocument *document = item->document; + if ( skip_all_lpeitems && + SP_IS_LPE_ITEM(item) && + !SP_IS_GROUP(item) ) // also convert objects in an SPGroup when skip_all_lpeitems is set. + { + continue; + } + if (SP_IS_PATH(item) && !SP_PATH(item)->original_curve) { continue; // already a path, and no path effect } diff --git a/src/path-chemistry.h b/src/path-chemistry.h index 14b2b6ffb..64d7f63af 100644 --- a/src/path-chemistry.h +++ b/src/path-chemistry.h @@ -18,9 +18,10 @@ void sp_selected_path_combine (SPDesktop *desktop); void sp_selected_path_break_apart (SPDesktop *desktop); void sp_selected_path_to_curves (SPDesktop *desktop, bool interactive = true); +void sp_selected_to_lpeitems(SPDesktop *desktop); Inkscape::XML::Node *sp_selected_item_to_curved_repr(SPItem *item, guint32 text_grouping_policy); void sp_selected_path_reverse (SPDesktop *desktop); -bool sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select); +bool sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_select, bool skip_all_lpeitems = false); #endif diff --git a/src/pen-context.cpp b/src/pen-context.cpp index 144717332..5b9f6808a 100644 --- a/src/pen-context.cpp +++ b/src/pen-context.cpp @@ -76,7 +76,7 @@ static bool pen_within_tolerance = false; static SPDrawContextClass *pen_parent_class; static int pen_next_paraxial_direction(const SPPenContext *const pc, Geom::Point const &pt, Geom::Point const &origin, guint state); -static void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state); +static void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state, bool snap); static int pen_last_paraxial_dir = 0; // last used direction in horizontal/vertical mode; 0 = horizontal, 1 = vertical @@ -298,21 +298,22 @@ sp_pen_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val) static void spdc_endpoint_snap(SPPenContext const *const pc, Geom::Point &p, guint const state) { - if ((state & GDK_CONTROL_MASK)) { //CTRL enables angular snapping + if ((state & GDK_CONTROL_MASK) && !pc->polylines_paraxial) { //CTRL enables angular snapping if (pc->npoints > 0) { spdc_endpoint_snap_rotation(pc, p, pc->p[0], state); } } else { - if (!(state & GDK_SHIFT_MASK)) { //SHIFT disables all snapping, except the angular snapping above - //After all, the user explicitely asked for angular snapping by - //pressing CTRL + // We cannot use shift here to disable snapping because the shift-key is already used + // to toggle the paraxial direction; if the user wants to disable snapping (s)he will + // have to use the %-key, the menu, or the snap toolbar + if ((pc->npoints > 0) && pc->polylines_paraxial) { + // snap constrained + pen_set_to_nearest_horiz_vert(pc, p, state, true); + } else { + // snap freely spdc_endpoint_snap_free(pc, p, state); } } - if (pc->polylines_paraxial) { - // TODO: must we avoid one of the snaps in the previous case distinction in some situations? - pen_set_to_nearest_horiz_vert(pc, p, state); - } } /** @@ -468,18 +469,18 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const case SP_PEN_CONTEXT_POINT: if (pc->npoints == 0) { - Geom::Point p; - if ((bevent.state & GDK_CONTROL_MASK) && (pc->polylines_only || pc->polylines_paraxial)) { - p = event_dt; - if (!(bevent.state & GDK_SHIFT_MASK)) { - SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); - } + Geom::Point p; + if ((bevent.state & GDK_CONTROL_MASK) && (pc->polylines_only || pc->polylines_paraxial)) { + p = event_dt; + if (!(bevent.state & GDK_SHIFT_MASK)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); + } spdc_create_single_dot(event_context, p, "/tools/freehand/pen", bevent.state); ret = TRUE; break; - } + } // TODO: Perhaps it would be nicer to rearrange the following case // distinction so that the case of a waiting LPE is treated separately @@ -507,11 +508,7 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const /* Create green anchor */ p = event_dt; - if (!pc->polylines_paraxial) { - // only snap the starting point if we're not in horizontal/vertical mode - // because otherwise it gets shifted; TODO: why do we snap here at all?? - spdc_endpoint_snap(pc, p, bevent.state); - } + spdc_endpoint_snap(pc, p, bevent.state); pc->green_anchor = sp_draw_anchor_new(pc, pc->green_curve, TRUE, p); } spdc_pen_set_initial_point(pc, p); @@ -557,20 +554,22 @@ static gint pen_handle_button_press(SPPenContext *const pc, GdkEventButton const default: break; } - } else if (bevent.button == 3 || pc->expecting_clicks_for_LPE == 1) { // when the last click for a waiting LPE occurs we want to finish the path - if (pc->npoints != 0) { - - spdc_pen_finish_segment(pc, event_dt, bevent.state); - if (pc->green_closed) { - // finishing at the start anchor, close curve - spdc_pen_finish(pc, TRUE); - } else { - // finishing at some other anchor, finish curve but not close - spdc_pen_finish(pc, FALSE); - } - - ret = TRUE; + } else if (pc->expecting_clicks_for_LPE == 1 && pc->npoints != 0) { + // when the last click for a waiting LPE occurs we want to finish the path + spdc_pen_finish_segment(pc, event_dt, bevent.state); + if (pc->green_closed) { + // finishing at the start anchor, close curve + spdc_pen_finish(pc, TRUE); + } else { + // finishing at some other anchor, finish curve but not close + spdc_pen_finish(pc, FALSE); } + + ret = TRUE; + } else if (bevent.button == 3 && pc->npoints != 0) { + // right click - finish path + spdc_pen_finish(pc, FALSE); + ret = TRUE; } if (pc->expecting_clicks_for_LPE > 0) { @@ -630,6 +629,10 @@ pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion const &mevent) spdc_endpoint_snap(pc, p, mevent.state); spdc_pen_set_subsequent_point(pc, p, true); ret = TRUE; + } else if (!sp_event_context_knot_mouseover(pc)) { + SnapManager &m = dt->namedview->snap_manager; + m.setup(dt); + m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case SP_PEN_CONTEXT_CONTROL: @@ -654,10 +657,11 @@ pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion const &mevent) if (!anchor) { /* Snap node only if not hitting anchor */ spdc_endpoint_snap(pc, p, mevent.state); + spdc_pen_set_subsequent_point(pc, p, true, mevent.state); + } else { + spdc_pen_set_subsequent_point(pc, anchor->dp, false, mevent.state); } - spdc_pen_set_subsequent_point(pc, p, !anchor, mevent.state); - if (anchor && !pc->anchor_statusbar) { pc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Click</b> or <b>click and drag</b> to close and finish the path.")); pc->anchor_statusbar = true; @@ -675,6 +679,11 @@ pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion const &mevent) pc->_message_context->clear(); pc->anchor_statusbar = false; } + if (!sp_event_context_knot_mouseover(pc)) { + SnapManager &m = dt->namedview->snap_manager; + m.setup(dt); + m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); + } } break; case SP_PEN_CONTEXT_CONTROL: @@ -696,6 +705,11 @@ pen_handle_motion_notify(SPPenContext *const pc, GdkEventMotion const &mevent) /* This is perfectly valid */ break; default: + if (!sp_event_context_knot_mouseover(pc)) { + SnapManager &m = dt->namedview->snap_manager; + m.setup(dt); + m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); + } break; } break; @@ -968,7 +982,7 @@ static gint pen_handle_key_press(SPPenContext *const pc, GdkEvent *event) { - gint ret = FALSE; + gint ret = FALSE; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gdouble const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); // in px @@ -1156,6 +1170,7 @@ pen_handle_key_press(SPPenContext *const pc, GdkEvent *event) sp_canvas_item_hide(pc->cl1); pc->state = SP_PEN_CONTEXT_POINT; spdc_pen_set_subsequent_point(pc, pt, true); + pen_last_paraxial_dir = !pen_last_paraxial_dir; ret = TRUE; } break; @@ -1241,16 +1256,20 @@ spdc_pen_set_subsequent_point(SPPenContext *const pc, Geom::Point const p, bool bool is_curve; pc->red_curve->moveto(pc->p[0]); if (pc->polylines_paraxial && !statusbar) { - // we are drawing horizontal/vertical lines and hit an anchor; draw an L-shaped path - Geom::Point intermed = p; - pen_set_to_nearest_horiz_vert(pc, intermed, status); - pc->red_curve->lineto(intermed); + // we are drawing horizontal/vertical lines and hit an anchor; + Geom::Point const origin = pc->p[0]; + // if the previous point and the anchor are not aligned either horizontally or vertically... + if ((abs(p[Geom::X] - origin[Geom::X]) > 1e-9) && (abs(p[Geom::Y] - origin[Geom::Y]) > 1e-9)) { + // ...then we should draw an L-shaped path, consisting of two paraxial segments + Geom::Point intermed = p; + pen_set_to_nearest_horiz_vert(pc, intermed, status, false); + pc->red_curve->lineto(intermed); + } pc->red_curve->lineto(p); is_curve = false; } else { // one of the 'regular' modes - if (pc->p[1] != pc->p[0]) - { + if (pc->p[1] != pc->p[0]) { pc->red_curve->curveto(pc->p[1], p, p); is_curve = true; } else { @@ -1318,6 +1337,7 @@ spdc_pen_finish_segment(SPPenContext *const pc, Geom::Point const p, guint const if (pc->polylines_paraxial) { pen_last_paraxial_dir = pen_next_paraxial_direction(pc, p, pc->p[0], state); } + ++pc->num_clicks; if (!pc->red_curve->is_empty()) { @@ -1417,7 +1437,11 @@ static int pen_next_paraxial_direction(const SPPenContext *const pc, * horizontal or vertical segment; for all subsequent mouse clicks, we use the direction * orthogonal to the last one; pressing Shift toggles the direction */ - if (pc->num_clicks == 0) { + // num_clicks is not reliable because spdc_pen_finish_segment is sometimes called too early + // (on first mouse release), in which case num_clicks immediately becomes 1. + // if (pc->num_clicks == 0) { + + if (pc->green_curve->is_empty()) { // first mouse click double dist_h = fabs(pt[Geom::X] - origin[Geom::X]); double dist_v = fabs(pt[Geom::Y] - origin[Geom::Y]); @@ -1430,18 +1454,33 @@ static int pen_next_paraxial_direction(const SPPenContext *const pc, } } -void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state) +void pen_set_to_nearest_horiz_vert(const SPPenContext *const pc, Geom::Point &pt, guint const state, bool snap) { - Geom::Point const &origin = pc->p[0]; + Geom::Point const origin = pc->p[0]; int next_dir = pen_next_paraxial_direction(pc, pt, origin, state); - if (next_dir == 0) { - // line is forced to be horizontal - pt[Geom::Y] = origin[Geom::Y]; + if (!snap) { + if (next_dir == 0) { + // line is forced to be horizontal + pt[Geom::Y] = origin[Geom::Y]; + } else { + // line is forced to be vertical + pt[Geom::X] = origin[Geom::X]; + } } else { - // line is forced to be vertical - pt[Geom::X] = origin[Geom::X]; + // Create a horizontal or vertical constraint line + Inkscape::Snapper::ConstraintLine cl(origin, next_dir ? Geom::Point(0, 1) : Geom::Point(1, 0)); + + // Snap along the constraint line; if we didn't snap then still the constraint will be applied + SnapManager &m = pc->desktop->namedview->snap_manager; + + Inkscape::Selection *selection = sp_desktop_selection (pc->desktop); + // selection->singleItem() is the item that is currently being drawn. This item will not be snapped to (to avoid self-snapping) + // TODO: Allow snapping to the stationary parts of the item, and only ignore the last segment + + m.setup(pc->desktop, true, selection->singleItem()); + m.constrainedSnapReturnByRef(pt, Inkscape::SNAPSOURCE_NODE_HANDLE, cl); } } diff --git a/src/pencil-context.cpp b/src/pencil-context.cpp index d6050ba04..9f9c187f3 100644 --- a/src/pencil-context.cpp +++ b/src/pencil-context.cpp @@ -235,6 +235,15 @@ pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &beve return TRUE; } + if (!pc->grab) { + /* Grab mouse, so release will not pass unnoticed */ + pc->grab = SP_CANVAS_ITEM(desktop->acetate); + sp_canvas_item_grab(pc->grab, ( GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK ), + NULL, bevent.time); + } + Geom::Point const button_w(bevent.x, bevent.y); /* Find desktop coordinates */ @@ -253,13 +262,13 @@ pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &beve break; default: /* Set first point of sequence */ - SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop); + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); if (bevent.state & GDK_CONTROL_MASK) { if (!(bevent.state & GDK_SHIFT_MASK)) { - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); - } + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); + } spdc_create_single_dot(event_context, p, "/tools/freehand/pencil", bevent.state); ret = true; break; @@ -270,15 +279,15 @@ pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &beve } else { if (!(bevent.state & GDK_SHIFT_MASK)) { - // This is the first click of a new curve; deselect item so that + // This is the first click of a new curve; deselect item so that // this curve is not combined with it (unless it is drawn from its // anchor, which is handled by the sibling branch above) selection->clear(); desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new path")); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); } else if (selection->singleItem() && SP_IS_PATH(selection->singleItem())) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Appending to selected path")); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_NODE_HANDLE); } } pc->sa = anchor; @@ -295,9 +304,9 @@ pencil_handle_button_press(SPPencilContext *const pc, GdkEventButton const &beve static gint pencil_handle_motion_notify(SPPencilContext *const pc, GdkEventMotion const &mevent) { - SPDesktop *const dt = pc->desktop; + SPDesktop *const dt = pc->desktop; - if ((mevent.state & GDK_CONTROL_MASK) && (mevent.state & GDK_BUTTON1_MASK)) { + if ((mevent.state & GDK_CONTROL_MASK) && (mevent.state & GDK_BUTTON1_MASK)) { // mouse was accidentally moved during Ctrl+click; // ignore the motion and create a single point pc->is_drawing = false; @@ -356,25 +365,28 @@ pencil_handle_motion_notify(SPPencilContext *const pc, GdkEventMotion const &mev /* We may be idle or already freehand */ if ( mevent.state & GDK_BUTTON1_MASK && pc->is_drawing ) { if (pc->state == SP_PENCIL_CONTEXT_IDLE) { - sp_event_context_discard_delayed_snap_event(event_context); + sp_event_context_discard_delayed_snap_event(event_context); } - pc->state = SP_PENCIL_CONTEXT_FREEHAND; + pc->state = SP_PENCIL_CONTEXT_FREEHAND; if ( !pc->sa && !pc->green_anchor ) { /* Create green anchor */ pc->green_anchor = sp_draw_anchor_new(pc, pc->green_curve, TRUE, pc->p[0]); } - /** \todo - * fixme: I am not sure whether we want to snap to anchors - * in middle of freehand (Lauris) - */ if (anchor) { p = anchor->dp; } if ( pc->npoints != 0) { // buttonpress may have happened before we entered draw context! - spdc_add_freehand_point(pc, p, mevent.state); - ret = TRUE; + if (pc->ps.size() == 0) { + // Only in freehand mode we have to add the first point also to pc->ps (apparently) + // - We cannot add this point in spdc_set_startpoint, because we only need it for freehand + // - We cannot do this in the button press handler because at that point we don't know yet + // wheter we're going into freehand mode or not + pc->ps.push_back(pc->p[0]); + } + spdc_add_freehand_point(pc, p, mevent.state); + ret = TRUE; } if (anchor && !pc->anchor_statusbar) { @@ -396,6 +408,15 @@ pencil_handle_motion_notify(SPPencilContext *const pc, GdkEventMotion const &mev pc->anchor_statusbar = false; } } + + // Show the pre-snap indicator to communicate to the user where we would snap to if he/she were to + // a) press the mousebutton to start a freehand drawing, or + // b) release the mousebutton to finish a freehand drawing + if (!sp_event_context_knot_mouseover(pc)) { + SnapManager &m = dt->namedview->snap_manager; + m.setup(dt); + m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE)); + } break; } return ret; @@ -459,7 +480,15 @@ pencil_handle_button_release(SPPencilContext *const pc, GdkEventButton const &re /// \todo fixme: Clean up what follows (Lauris) if (anchor) { p = anchor->dp; + } else { + Geom::Point p_end = p; + spdc_endpoint_snap(pc, p_end, revent.state); + if (p_end != p) { + // then we must have snapped! + spdc_add_freehand_point(pc, p_end, revent.state); + } } + pc->ea = anchor; /* Write curves to object */ diff --git a/src/pixmaps/cursor-select-d.xpm b/src/pixmaps/cursor-select-d.xpm index 5cc0e56be..53b8c0cdf 100644 --- a/src/pixmaps/cursor-select-d.xpm +++ b/src/pixmaps/cursor-select-d.xpm @@ -4,35 +4,35 @@ static char const *cursor_select_d_xpm[] = { " g None", ". g #FFFFFF", "+ g #000000", -".. ", -".+. ", -".++. ", -".+++. ", -".++++. ", -".+++++. ", -".++++++. ", -".+++++++. ", -".++++++++. ", -".+++++++++. ", -".+++++.... ", -".++.++. ", -".+. .++. ", -" . .++. ", -" .++. ++ ++ ", -" .++. +..+..+ ", -" .. ++..+..++ ", -" +.+..+..+.+ ", -" +.+..+..+.+ ", -" +.+..+..+.+ ", +"+ ", +"++ ", +"+.+ ", +"+..+ ", +"+...+ ", +"+....+ ", +"+.....+ ", +"+......+ ", +"+.......+ ", +"+........+ ", +"+.........+ ", +"+..........+ ", +"+......++++ ", +"+...+..+ ", +"+..+ +..+ ", +"+.+ +..+ ", +"++ +..+ ++ ++ ", +" +..+ +..+..+ ", +" +..+ ++..+..++ ", +" +..+ +.+..+..+.+ ", +" ++ +..+..+..+..+ ", +" +..+..+..+..+ ", +" +...........+ ", +" +...........+ ", " +.........+ ", -" +.........+ ", -" +........+ ", " +........+ ", " +......+ ", " +......+ ", " +.....+ ", " +.....+ ", " +++++++ ", -" ", -" ", " "}; diff --git a/src/pixmaps/cursor-select-m.xpm b/src/pixmaps/cursor-select-m.xpm index 4ce619bc6..beea4739a 100644 --- a/src/pixmaps/cursor-select-m.xpm +++ b/src/pixmaps/cursor-select-m.xpm @@ -4,24 +4,27 @@ static char const *cursor_select_m_xpm[] = { " g None", ". g #FFFFFF", "+ g #000000", -".. ", -".+. ", -".++. ", -".+++. ", -".++++. ", -".+++++. ", -".++++++. ", -".+++++++. ", -".++++++++. ", -".+++++++++. ", -".+++++.... ", -".++.++. ++++ ", -".+. .++. +.+..+ ", -" . .++. +..+..++ ", -" .++. +..+..+.+ ", -" .++. + +..+..+..+ ", -" .. +.++..+..+..+ ", -" +..+..+..+..+ ", +"+ ", +"++ ", +"+.+ ", +"+..+ ", +"+...+ ", +"+....+ ", +"+.....+ ", +"+......+ ", +"+.......+ ", +"+........+ ", +"+.........+ ", +"+..........+ ", +"+......++++ ", +"+...+..+ ++ ++ ", +"+..+ +..+ +..+..+ ", +"+.+ +..+ +..+..+ ", +"++ +..+ +..+..++ ", +" +..+ +..+..+.+ ", +" +..+ ++ +..+..+..+ ", +" +..+ +.++..+..+..+ ", +" ++ +..+..+..+..+ ", " +..+..+..+..+ ", " +..+..+..+..+ ", " +...........+ ", @@ -32,7 +35,4 @@ static char const *cursor_select_m_xpm[] = { " +......+ ", " +.....+ ", " +.....+ ", -" +++++++ ", -" ", -" ", -" "}; +" +++++++ "}; diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index 4e5291baf..297d19c10 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -113,7 +113,7 @@ static char const preferences_skeleton[] = " font_sample=\"AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()\"\n" " show_sample_in_list=\"1\"\n" " style=\"fill:black;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;font-style:normal;font-weight:normal;font-size:40px;\" selcue=\"1\"/>\n" -" <eventcontext id=\"nodes\" selcue=\"1\" gradientdrag=\"1\" highlight_color=\"4278190335\" pathflash_enabled=\"1\" pathflash_unselected=\"0\" pathflash_timeout=\"500\" show_handles=\"1\" show_helperpath=\"0\" sculpting_profile=\"1\" />\n" +" <eventcontext id=\"nodes\" selcue=\"1\" gradientdrag=\"1\" highlight_color=\"4278190335\" pathflash_enabled=\"1\" pathflash_unselected=\"0\" pathflash_timeout=\"500\" show_handles=\"1\" show_outline=\"0\" sculpting_profile=\"1\" single_node_transform_handles=\"0\" show_transform_handles=\"0\" live_outline=\"1\" live_objects=\"1\" />\n" " <eventcontext id=\"tweak\" selcue=\"0\" gradientdrag=\"0\" show_handles=\"0\" width=\"0.2\" force=\"0.2\" fidelity=\"0.5\" usepressure=\"1\" style=\"fill:red;stroke:none;\" usecurrent=\"0\"/>\n" " <eventcontext id=\"spray\" selcue=\"0\" gradientdrag=\"0\" show_handles=\"0\" width=\"0.2\" force=\"0.2\" fidelity=\"0.5\" usepressure=\"1\" style=\"fill:red;stroke:none;\" usecurrent=\"0\"/>\n" " <eventcontext id=\"gradient\" selcue=\"1\"/>\n" @@ -339,7 +339,8 @@ static char const preferences_skeleton[] = " empspacing=\"5\"/>\n" " </group>\n" " <group id=\"workarounds\"\n" -" colorsontop=\"0\"/>\n" +" colorsontop=\"0\"\n" +" partialdynamic=\"0\"/>\n" " <group id=\"threading\" numthreads=\"1\"/>\n" " </group>\n" "\n" diff --git a/src/preferences.cpp b/src/preferences.cpp index 39a9e4d69..315c668b4 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -11,6 +11,7 @@ */ #include <cstring> +#include <sstream> #include <glibmm/fileutils.h> #include <glibmm/i18n.h> #include <glib.h> @@ -446,6 +447,13 @@ void Preferences::setDouble(Glib::ustring const &pref_path, double value) _setRawValue(pref_path, buf); } +void Preferences::setColor(Glib::ustring const &pref_path, guint32 value) +{ + gchar buf[16]; + g_snprintf(buf, 16, "#%08x", value); + _setRawValue(pref_path, buf); +} + /** * @brief Set a string attribute of a preference * @param pref_path Path of the preference to modify @@ -732,6 +740,20 @@ Glib::ustring Preferences::_extractString(Entry const &v) return Glib::ustring(static_cast<gchar const *>(v._value)); } +guint32 Preferences::_extractColor(Entry const &v) +{ + gchar const *s = static_cast<gchar const *>(v._value); + std::istringstream hr(s); + guint32 color; + if (s[0] == '#') { + hr.ignore(1); + hr >> std::hex >> color; + } else { + hr >> color; + } + return color; +} + SPCSSAttr *Preferences::_extractStyle(Entry const &v) { SPCSSAttr *style = sp_repr_css_attr_new(); diff --git a/src/preferences.h b/src/preferences.h index a7be08009..5e1ccf9d6 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -178,6 +178,11 @@ public: inline Glib::ustring getString() const; /** + * @brief Interpret the preference as an RGBA color value. + */ + inline guint32 getColor(guint32 def) const; + + /** * @brief Interpret the preference as a CSS style. * @return A CSS style that has to be unrefed when no longer necessary. Never NULL. */ @@ -329,6 +334,10 @@ public: return getEntry(pref_path).getString(); } + guint32 getColor(Glib::ustring const &pref_path, guint32 def=0x000000ff) { + return getEntry(pref_path).getColor(def); + } + /** * @brief Retrieve a CSS style * @param pref_path Path to the retrieved preference @@ -384,6 +393,11 @@ public: void setString(Glib::ustring const &pref_path, Glib::ustring const &value); /** + * @brief Set an RGBA color value + */ + void setColor(Glib::ustring const &pref_path, guint32 value); + + /** * @brief Set a CSS style */ void setStyle(Glib::ustring const &pref_path, SPCSSAttr *style); @@ -459,6 +473,7 @@ protected: int _extractInt(Entry const &v); double _extractDouble(Entry const &v); Glib::ustring _extractString(Entry const &v); + guint32 _extractColor(Entry const &v); SPCSSAttr *_extractStyle(Entry const &v); SPCSSAttr *_extractInheritedStyle(Entry const &v); @@ -567,6 +582,15 @@ inline Glib::ustring Preferences::Entry::getString() const } } +inline guint32 Preferences::Entry::getColor(guint32 def) const +{ + if (!this->isValid()) { + return def; + } else { + return Inkscape::Preferences::get()->_extractColor(*this); + } +} + inline SPCSSAttr *Preferences::Entry::getStyle() const { if (!this->isValid()) { diff --git a/src/rect-context.cpp b/src/rect-context.cpp index b88b4b83a..7ae27c13d 100644 --- a/src/rect-context.cpp +++ b/src/rect-context.cpp @@ -122,15 +122,15 @@ static void sp_rect_context_init(SPRectContext *rect_context) static void sp_rect_context_finish(SPEventContext *ec) { SPRectContext *rc = SP_RECT_CONTEXT(ec); - SPDesktop *desktop = ec->desktop; + SPDesktop *desktop = ec->desktop; - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); - sp_rect_finish(rc); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), GDK_CURRENT_TIME); + sp_rect_finish(rc); rc->sel_changed_connection.disconnect(); if (((SPEventContextClass *) parent_class)->finish) { - ((SPEventContextClass *) parent_class)->finish(ec); - } + ((SPEventContextClass *) parent_class)->finish(ec); + } } @@ -283,7 +283,7 @@ static gint sp_rect_context_root_handler(SPEventContext *event_context, GdkEvent /* Snap center */ SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, button_dt, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); rc->center = from_2geom(button_dt); sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), @@ -317,6 +317,13 @@ static gint sp_rect_context_root_handler(SPEventContext *event_context, GdkEvent sp_rect_drag(*rc, motion_dt, event->motion.state); // this will also handle the snapping gobble_motion_events(GDK_BUTTON1_MASK); ret = TRUE; + } else if (!sp_event_context_knot_mouseover(rc)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case GDK_BUTTON_RELEASE: @@ -389,14 +396,14 @@ static gint sp_rect_context_root_handler(SPEventContext *event_context, GdkEvent break; case GDK_Escape: - if (dragging) { - dragging = false; - sp_event_context_discard_delayed_snap_event(event_context); - // if drawing, cancel, otherwise pass it up for deselecting - sp_rect_cancel(rc); - ret = TRUE; - } - break; + if (dragging) { + dragging = false; + sp_event_context_discard_delayed_snap_event(event_context); + // if drawing, cancel, otherwise pass it up for deselecting + sp_rect_cancel(rc); + ret = TRUE; + } + break; case GDK_space: if (dragging) { @@ -527,8 +534,8 @@ static void sp_rect_finish(SPRectContext *rc) if ( rc->item != NULL ) { SPRect *rect = SP_RECT(rc->item); if (rect->width.computed == 0 || rect->height.computed == 0) { - sp_rect_cancel(rc); // Don't allow the creating of zero sized rectangle, for example when the start and and point snap to the snap grid point - return; + sp_rect_cancel(rc); // Don't allow the creating of zero sized rectangle, for example when the start and and point snap to the snap grid point + return; } SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(rc); @@ -547,14 +554,14 @@ static void sp_rect_finish(SPRectContext *rc) static void sp_rect_cancel(SPRectContext *rc) { - SPDesktop *desktop = SP_EVENT_CONTEXT(rc)->desktop; + SPDesktop *desktop = SP_EVENT_CONTEXT(rc)->desktop; - sp_desktop_selection(desktop)->clear(); - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); + sp_desktop_selection(desktop)->clear(); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); if (rc->item != NULL) { - SP_OBJECT(rc->item)->deleteObject(); - rc->item = NULL; + SP_OBJECT(rc->item)->deleteObject(); + rc->item = NULL; } rc->within_tolerance = false; diff --git a/src/registrytool.cpp b/src/registrytool.cpp index 8bc9cbebf..2017f50c7 100644 --- a/src/registrytool.cpp +++ b/src/registrytool.cpp @@ -27,6 +27,7 @@ #include <windows.h> #include <string> +#include <cstdio> #include "registrytool.h" diff --git a/src/removeoverlap/removeoverlap.cpp b/src/removeoverlap.cpp index 975b4becb..975b4becb 100644 --- a/src/removeoverlap/removeoverlap.cpp +++ b/src/removeoverlap.cpp diff --git a/src/removeoverlap/removeoverlap.h b/src/removeoverlap.h index 5b16e706b..5b16e706b 100644 --- a/src/removeoverlap/removeoverlap.h +++ b/src/removeoverlap.h diff --git a/src/removeoverlap/CMakeLists.txt b/src/removeoverlap/CMakeLists.txt deleted file mode 100644 index 7f71c29dc..000000000 --- a/src/removeoverlap/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -SET(removeoverlap_SRC -removeoverlap.cpp -) -ADD_LIBRARY(removeoverlap STATIC ${removeoverlap_SRC}) -TARGET_LINK_LIBRARIES(removeoverlap -2geom ${INKSCAPE_LIBS})
\ No newline at end of file diff --git a/src/removeoverlap/Makefile_insert b/src/removeoverlap/Makefile_insert deleted file mode 100644 index d5e90ec02..000000000 --- a/src/removeoverlap/Makefile_insert +++ /dev/null @@ -1,5 +0,0 @@ -## Makefile.am fragment sourced by src/Makefile.am. - -ink_common_sources += \ - removeoverlap/removeoverlap.cpp \ - removeoverlap/removeoverlap.h diff --git a/src/removeoverlap/makefile.in b/src/removeoverlap/makefile.in deleted file mode 100644 index 34e31a9a3..000000000 --- a/src/removeoverlap/makefile.in +++ /dev/null @@ -1,17 +0,0 @@ -# Convenience stub makefile to call the real Makefile. - -@SET_MAKE@ - -OBJEXT = @OBJEXT@ - -# Explicit so that it's the default rule. -all: - cd .. && $(MAKE) removeoverlap/all - -clean %.a %.$(OBJEXT): - cd .. && $(MAKE) removeoverlap/$@ - -.PHONY: all clean - -.SUFFIXES: -.SUFFIXES: .a .$(OBJEXT) diff --git a/src/satisfied-guide-cns.cpp b/src/satisfied-guide-cns.cpp index 505e18675..dcf635989 100644 --- a/src/satisfied-guide-cns.cpp +++ b/src/satisfied-guide-cns.cpp @@ -6,14 +6,14 @@ #include <approx-equal.h> void satisfied_guide_cns(SPDesktop const &desktop, - SnapPointsWithType const &snappoints, + std::vector<Inkscape::SnapCandidatePoint> const &snappoints, std::vector<SPGuideConstraint> &cns) { SPNamedView const &nv = *sp_desktop_namedview(&desktop); for (GSList const *l = nv.guides; l != NULL; l = l->next) { SPGuide &g = *SP_GUIDE(l->data); for (unsigned int i = 0; i < snappoints.size(); ++i) { - if (approx_equal( sp_guide_distance_from_pt(&g, snappoints[i].first), 0) ) { + if (approx_equal( sp_guide_distance_from_pt(&g, snappoints[i].getPoint()), 0) ) { cns.push_back(SPGuideConstraint(&g, i)); } } diff --git a/src/satisfied-guide-cns.h b/src/satisfied-guide-cns.h index 99229f64c..7fba29161 100644 --- a/src/satisfied-guide-cns.h +++ b/src/satisfied-guide-cns.h @@ -9,7 +9,7 @@ class SPGuideConstraint; void satisfied_guide_cns(SPDesktop const &desktop, - SnapPointsWithType const &snappoints, + std::vector<Inkscape::SnapCandidatePoint> const &snappoints, std::vector<SPGuideConstraint> &cns); diff --git a/src/select-context.cpp b/src/select-context.cpp index 606934ca6..a9e1ff623 100644 --- a/src/select-context.cpp +++ b/src/select-context.cpp @@ -98,6 +98,18 @@ sp_select_context_class_init(SPSelectContextClass *klass) event_context_class->set = sp_select_context_set; event_context_class->root_handler = sp_select_context_root_handler; event_context_class->item_handler = sp_select_context_item_handler; +} + +static void +sp_select_context_init(SPSelectContext *sc) +{ + sc->dragging = FALSE; + sc->moved = FALSE; + sc->button_press_shift = false; + sc->button_press_ctrl = false; + sc->button_press_alt = false; + sc->_seltrans = NULL; + sc->_describer = NULL; // cursors in select context CursorSelectMouseover = sp_cursor_new_from_xpm(cursor_select_m_xpm , 1, 1); @@ -116,19 +128,6 @@ sp_select_context_class_init(SPSelectContextClass *klass) handles[10] = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_sw_xpm); handles[11] = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_rotate_w_xpm); handles[12] = gdk_pixbuf_new_from_xpm_data((gchar const **)handle_center_xpm); - -} - -static void -sp_select_context_init(SPSelectContext *sc) -{ - sc->dragging = FALSE; - sc->moved = FALSE; - sc->button_press_shift = false; - sc->button_press_ctrl = false; - sc->button_press_alt = false; - sc->_seltrans = NULL; - sc->_describer = NULL; } static void @@ -172,7 +171,12 @@ sp_select_context_setup(SPEventContext *ec) SPDesktop *desktop = ec->desktop; - select_context->_describer = new Inkscape::SelectionDescriber(desktop->selection, desktop->messageStack()); + select_context->_describer = new Inkscape::SelectionDescriber( + desktop->selection, + desktop->messageStack(), + _("Click selection to toggle scale/rotation handles"), + _("No objects selected. Click, Shift+click, or drag around objects to select.") + ); select_context->_seltrans = new Inkscape::SelTrans(desktop); @@ -326,6 +330,8 @@ sp_select_context_item_handler(SPEventContext *event_context, SPItem *item, GdkE sc->dragging = TRUE; sc->moved = FALSE; + gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, CursorSelectDragging); + sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5); // remember the clicked item in sc->item: @@ -361,16 +367,14 @@ sp_select_context_item_handler(SPEventContext *event_context, SPItem *item, GdkE case GDK_ENTER_NOTIFY: { - if (!desktop->isWaitingCursor()) { - GdkCursor *cursor = gdk_cursor_new(GDK_FLEUR); - gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, cursor); - gdk_cursor_destroy(cursor); + if (!desktop->isWaitingCursor() && !sc->dragging) { + gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, CursorSelectMouseover); } break; } case GDK_LEAVE_NOTIFY: - if (!desktop->isWaitingCursor()) + if (!desktop->isWaitingCursor() && !sc->dragging) gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, event_context->cursor); break; @@ -494,7 +498,8 @@ sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event) if (sc->button_press_ctrl || (sc->button_press_alt && !sc->button_press_shift && !selection->isEmpty())) { // if it's not click and ctrl or alt was pressed (the latter with some selection // but not with shift) we want to drag rather than rubberband - sc->dragging = TRUE; + sc->dragging = TRUE; + gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, CursorSelectDragging); sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 5); } @@ -594,6 +599,7 @@ sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event) } } sc->dragging = FALSE; + gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, CursorSelectMouseover); sp_event_context_discard_delayed_snap_event(event_context); sp_canvas_end_forced_full_redraws(desktop->canvas); @@ -723,9 +729,7 @@ sp_select_context_root_handler(SPEventContext *event_context, GdkEvent *event) _("<b>Alt</b>: click to select under; drag to move selected or select by touch")); // if Alt and nonempty selection, show moving cursor ("move selected"): if (alt && !selection->isEmpty() && !desktop->isWaitingCursor()) { - GdkCursor *cursor = gdk_cursor_new(GDK_FLEUR); - gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, cursor); - gdk_cursor_destroy(cursor); + gdk_window_set_cursor(GTK_WIDGET(sp_desktop_canvas(desktop))->window, CursorSelectDragging); } //*/ break; diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 31e899d8a..730467ee5 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -1,5 +1,3 @@ -#define __SP_SELECTION_CHEMISTRY_C__ - /** @file * @brief Miscellanous operations on selected items */ @@ -9,8 +7,10 @@ * MenTaLguY <mental@rydia.net> * bulia byak <buliabyak@users.sf.net> * Andrius R. <knutux@gmail.com> + * Jon A. Cruz <jon@joncruz.org> + * Martin Sucha <martin.sucha-inkscape@jts-sro.sk> * - * Copyright (C) 1999-2006 authors + * Copyright (C) 1999-2010 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information @@ -22,6 +22,10 @@ #include "selection-chemistry.h" +// TOOD fixme: This should be moved into preference repr +SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; + + #include <gtkmm/clipboard.h> #include "svg/svg.h" @@ -40,6 +44,7 @@ #include "sp-tref.h" #include "sp-flowtext.h" #include "sp-flowregion.h" +#include "sp-image.h" #include "text-editing.h" #include "text-context.h" #include "connector-context.h" @@ -61,6 +66,7 @@ #include "sp-linear-gradient-fns.h" #include "sp-pattern.h" #include "sp-radial-gradient-fns.h" +#include "gradient-context.h" #include "sp-namedview.h" #include "preferences.h" #include "sp-offset.h" @@ -86,12 +92,16 @@ #include "display/curve.h" #include "display/canvas-bpath.h" #include "inkscape-private.h" +#include "path-chemistry.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/multi-path-manipulator.h" + +#include "enums.h" +#include "sp-item-group.h" // For clippath editing #include "tools-switch.h" -#include "shape-editor.h" -#include "node-context.h" -#include "nodepath.h" +#include "ui/tool/node-tool.h" #include "ui/clipboard.h" @@ -102,6 +112,104 @@ using Geom::Y; because the layer manipulation code uses them. It should be rewritten specifically for that purpose. */ + + +namespace Inkscape { + +void SelectionHelper::selectAll(SPDesktop *dt) +{ + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + if (!nt->_multipath->empty()) { + nt->_multipath->selectSubpaths(); + return; + } + } + sp_edit_select_all(dt); +} + +void SelectionHelper::selectAllInAll(SPDesktop *dt) +{ + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_selected_nodes->selectAll(); + } else { + sp_edit_select_all_in_all_layers(dt); + } +} + +void SelectionHelper::selectNone(SPDesktop *dt) +{ + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_selected_nodes->clear(); + } else { + sp_desktop_selection(dt)->clear(); + } +} + +void SelectionHelper::invert(SPDesktop *dt) +{ + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_multipath->invertSelectionInSubpaths(); + } else { + sp_edit_invert(dt); + } +} + +void SelectionHelper::invertAllInAll(SPDesktop *dt) +{ + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_selected_nodes->invertSelection(); + } else { + sp_edit_invert_in_all_layers(dt); + } +} + +void SelectionHelper::reverse(SPDesktop *dt) +{ + // TODO make this a virtual method of event context! + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_multipath->reverseSubpaths(); + } else { + sp_selected_path_reverse(dt); + } +} + +void SelectionHelper::selectNext(SPDesktop *dt) +{ + SPEventContext *ec = dt->event_context; + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_multipath->shiftSelection(1); + } else if (tools_isactive(dt, TOOLS_GRADIENT) + && ec->_grdrag->isNonEmpty()) { + sp_gradient_context_select_next(ec); + } else { + sp_selection_item_next(dt); + } +} + +void SelectionHelper::selectPrev(SPDesktop *dt) +{ + SPEventContext *ec = dt->event_context; + if (tools_isactive(dt, TOOLS_NODES)) { + InkNodeTool *nt = static_cast<InkNodeTool*>(dt->event_context); + nt->_multipath->shiftSelection(-1); + } else if (tools_isactive(dt, TOOLS_GRADIENT) + && ec->_grdrag->isNonEmpty()) { + sp_gradient_context_select_prev(ec); + } else { + sp_selection_item_prev(dt); + } +} + +} // namespace Inkscape + + /** * Copies repr and its inherited css style elements, along with the accumulated transform 'full_t', * then prepends the copy to 'clip'. @@ -227,7 +335,7 @@ void add_ids_recursive(std::vector<const gchar *> &ids, SPObject *obj) if (!obj) return; - ids.push_back(SP_OBJECT_ID(obj)); + ids.push_back(obj->getId()); if (SP_IS_GROUP(obj)) { for (SPObject *child = sp_object_first_child(obj) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { @@ -297,7 +405,7 @@ void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone) if (!orig) // orphaned continue; for (unsigned int j = 0; j < old_ids.size(); j++) { - if (!strcmp(SP_OBJECT_ID(orig), old_ids[j])) { + if (!strcmp(orig->getId(), old_ids[j])) { // we have both orig and clone in selection, relink // std::cout << id << " old, its ori: " << SP_OBJECT_ID(orig) << "; will relink:" << new_ids[i] << " to " << new_ids[j] << "\n"; gchar *newref = g_strdup_printf("#%s", new_ids[j]); @@ -450,36 +558,14 @@ void sp_edit_invert_in_all_layers(SPDesktop *desktop) sp_edit_select_all_full(desktop, true, true); } -void sp_selection_group(SPDesktop *desktop) -{ - if (desktop == NULL) - return; - - SPDocument *doc = sp_desktop_document(desktop); - Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); - - Inkscape::Selection *selection = sp_desktop_selection(desktop); - - // Check if something is selected. - if (selection->isEmpty()) { - desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>some objects</b> to group.")); - return; - } - - GSList const *l = (GSList *) selection->reprList(); - - GSList *p = g_slist_copy((GSList *) l); - - selection->clear(); - +void sp_selection_group_impl(GSList *p, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc) { + p = g_slist_sort(p, (GCompareFunc) sp_repr_compare_position); // Remember the position and parent of the topmost object. gint topmost = ((Inkscape::XML::Node *) g_slist_last(p)->data)->position(); Inkscape::XML::Node *topmost_parent = ((Inkscape::XML::Node *) g_slist_last(p)->data)->parent(); - Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); - while (p) { Inkscape::XML::Node *current = (Inkscape::XML::Node *) p->data; @@ -533,6 +619,33 @@ void sp_selection_group(SPDesktop *desktop) // Move to the position of the topmost, reduced by the number of items deleted from topmost_parent group->setPosition(topmost + 1); +} + +void sp_selection_group(SPDesktop *desktop) +{ + if (desktop == NULL) + return; + + SPDocument *doc = sp_desktop_document(desktop); + Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc); + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + // Check if something is selected. + if (selection->isEmpty()) { + desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>some objects</b> to group.")); + return; + } + + GSList const *l = (GSList *) selection->reprList(); + + GSList *p = g_slist_copy((GSList *) l); + + selection->clear(); + + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + + sp_selection_group_impl(p, group, xml_doc, doc); sp_document_done(sp_desktop_document(desktop), SP_VERB_SELECTION_GROUP, _("Group")); @@ -573,7 +686,7 @@ void sp_selection_ungroup(SPDesktop *desktop) /* We do not allow ungrouping <svg> etc. (lauris) */ if (strcmp(SP_OBJECT_REPR(group)->name(), "svg:g") && strcmp(SP_OBJECT_REPR(group)->name(), "svg:switch")) { // keep the non-group item in the new selection - selection->add(group); + new_select = g_slist_append(new_select, group); continue; } @@ -734,7 +847,7 @@ sp_selection_raise(SPDesktop *desktop) //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS // "Raise" means "to raise an object" in the undo history - Q_("undo_action|Raise")); + Q_("undo action|Raise")); } void sp_selection_raise_to_top(SPDesktop *desktop) @@ -898,7 +1011,7 @@ sp_redo(SPDesktop *desktop, SPDocument *) void sp_selection_cut(SPDesktop *desktop) { - sp_selection_copy(); + sp_selection_copy(desktop); sp_selection_delete(desktop); } @@ -943,33 +1056,36 @@ take_style_from_item(SPItem *item) } -void sp_selection_copy() +void sp_selection_copy(SPDesktop *desktop) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - cm->copy(); + cm->copy(desktop); } void sp_selection_paste(SPDesktop *desktop, bool in_place) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - if (cm->paste(in_place)) + if (cm->paste(desktop, in_place)) { sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE, _("Paste")); + } } void sp_selection_paste_style(SPDesktop *desktop) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - if (cm->pasteStyle()) + if (cm->pasteStyle(desktop)) { sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_STYLE, _("Paste style")); + } } void sp_selection_paste_livepatheffect(SPDesktop *desktop) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - if (cm->pastePathEffect()) + if (cm->pastePathEffect(desktop)) { sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_LIVEPATHEFFECT, _("Paste live path effect")); + } } @@ -1029,17 +1145,19 @@ void sp_selection_remove_filter(SPDesktop *desktop) void sp_selection_paste_size(SPDesktop *desktop, bool apply_x, bool apply_y) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - if (cm->pasteSize(false, apply_x, apply_y)) + if (cm->pasteSize(desktop, false, apply_x, apply_y)) { sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_SIZE, _("Paste size")); + } } void sp_selection_paste_size_separately(SPDesktop *desktop, bool apply_x, bool apply_y) { Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get(); - if (cm->pasteSize(true, apply_x, apply_y)) + if (cm->pasteSize(desktop, true, apply_x, apply_y)) { sp_document_done(sp_desktop_document(desktop), SP_VERB_EDIT_PASTE_SIZE_SEPARATELY, _("Paste size separately")); + } } void sp_selection_to_next_layer(SPDesktop *dt, bool suppressDone) @@ -1739,53 +1857,52 @@ void sp_selection_next_patheffect_param(SPDesktop * dt) } } -void sp_selection_edit_clip_or_mask(SPDesktop * dt, bool clip) +/*bool has_path_recursive(SPObject *obj) { - if (!dt) return; - - Inkscape::Selection *selection = sp_desktop_selection(dt); - if ( selection && !selection->isEmpty() ) { - SPItem *item = selection->singleItem(); - if ( item ) { - SPObject *obj = NULL; - if (clip) - obj = item->clip_ref ? SP_OBJECT(item->clip_ref->getObject()) : NULL; - else - obj = item->mask_ref ? SP_OBJECT(item->mask_ref->getObject()) : NULL; - - if (obj) { - // obj is a group object, the children are the actual clippers - for ( SPObject *child = obj->children ; child ; child = child->next ) { - if ( SP_IS_ITEM(child) ) { - // If not already in nodecontext, goto it! - if (!tools_isactive(dt, TOOLS_NODES)) { - tools_switch(dt, TOOLS_NODES); - } - - ShapeEditor * shape_editor = dt->event_context->shape_editor; - // TODO: should we set the item for nodepath or knotholder or both? seems to work with both. - shape_editor->set_item(SP_ITEM(child), SH_NODEPATH); - shape_editor->set_item(SP_ITEM(child), SH_KNOTHOLDER); - Inkscape::NodePath::Path *np = shape_editor->get_nodepath(); - if (np) { - // take colors from prefs (same as used in outline mode) - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - np->helperpath_rgba = clip ? - prefs->getInt("/options/wireframecolors/clips", 0x00ff00ff) : - prefs->getInt("/options/wireframecolors/masks", 0x0000ffff); - np->helperpath_width = 1.0; - sp_nodepath_show_helperpath(np, true); - } - break; // break out of for loop after 1st encountered item - } - } - } else if (clip) { - dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("The selection has no applied clip path.")); - } else { - dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("The selection has no applied mask.")); - } + if (!obj) return false; + if (SP_IS_PATH(obj)) { + return true; + } + if (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj)) { + for (SPObject *c = obj->children; c; c = c->next) { + if (has_path_recursive(c)) return true; } } + return false; +}*/ + +void sp_selection_edit_clip_or_mask(SPDesktop * /*dt*/, bool /*clip*/) +{ + return; + /*if (!dt) return; + using namespace Inkscape::UI; + + Inkscape::Selection *selection = sp_desktop_selection(dt); + if (!selection || selection->isEmpty()) return; + + GSList const *items = selection->itemList(); + bool has_path = false; + for (GSList *i = const_cast<GSList*>(items); i; i= i->next) { + SPItem *item = SP_ITEM(i->data); + SPObject *search = clip + ? SP_OBJECT(item->clip_ref ? item->clip_ref->getObject() : NULL) + : SP_OBJECT(item->mask_ref ? item->mask_ref->getObject() : NULL); + has_path |= has_path_recursive(search); + if (has_path) break; + } + if (has_path) { + if (!tools_isactive(dt, TOOLS_NODES)) { + tools_switch(dt, TOOLS_NODES); + } + ink_node_tool_set_mode(INK_NODE_TOOL(dt->event_context), + clip ? NODE_TOOL_EDIT_CLIPPING_PATHS : NODE_TOOL_EDIT_MASKS); + } else if (clip) { + dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, + _("The selection has no applied clip path.")); + } else { + dt->messageStack()->flash(Inkscape::WARNING_MESSAGE, + _("The selection has no applied mask.")); + }*/ } @@ -2661,12 +2778,7 @@ sp_selection_create_bitmap_copy(SPDesktop *desktop) if (pb) { // Create the repr for the image Inkscape::XML::Node * repr = xml_doc->createElement("svg:image"); - { - repr->setAttribute("sodipodi:absref", filepath); - gchar *abs_base = Inkscape::XML::calc_abs_doc_base(document->base); - repr->setAttribute("xlink:href", sp_relative_path_from_path(filepath, abs_base)); - g_free(abs_base); - } + sp_embed_image(repr, pb, "image/png"); if (res == PX_PER_IN) { // for default 90 dpi, snap it to pixel grid sp_repr_set_svg_double(repr, "width", width); sp_repr_set_svg_double(repr, "height", height); @@ -2748,27 +2860,36 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la GSList *items = g_slist_copy((GSList *) selection->itemList()); items = g_slist_sort(items, (GCompareFunc) sp_object_compare_position); + + // See lp bug #542004 + selection->clear(); // create a list of duplicates GSList *mask_items = NULL; GSList *apply_to_items = NULL; GSList *items_to_delete = NULL; + GSList *items_to_select = NULL; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool topmost = prefs->getBool("/options/maskobject/topmost", true); bool remove_original = prefs->getBool("/options/maskobject/remove", true); + int grouping = prefs->getInt("/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE); if (apply_to_layer) { // all selected items are used for mask, which is applied to a layer apply_to_items = g_slist_prepend(apply_to_items, desktop->currentLayer()); - + for (GSList *i = items; i != NULL; i = i->next) { Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc); mask_items = g_slist_prepend(mask_items, dup); - if (remove_original) { - SPObject *item = SP_OBJECT(i->data); + SPObject *item = SP_OBJECT(i->data); + if (remove_original) { items_to_delete = g_slist_prepend(items_to_delete, item); } + else { + items_to_select = g_slist_prepend(items_to_select, item); + } } } else if (!topmost) { // topmost item is used as a mask, which is applied to other items in a selection @@ -2783,11 +2904,13 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la for (i = i->next; i != NULL; i = i->next) { apply_to_items = g_slist_prepend(apply_to_items, i->data); + items_to_select = g_slist_prepend(items_to_select, i->data); } } else { GSList *i = NULL; for (i = items; NULL != i->next; i = i->next) { apply_to_items = g_slist_prepend(apply_to_items, i->data); + items_to_select = g_slist_prepend(items_to_select, i->data); } Inkscape::XML::Node *dup = (SP_OBJECT_REPR(i->data))->duplicate(xml_doc); @@ -2802,6 +2925,36 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la g_slist_free(items); items = NULL; + if (apply_to_items && grouping == PREFS_MASKOBJECT_GROUPING_ALL) { + // group all those objects into one group + // and apply mask to that + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + + // make a note we should ungroup this when unsetting mask + group->setAttribute("inkscape:groupmode", "maskhelper"); + + GSList *reprs_to_group = NULL; + + for (GSList *i = apply_to_items ; NULL != i ; i = i->next) { + reprs_to_group = g_slist_prepend(reprs_to_group, SP_OBJECT_REPR(i->data)); + items_to_select = g_slist_remove(items_to_select, i->data); + } + reprs_to_group = g_slist_reverse(reprs_to_group); + + sp_selection_group_impl(reprs_to_group, group, xml_doc, doc); + + reprs_to_group = NULL; + + // apply clip/mask only to newly created group + g_slist_free(apply_to_items); + apply_to_items = NULL; + apply_to_items = g_slist_prepend(apply_to_items, doc->getObjectByRepr(group)); + + items_to_select = g_slist_prepend(items_to_select, doc->getObjectByRepr(group)); + + Inkscape::GC::release(group); + } + gchar const *attributeName = apply_clip_path ? "clip-path" : "mask"; for (GSList *i = apply_to_items; NULL != i; i = i->next) { SPItem *item = reinterpret_cast<SPItem *>(i->data); @@ -2825,7 +2978,34 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la g_slist_free(mask_items_dup); mask_items_dup = NULL; - SP_OBJECT_REPR(i->data)->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id)); + Inkscape::XML::Node *current = SP_OBJECT_REPR(i->data); + // Node to apply mask to + Inkscape::XML::Node *apply_mask_to = current; + + if (grouping == PREFS_MASKOBJECT_GROUPING_SEPARATE) { + // enclose current node in group, and apply crop/mask on that + Inkscape::XML::Node *group = xml_doc->createElement("svg:g"); + // make a note we should ungroup this when unsetting mask + group->setAttribute("inkscape:groupmode", "maskhelper"); + + Inkscape::XML::Node *spnew = current->duplicate(xml_doc); + gint position = current->position(); + items_to_select = g_slist_remove(items_to_select, item); + current->parent()->appendChild(group); + sp_repr_unparent(current); + group->appendChild(spnew); + group->setPosition(position); + + // Apply clip/mask to group instead + apply_mask_to = group; + + items_to_select = g_slist_prepend(items_to_select, doc->getObjectByRepr(group)); + Inkscape::GC::release(spnew); + Inkscape::GC::release(group); + } + + apply_mask_to->setAttribute(attributeName, g_strdup_printf("url(#%s)", mask_id)); + } g_slist_free(mask_items); @@ -2834,8 +3014,14 @@ sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_la for (GSList *i = items_to_delete; NULL != i; i = i->next) { SPObject *item = SP_OBJECT(i->data); item->deleteObject(false); + items_to_select = g_slist_remove(items_to_select, item); } g_slist_free(items_to_delete); + + items_to_select = g_slist_reverse(items_to_select); + + selection->addList(items_to_select); + g_slist_free(items_to_select); if (apply_clip_path) sp_document_done(doc, SP_VERB_OBJECT_SET_CLIPPATH, _("Set clipping path")); @@ -2859,13 +3045,23 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool remove_original = prefs->getBool("/options/maskobject/remove", true); + bool ungroup_masked = prefs->getBool("/options/maskobject/ungrouping", true); sp_document_ensure_up_to_date(doc); gchar const *attributeName = apply_clip_path ? "clip-path" : "mask"; std::map<SPObject*,SPItem*> referenced_objects; + + GSList *items = g_slist_copy((GSList *) selection->itemList()); + selection->clear(); + + GSList *items_to_ungroup = NULL; + GSList *items_to_select = g_slist_copy(items); + items_to_select = g_slist_reverse(items_to_select); + + // SPObject* refers to a group containing the clipped path or mask itself, // whereas SPItem* refers to the item being clipped or masked - for (GSList const *i = selection->itemList(); NULL != i; i = i->next) { + for (GSList const *i = items; NULL != i; i = i->next) { if (remove_original) { // remember referenced mask/clippath, so orphaned masks can be moved back to document SPItem *item = reinterpret_cast<SPItem *>(i->data); @@ -2884,7 +3080,20 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } SP_OBJECT_REPR(i->data)->setAttribute(attributeName, "none"); + + if (ungroup_masked && SP_IS_GROUP(i->data)) { + // if we had previously enclosed masked object in group, + // add it to list so we can ungroup it later + SPGroup *item = SP_GROUP(i->data); + + // ungroup only groups we created when setting clip/mask + if (item->layerMode() == SPGroup::MASK_HELPER) { + items_to_ungroup = g_slist_prepend(items_to_ungroup, item); + } + + } } + g_slist_free(items); // restore mask objects into a document for ( std::map<SPObject*,SPItem*>::iterator it = referenced_objects.begin() ; it != referenced_objects.end() ; ++it) { @@ -2914,7 +3123,7 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { repr->setPosition((pos + 1) > 0 ? (pos + 1) : 0); SPItem *mask_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); - selection->add(repr); + items_to_select = g_slist_prepend(items_to_select, mask_item); // transform mask, so it is moved the same spot where mask was applied Geom::Matrix transform(mask_item->transform); @@ -2925,6 +3134,21 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { g_slist_free(items_to_move); } + // ungroup marked groups added when setting mask + for (GSList *i = items_to_ungroup ; NULL != i ; i = i->next) { + items_to_select = g_slist_remove(items_to_select, SP_GROUP(i->data)); + GSList *children = NULL; + sp_item_group_ungroup(SP_GROUP(i->data), &children, false); + items_to_select = g_slist_concat(children, items_to_select); + } + + g_slist_free(items_to_ungroup); + + // rebuild selection + items_to_select = g_slist_reverse(items_to_select); + selection->addList(items_to_select); + g_slist_free(items_to_select); + if (apply_clip_path) sp_document_done(doc, SP_VERB_OBJECT_UNSET_CLIPPATH, _("Release clipping path")); else @@ -2932,10 +3156,12 @@ void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path) { } /** - * Returns true if an undoable change should be recorded. + * \param with_margins margins defined in the xml under <sodipodi:namedview> + * "fit-margin-..." attributes. See SPDocument::fitToRect. + * \return true if an undoable change should be recorded. */ bool -fit_canvas_to_selection(SPDesktop *desktop) +fit_canvas_to_selection(SPDesktop *desktop, bool with_margins) { g_return_val_if_fail(desktop != NULL, false); SPDocument *doc = sp_desktop_document(desktop); @@ -2949,7 +3175,7 @@ fit_canvas_to_selection(SPDesktop *desktop) } Geom::OptRect const bbox(desktop->selection->bounds()); if (bbox) { - doc->fitToRect(*bbox); + doc->fitToRect(*bbox, with_margins); return true; } else { return false; @@ -2968,8 +3194,12 @@ verb_fit_canvas_to_selection(SPDesktop *const desktop) } } +/** + * \param with_margins margins defined in the xml under <sodipodi:namedview> + * "fit-margin-..." attributes. See SPDocument::fitToRect. + */ bool -fit_canvas_to_drawing(SPDocument *doc) +fit_canvas_to_drawing(SPDocument *doc, bool with_margins) { g_return_val_if_fail(doc != NULL, false); @@ -2977,7 +3207,7 @@ fit_canvas_to_drawing(SPDocument *doc) SPItem const *const root = SP_ITEM(doc->root); Geom::OptRect const bbox(root->getBounds(sp_item_i2d_affine(root))); if (bbox) { - doc->fitToRect(*bbox); + doc->fitToRect(*bbox, with_margins); return true; } else { return false; @@ -2993,6 +3223,11 @@ verb_fit_canvas_to_drawing(SPDesktop *desktop) } } +/** + * Fits canvas to selection or drawing with margins from <sodipodi:namedview> + * "fit-margin-..." attributes. See SPDocument::fitToRect and + * ui/dialog/page-sizer. + */ void fit_canvas_to_selection_or_drawing(SPDesktop *desktop) { g_return_if_fail(desktop != NULL); SPDocument *doc = sp_desktop_document(desktop); @@ -3001,8 +3236,8 @@ void fit_canvas_to_selection_or_drawing(SPDesktop *desktop) { g_return_if_fail(desktop->selection != NULL); bool const changed = ( desktop->selection->isEmpty() - ? fit_canvas_to_drawing(doc) - : fit_canvas_to_selection(desktop) ); + ? fit_canvas_to_drawing(doc, true) + : fit_canvas_to_selection(desktop, true) ); if (changed) { sp_document_done(sp_desktop_document(desktop), SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING, _("Fit Page to Selection or Drawing")); diff --git a/src/selection-chemistry.h b/src/selection-chemistry.h index 67e772a00..1f975975a 100644 --- a/src/selection-chemistry.h +++ b/src/selection-chemistry.h @@ -1,5 +1,5 @@ -#ifndef __SP_SELECTION_CHEMISTRY_H__ -#define __SP_SELECTION_CHEMISTRY_H__ +#ifndef SEEN_SELECTION_CHEMISTRY_H +#define SEEN_SELECTION_CHEMISTRY_H /* * Miscellanous operations on selected items @@ -8,8 +8,9 @@ * Lauris Kaplinski <lauris@kaplinski.com> * Frank Felfe <innerspace@iname.com> * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> * - * Copyright (C) 1999-2005 authors + * Copyright (C) 1999-2010 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information @@ -28,6 +29,20 @@ namespace LivePathEffect { class SPCSSAttr; +namespace Inkscape { + class SelectionHelper { + public: + static void selectAll(SPDesktop *desktop); + static void selectAllInAll(SPDesktop *desktop); + static void selectNone(SPDesktop *desktop); + static void invert(SPDesktop *desktop); + static void invertAllInAll(SPDesktop *desktop); + static void reverse(SPDesktop *dt); + static void selectNext(SPDesktop *desktop); + static void selectPrev(SPDesktop *desktop); + }; +} // namespace Inkscape + void sp_selection_delete(SPDesktop *desktop); void sp_selection_duplicate(SPDesktop *desktop, bool suppressDone = false); void sp_edit_clear_all(SPDesktop *desktop); @@ -48,6 +63,7 @@ void sp_selection_to_guides(SPDesktop *desktop); void sp_selection_tile(SPDesktop *desktop, bool apply = true); void sp_selection_untile(SPDesktop *desktop); +//void sp_selection_group_impl(GSList const *reprs_to_group, Inkscape::XML::Node *group, Inkscape::XML::Document *xml_doc, SPDocument *doc); void sp_selection_group(SPDesktop *desktop); void sp_selection_ungroup(SPDesktop *desktop); @@ -59,7 +75,7 @@ void sp_selection_lower_to_bottom(SPDesktop *desktop); SPCSSAttr *take_style_from_item (SPItem *item); void sp_selection_cut(SPDesktop *desktop); -void sp_selection_copy(); +void sp_selection_copy(SPDesktop *desktop); void sp_selection_paste(SPDesktop *desktop, bool in_place); void sp_selection_paste_style(SPDesktop *desktop); void sp_selection_paste_livepatheffect(SPDesktop *desktop); @@ -115,9 +131,9 @@ void sp_selection_create_bitmap_copy (SPDesktop *desktop); void sp_selection_set_mask(SPDesktop *desktop, bool apply_clip_path, bool apply_to_layer); void sp_selection_unset_mask(SPDesktop *desktop, bool apply_clip_path); -bool fit_canvas_to_selection(SPDesktop *); +bool fit_canvas_to_selection(SPDesktop *, bool with_margins = false); void verb_fit_canvas_to_selection(SPDesktop *); -bool fit_canvas_to_drawing(SPDocument *); +bool fit_canvas_to_drawing(SPDocument *, bool with_margins = false); void verb_fit_canvas_to_drawing(SPDesktop *); void fit_canvas_to_selection_or_drawing(SPDesktop *); @@ -131,19 +147,14 @@ GSList *sp_degroup_list (GSList *items); /* selection cycling */ typedef enum { - SP_CYCLE_SIMPLE, - SP_CYCLE_VISIBLE, /* cycle only visible items */ - SP_CYCLE_FOCUS /* readjust visible area to view selected item */ + SP_CYCLE_SIMPLE, + SP_CYCLE_VISIBLE, // cycle only visible items + SP_CYCLE_FOCUS // readjust visible area to view selected item } SPCycleType; -/* fixme: This should be moved into preference repr */ -#ifndef __SP_SELECTION_CHEMISTRY_C__ -extern SPCycleType SP_CYCLING; -#else -SPCycleType SP_CYCLING = SP_CYCLE_FOCUS; -#endif - -#endif +// TOOD fixme: This should be moved into preference repr +extern SPCycleType SP_CYCLING; +#endif // SEEN_SELECTION_CHEMISTRY_H diff --git a/src/selection-describer.cpp b/src/selection-describer.cpp index 27dd8413a..9321ea0ef 100644 --- a/src/selection-describer.cpp +++ b/src/selection-describer.cpp @@ -97,8 +97,10 @@ GSList *collect_terms (GSList *items) namespace Inkscape { -SelectionDescriber::SelectionDescriber(Inkscape::Selection *selection, MessageStack *stack) -: _context(stack) +SelectionDescriber::SelectionDescriber(Inkscape::Selection *selection, MessageStack *stack, char *when_selected, char *when_nothing) + : _context(stack), + _when_selected (when_selected), + _when_nothing (when_nothing) { _selection_changed_connection = new sigc::connection ( selection->connectChanged( @@ -125,9 +127,8 @@ void SelectionDescriber::_selectionModified(Inkscape::Selection *selection, guin void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *selection) { GSList const *items = selection->itemList(); - char const *when_selected = _("Click selection to toggle scale/rotation handles"); if (!items) { // no items - _context.set(Inkscape::NORMAL_MESSAGE, _("No objects selected. Click, Shift+click, or drag around objects to select.")); + _context.set(Inkscape::NORMAL_MESSAGE, _when_nothing); } else { SPItem *item = SP_ITEM(items->data); SPObject *layer = selection->desktop()->layerForObject (SP_OBJECT (item)); @@ -157,7 +158,7 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select // Parent name SPObject *parent = SP_OBJECT_PARENT (item); - gchar *parent_label = SP_OBJECT_ID(parent); + gchar const *parent_label = parent->getId(); char *quoted_parent_label = xml_quote_strdup(parent_label); gchar *parent_name = g_strdup_printf(_("<i>%s</i>"), quoted_parent_label); g_free(quoted_parent_label); @@ -185,18 +186,18 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select if (SP_IS_USE(item) || (SP_IS_OFFSET(item) && SP_OFFSET (item)->sourceHref)) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, - _("Use <b>Shift+D</b> to look up original"), when_selected); + _("Use <b>Shift+D</b> to look up original"), _when_selected); } else if (SP_IS_TEXT_TEXTPATH(item)) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, - _("Use <b>Shift+D</b> to look up path"), when_selected); + _("Use <b>Shift+D</b> to look up path"), _when_selected); } else if (SP_IS_FLOWTEXT(item) && !SP_FLOWTEXT(item)->has_internal_frame()) { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s. %s.", item_desc, in_phrase, - _("Use <b>Shift+D</b> to look up frame"), when_selected); + _("Use <b>Shift+D</b> to look up frame"), _when_selected); } else { _context.setF(Inkscape::NORMAL_MESSAGE, "%s%s. %s.", - item_desc, in_phrase, when_selected); + item_desc, in_phrase, _when_selected); } g_free(item_desc); } else { // multiple items @@ -233,7 +234,7 @@ void SelectionDescriber::_updateMessageFromSelection(Inkscape::Selection *select } g_slist_free (terms); - _context.setF(Inkscape::NORMAL_MESSAGE, _("%s%s. %s."), objects_str, in_phrase, when_selected); + _context.setF(Inkscape::NORMAL_MESSAGE, _("%s%s. %s."), objects_str, in_phrase, _when_selected); if (objects_str) g_free ((gchar *) objects_str); diff --git a/src/selection-describer.h b/src/selection-describer.h index 0c4c9b9c4..4b0e3d8c7 100644 --- a/src/selection-describer.h +++ b/src/selection-describer.h @@ -23,7 +23,7 @@ class MessageStack; class SelectionDescriber : public sigc::trackable { public: - SelectionDescriber(Inkscape::Selection *selection, MessageStack *stack); + SelectionDescriber(Inkscape::Selection *selection, MessageStack *stack, char *when_selected, char *when_nothing); ~SelectionDescriber(); private: @@ -34,6 +34,9 @@ private: sigc::connection *_selection_modified_connection; MessageContext _context; + + char *_when_selected; + char *_when_nothing; }; } diff --git a/src/selection.cpp b/src/selection.cpp index 7b936587c..1e14591fa 100644 --- a/src/selection.cpp +++ b/src/selection.cpp @@ -426,48 +426,48 @@ boost::optional<Geom::Point> Selection::center() const { /** * Compute the list of points in the selection that are to be considered for snapping. */ -std::vector<std::pair<Geom::Point, int> > Selection::getSnapPoints(SnapPreferences const *snapprefs) const { +std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferences const *snapprefs) const { GSList const *items = const_cast<Selection *>(this)->itemList(); SnapPreferences snapprefs_dummy = *snapprefs; // create a local copy of the snapping prefs snapprefs_dummy.setIncludeItemCenter(false); // locally disable snapping to the item center - std::vector<std::pair<Geom::Point, int> > p; + std::vector<Inkscape::SnapCandidatePoint> p; for (GSList const *iter = items; iter != NULL; iter = iter->next) { SPItem *this_item = SP_ITEM(iter->data); - sp_item_snappoints(this_item, false, p, &snapprefs_dummy); + sp_item_snappoints(this_item, p, &snapprefs_dummy); //Include the transformation origin for snapping //For a selection or group only the overall origin is considered if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) { - p.push_back(std::make_pair(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER)); + p.push_back(Inkscape::SnapCandidatePoint(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER)); } } return p; } -std::vector<std::pair<Geom::Point, int> > Selection::getSnapPointsConvexHull(SnapPreferences const *snapprefs) const { +std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPointsConvexHull(SnapPreferences const *snapprefs) const { GSList const *items = const_cast<Selection *>(this)->itemList(); - std::vector<std::pair<Geom::Point, int> > p; + std::vector<Inkscape::SnapCandidatePoint> p; for (GSList const *iter = items; iter != NULL; iter = iter->next) { - sp_item_snappoints(SP_ITEM(iter->data), false, p, snapprefs); + sp_item_snappoints(SP_ITEM(iter->data), p, snapprefs); } - std::vector<std::pair<Geom::Point, int> > pHull; + std::vector<Inkscape::SnapCandidatePoint> pHull; if (!p.empty()) { - std::vector<std::pair<Geom::Point, int> >::iterator i; - Geom::RectHull cvh((p.front()).first); + std::vector<Inkscape::SnapCandidatePoint>::iterator i; + Geom::RectHull cvh((p.front()).getPoint()); for (i = p.begin(); i != p.end(); i++) { // these are the points we get back - cvh.add((*i).first); + cvh.add((*i).getPoint()); } Geom::OptRect rHull = cvh.bounds(); if (rHull) { for ( unsigned i = 0 ; i < 4 ; ++i ) { - pHull.push_back(std::make_pair(rHull->corner(i), SNAPSOURCE_CONVEX_HULL_CORNER)); + pHull.push_back(Inkscape::SnapCandidatePoint(rHull->corner(i), SNAPSOURCE_CONVEX_HULL_CORNER)); } } } diff --git a/src/selection.h b/src/selection.h index a0734c299..479a99e76 100644 --- a/src/selection.h +++ b/src/selection.h @@ -274,13 +274,13 @@ public: * @brief Gets the selection's snap points. * @return Selection's snap points */ - std::vector<std::pair<Geom::Point, int> > getSnapPoints(SnapPreferences const *snapprefs) const; + std::vector<Inkscape::SnapCandidatePoint> getSnapPoints(SnapPreferences const *snapprefs) const; /** * @brief Gets the snap points of a selection that form a convex hull. * @return Selection's convex hull points */ - std::vector<std::pair<Geom::Point, int> > getSnapPointsConvexHull(SnapPreferences const *snapprefs) const; + std::vector<Inkscape::SnapCandidatePoint> getSnapPointsConvexHull(SnapPreferences const *snapprefs) const; /** * @brief Connects a slot to be notified of selection changes diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 8ff00d60a..7a08e0a7e 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -295,7 +295,7 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s // but as a snap source we still need some nodes though! _snap_points.clear(); _snap_points = selection->getSnapPoints(&local_snapprefs); - std::vector<std::pair<Geom::Point, int> > snap_points_hull = selection->getSnapPointsConvexHull(&local_snapprefs); + std::vector<Inkscape::SnapCandidatePoint> snap_points_hull = selection->getSnapPointsConvexHull(&local_snapprefs); if (_snap_points.size() > 200) { /* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes An average user would rarely ever try to snap such a large number of nodes anyway, because @@ -313,11 +313,11 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s // any other special points Geom::Rect snap_points_bbox; if ( snap_points_hull.empty() == false ) { - std::vector<std::pair<Geom::Point, int> >::iterator i = snap_points_hull.begin(); - snap_points_bbox = Geom::Rect((*i).first, (*i).first); + std::vector<Inkscape::SnapCandidatePoint>::iterator i = snap_points_hull.begin(); + snap_points_bbox = Geom::Rect((*i).getPoint(), (*i).getPoint()); i++; while (i != snap_points_hull.end()) { - snap_points_bbox.expandTo((*i).first); + snap_points_bbox.expandTo((*i).getPoint()); i++; } } @@ -381,9 +381,9 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s } // Now let's reduce this to a single closest snappoint - Geom::Coord dsp = _snap_points.size() == 1 ? Geom::L2((_snap_points.at(0)).first - p) : NR_HUGE; - Geom::Coord dbbp = _bbox_points.size() == 1 ? Geom::L2((_bbox_points.at(0)).first - p) : NR_HUGE; - Geom::Coord dbbpft = _bbox_points_for_translating.size() == 1 ? Geom::L2((_bbox_points_for_translating.at(0)).first - p) : NR_HUGE; + Geom::Coord dsp = _snap_points.size() == 1 ? Geom::L2((_snap_points.at(0)).getPoint() - p) : NR_HUGE; + Geom::Coord dbbp = _bbox_points.size() == 1 ? Geom::L2((_bbox_points.at(0)).getPoint() - p) : NR_HUGE; + Geom::Coord dbbpft = _bbox_points_for_translating.size() == 1 ? Geom::L2((_bbox_points_for_translating.at(0)).getPoint() - p) : NR_HUGE; if (translating) { _bbox_points.clear(); @@ -1015,8 +1015,8 @@ gboolean Inkscape::SelTrans::scaleRequest(Geom::Point &pt, guint state) } // Snap along a suitable constraint vector from the origin. - bb = m.constrainedSnapScale(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, default_scale, _origin_for_bboxpoints); - sn = m.constrainedSnapScale(SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, geom_scale, _origin_for_specpoints); + bb = m.constrainedSnapScale(_bbox_points, _point, default_scale, _origin_for_bboxpoints); + sn = m.constrainedSnapScale(_snap_points, _point, geom_scale, _origin_for_specpoints); /* Choose the smaller difference in scale. Since s[X] == s[Y] we can ** just compare difference in s[X]. @@ -1025,8 +1025,8 @@ gboolean Inkscape::SelTrans::scaleRequest(Geom::Point &pt, guint state) sd = sn.getSnapped() ? fabs(sn.getTransformation()[Geom::X] - geom_scale[Geom::X]) : NR_HUGE; } else { /* Scale aspect ratio is unlocked */ - bb = m.freeSnapScale(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, default_scale, _origin_for_bboxpoints); - sn = m.freeSnapScale(SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, geom_scale, _origin_for_specpoints); + bb = m.freeSnapScale(_bbox_points, _point, default_scale, _origin_for_bboxpoints); + sn = m.freeSnapScale(_snap_points, _point, geom_scale, _origin_for_specpoints); /* Pick the snap that puts us closest to the original scale */ bd = bb.getSnapped() ? fabs(Geom::L2(bb.getTransformation()) - Geom::L2(Geom::Point(default_scale[Geom::X], default_scale[Geom::Y]))) : NR_HUGE; @@ -1114,8 +1114,8 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, Geom bool symmetrical = state & GDK_CONTROL_MASK; - bb = m.constrainedSnapStretch(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, Geom::Coord(default_scale[axis]), _origin_for_bboxpoints, Geom::Dim2(axis), symmetrical); - sn = m.constrainedSnapStretch(SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, Geom::Coord(geom_scale[axis]), _origin_for_specpoints, Geom::Dim2(axis), symmetrical); + bb = m.constrainedSnapStretch(_bbox_points, _point, Geom::Coord(default_scale[axis]), _origin_for_bboxpoints, Geom::Dim2(axis), symmetrical); + sn = m.constrainedSnapStretch(_snap_points, _point, Geom::Coord(geom_scale[axis]), _origin_for_specpoints, Geom::Dim2(axis), symmetrical); if (bb.getSnapped()) { // We snapped the bbox (which is either visual or geometric) @@ -1232,7 +1232,7 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P Inkscape::Snapper::ConstraintLine const constraint(component_vectors[dim_b]); // When skewing, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapSkew" for details Geom::Point const s(skew[dim_a], scale[dim_a]); - Inkscape::SnappedPoint sn = m.constrainedSnapSkew(Inkscape::SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b)); + Inkscape::SnappedPoint sn = m.constrainedSnapSkew(_snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b)); if (sn.getSnapped()) { // We snapped something, so change the skew to reflect it @@ -1336,7 +1336,7 @@ gboolean Inkscape::SelTrans::centerRequest(Geom::Point &pt, guint state) { SnapManager &m = _desktop->namedview->snap_manager; m.setup(_desktop); - m.freeSnapReturnByRef(SnapPreferences::SNAPPOINT_NODE, pt, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(pt, Inkscape::SNAPSOURCE_OTHER_HANDLE); if (state & GDK_CONTROL_MASK) { if ( fabs(_point[Geom::X] - pt[Geom::X]) > fabs(_point[Geom::Y] - pt[Geom::Y]) ) { @@ -1439,13 +1439,17 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) if (alt) { - /* Alt pressed means keep offset: snap the moved distance to the grid. - ** FIXME: this will snap to more than just the grid, nowadays. - */ + // Alt pressed means: move only by integer multiples of the grid spacing + if (control) { // ... if also constrained to the orthogonal axes + if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) { + dxy[Geom::Y] = 0; + } else { + dxy[Geom::X] = 0; + } + } m.setup(_desktop, true, _items_const); - m.freeSnapReturnByRef(SnapPreferences::SNAPPOINT_NODE, dxy, Inkscape::SNAPSOURCE_UNDEFINED); - + dxy = m.multipleOfGridPitch(dxy, _point); } else if (shift) { if (control) { // shift & control: constrained movement without snapping if (fabs(dxy[Geom::X]) > fabs(dxy[Geom::Y])) { @@ -1476,14 +1480,12 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) // the constraint-line once. The constraint lines are parallel, but might not be colinear. // Therefore we will have to set the point through which the constraint-line runs // individually for each point to be snapped; this will be handled however by _snapTransformed() - s.push_back(m.constrainedSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_BBOX, - _bbox_points_for_translating, + s.push_back(m.constrainedSnapTranslation(_bbox_points_for_translating, _point, Inkscape::Snapper::ConstraintLine(component_vectors[dim]), dxy)); - s.push_back(m.constrainedSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_NODE, - _snap_points, + s.push_back(m.constrainedSnapTranslation(_snap_points, _point, Inkscape::Snapper::ConstraintLine(component_vectors[dim]), dxy)); @@ -1495,8 +1497,8 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) g_get_current_time(&starttime); */ /* Snap to things with no constraint */ - s.push_back(m.freeSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_BBOX, _bbox_points_for_translating, _point, dxy)); - s.push_back(m.freeSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, dxy)); + s.push_back(m.freeSnapTranslation(_bbox_points_for_translating, _point, dxy)); + s.push_back(m.freeSnapTranslation(_snap_points, _point, dxy)); /*g_get_current_time(&endtime); double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0; @@ -1513,6 +1515,7 @@ void Inkscape::SelTrans::moveTo(Geom::Point const &xy, guint state) } } } + if (best_snapped_point.getSnapped()) { _desktop->snapindicator->set_new_snaptarget(best_snapped_point); } else { @@ -1643,15 +1646,15 @@ Geom::Point Inkscape::SelTrans::_calcAbsAffineGeom(Geom::Scale const geom_scale) return _calcAbsAffineDefault(geom_scale); // this is bogus, but we must return _something_ } -void Inkscape::SelTrans::_keepClosestPointOnly(std::vector<std::pair<Geom::Point, int> > &points, const Geom::Point &reference) +void Inkscape::SelTrans::_keepClosestPointOnly(std::vector<Inkscape::SnapCandidatePoint> &points, const Geom::Point &reference) { if (points.size() < 2) return; - std::pair<Geom::Point, int> closest_point = std::make_pair(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED); + Inkscape::SnapCandidatePoint closest_point = Inkscape::SnapCandidatePoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPSOURCE_UNDEFINED, SNAPTARGET_UNDEFINED); Geom::Coord closest_dist = NR_HUGE; - for(std::vector<std::pair<Geom::Point, int> >::const_iterator i = points.begin(); i != points.end(); i++) { - Geom::Coord dist = Geom::L2((*i).first - reference); + for(std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) { + Geom::Coord dist = Geom::L2((*i).getPoint() - reference); if (i == points.begin() || dist < closest_dist) { closest_point = *i; closest_dist = dist; diff --git a/src/seltrans.h b/src/seltrans.h index d0ac5dccf..8b2810621 100644 --- a/src/seltrans.h +++ b/src/seltrans.h @@ -102,7 +102,7 @@ private: Geom::Point _getGeomHandlePos(Geom::Point const &visual_handle_pos); Geom::Point _calcAbsAffineDefault(Geom::Scale const default_scale); Geom::Point _calcAbsAffineGeom(Geom::Scale const geom_scale); - void _keepClosestPointOnly(std::vector<std::pair<Geom::Point, int> > &points, const Geom::Point &reference); + void _keepClosestPointOnly(std::vector<Inkscape::SnapCandidatePoint> &points, const Geom::Point &reference); void _display_snapsource(); enum State { @@ -117,9 +117,9 @@ private: std::vector<Geom::Matrix> _items_affines; std::vector<Geom::Point> _items_centers; - std::vector<std::pair<Geom::Point, int> > _snap_points; - std::vector<std::pair<Geom::Point, int> > _bbox_points; // the bbox point of the selection as a whole, i.e. max. 4 corners plus optionally some midpoints - std::vector<std::pair<Geom::Point, int> > _bbox_points_for_translating; // the bbox points of each selected item, only to be used for translating + std::vector<Inkscape::SnapCandidatePoint> _snap_points; + std::vector<Inkscape::SnapCandidatePoint> _bbox_points; // the bbox point of the selection as a whole, i.e. max. 4 corners plus optionally some midpoints + std::vector<Inkscape::SnapCandidatePoint> _bbox_points_for_translating; // the bbox points of each selected item, only to be used for translating Inkscape::SelCue _selcue; diff --git a/src/shape-editor.cpp b/src/shape-editor.cpp index 44ad9dc9e..1962b710c 100644 --- a/src/shape-editor.cpp +++ b/src/shape-editor.cpp @@ -1,10 +1,9 @@ -#define __SHAPE_EDITOR_CPP__ - /* * Inkscape::ShapeEditor * * Authors: * bulia byak <buliabyak@users.sf.net> + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> * */ @@ -25,7 +24,6 @@ #include "desktop-handles.h" #include "knotholder.h" #include "live_effects/parameter/point.h" -#include "nodepath.h" #include "xml/node-event-vector.h" #include "preferences.h" #include "object-edit.h" @@ -35,33 +33,14 @@ #include "shape-editor.h" - -ShapeEditorsCollective::ShapeEditorsCollective(SPDesktop */*dt*/) { -} - -ShapeEditorsCollective::~ShapeEditorsCollective() { -} - - -void ShapeEditorsCollective::update_statusbar() { - -//!!! move from nodepath: sp_nodepath_update_statusbar but summing for all nodepaths - -} - ShapeEditor::ShapeEditor(SPDesktop *dt) { this->desktop = dt; - this->grab_node = -1; - this->nodepath = NULL; this->knotholder = NULL; - this->hit = false; this->knotholder_listener_attached_for = NULL; - this->nodepath_listener_attached_for = NULL; } ShapeEditor::~ShapeEditor() { unset_item(SH_KNOTHOLDER); - unset_item(SH_NODEPATH); } void ShapeEditor::unset_item(SubType type, bool keep_knotholder) { @@ -69,18 +48,7 @@ void ShapeEditor::unset_item(SubType type, bool keep_knotholder) { switch (type) { case SH_NODEPATH: - if (this->nodepath) { - old_repr = this->nodepath->repr; - if (old_repr && old_repr == nodepath_listener_attached_for) { - sp_repr_remove_listener_by_data(old_repr, this); - Inkscape::GC::release(old_repr); - nodepath_listener_attached_for = NULL; - } - - this->grab_node = -1; - delete this->nodepath; - this->nodepath = NULL; - } + // defunct break; case SH_KNOTHOLDER: if (this->knotholder) { @@ -101,7 +69,7 @@ void ShapeEditor::unset_item(SubType type, bool keep_knotholder) { } bool ShapeEditor::has_nodepath () { - return (this->nodepath != NULL); + return false; } bool ShapeEditor::has_knotholder () { @@ -116,7 +84,8 @@ void ShapeEditor::update_knotholder () { bool ShapeEditor::has_local_change (SubType type) { switch (type) { case SH_NODEPATH: - return (this->nodepath && this->nodepath->local_change); + // defunct + return false; case SH_KNOTHOLDER: return (this->knotholder && this->knotholder->local_change != 0); default: @@ -127,9 +96,7 @@ bool ShapeEditor::has_local_change (SubType type) { void ShapeEditor::decrement_local_change (SubType type) { switch (type) { case SH_NODEPATH: - if (this->nodepath && this->nodepath->local_change > 0) { - this->nodepath->local_change--; - } + // defunct break; case SH_KNOTHOLDER: if (this->knotholder) { @@ -145,9 +112,7 @@ const SPItem *ShapeEditor::get_item (SubType type) { const SPItem *item = NULL; switch (type) { case SH_NODEPATH: - if (this->has_nodepath()) { - item = this->nodepath->item; - } + // defunct break; case SH_KNOTHOLDER: if (this->has_knotholder()) { @@ -159,50 +124,18 @@ const SPItem *ShapeEditor::get_item (SubType type) { } GList *ShapeEditor::save_nodepath_selection () { - if (this->nodepath) - return ::save_nodepath_selection (this->nodepath); + // defunct stub return NULL; } -void ShapeEditor::restore_nodepath_selection (GList *saved) { - if (this->nodepath && saved) - ::restore_nodepath_selection (this->nodepath, saved); +void ShapeEditor::restore_nodepath_selection (GList */*saved*/) { + // defunct stub } -bool ShapeEditor::nodepath_edits_repr_key(gchar const *name) { - if (nodepath && name) { - return ( !strcmp(name, nodepath->repr_key) || !strcmp(name, nodepath->repr_nodetypes_key) ); - } - - return false; -} - - void ShapeEditor::shapeeditor_event_attr_changed(gchar const *name) { - gboolean changed_np = FALSE; gboolean changed_kh = FALSE; - if (has_nodepath() && nodepath_edits_repr_key(name)) - { - changed_np = !has_local_change(SH_NODEPATH); - decrement_local_change(SH_NODEPATH); - } - - if (changed_np) { - GList *saved = NULL; - if (has_nodepath()) { - saved = save_nodepath_selection(); - } - - reset_item(SH_NODEPATH); - - if (has_nodepath() && saved) { - restore_nodepath_selection(saved); - g_list_free (saved); - } - } - if (has_knotholder()) { changed_kh = !has_local_change(SH_KNOTHOLDER); @@ -213,8 +146,6 @@ void ShapeEditor::shapeeditor_event_attr_changed(gchar const *name) reset_item(SH_KNOTHOLDER, !strcmp(name, "d")); } } - - update_statusbar(); //TODO: get_container()->update_statusbar(); } @@ -243,27 +174,11 @@ void ShapeEditor::set_item(SPItem *item, SubType type, bool keep_knotholder) { // since this freezes the handles unset_item(type, keep_knotholder); - this->grab_node = -1; - if (item) { Inkscape::XML::Node *repr; switch(type) { case SH_NODEPATH: - if (SP_IS_LPE_ITEM(item)) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - this->nodepath = sp_nodepath_new(desktop, item, (prefs->getBool("/tools/nodes/show_handles", true))); - } - if (this->nodepath) { - this->nodepath->shape_editor = this; - - // setting new listener - repr = SP_OBJECT_REPR(item); - if (repr != nodepath_listener_attached_for) { - Inkscape::GC::anchor(repr); - sp_repr_add_listener(repr, &shapeeditor_repr_events, this); - nodepath_listener_attached_for = repr; - } - } + // defunct break; case SH_KNOTHOLDER: @@ -286,34 +201,6 @@ void ShapeEditor::set_item(SPItem *item, SubType type, bool keep_knotholder) { } } -/** Please note that this function only works for path parameters. -* All other parameters probably will crash Inkscape! -*/ -void ShapeEditor::set_item_lpe_path_parameter(SPItem *item, LivePathEffectObject *lpeobject, const char * key) -{ - unset_item(SH_NODEPATH); - - this->grab_node = -1; - - if (lpeobject) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - this->nodepath = sp_nodepath_new( desktop, lpeobject, - (prefs->getInt("/tools/nodes/show_handles", true)), - key, item); - if (this->nodepath) { - this->nodepath->shape_editor = this; - - // setting new listener - Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeobject); - if (repr && repr != nodepath_listener_attached_for) { - Inkscape::GC::anchor(repr); - sp_repr_add_listener(repr, &shapeeditor_repr_events, this); - nodepath_listener_attached_for = repr; - } - } - } -} - /** FIXME: This thing is only called when the item needs to be updated in response to repr change. Why not make a reload function in NodePath and in KnotHolder? */ @@ -321,14 +208,7 @@ void ShapeEditor::reset_item (SubType type, bool keep_knotholder) { switch (type) { case SH_NODEPATH: - if ( (nodepath) && (IS_LIVEPATHEFFECT(nodepath->object)) ) { - char * key = g_strdup(nodepath->repr_key); - set_item_lpe_path_parameter(nodepath->item, LIVEPATHEFFECT(nodepath->object), key); // the above checks for nodepath, so it is indeed a path that we are editing - g_free(key); - } else { - SPObject *obj = sp_desktop_document(desktop)->getObjectByRepr(nodepath_listener_attached_for); /// note that it is not certain that this is an SPItem; it could be a LivePathEffectObject. - set_item(SP_ITEM(obj), SH_NODEPATH); - } + // defunct break; case SH_KNOTHOLDER: if ( knotholder ) { @@ -340,267 +220,23 @@ void ShapeEditor::reset_item (SubType type, bool keep_knotholder) } void ShapeEditor::nodepath_destroyed () { - this->nodepath = NULL; -} - -void ShapeEditor::update_statusbar () { - if (this->nodepath) - sp_nodepath_update_statusbar(this->nodepath); -} - -bool ShapeEditor::is_over_stroke (Geom::Point event_p, bool remember) { - if (!this->nodepath) - return false; // no stroke in knotholder - - const SPItem *item = get_item(SH_NODEPATH); - - if (!item || !SP_IS_ITEM(item)) - return false; - - //Translate click point into proper coord system - this->curvepoint_doc = desktop->w2d(event_p); - this->curvepoint_doc *= sp_item_dt2i_affine(item); - - SPCurve *curve = this->nodepath->curve; // not sure if np->curve is always up to date... - Geom::PathVector const &pathv = curve->get_pathvector(); - boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, this->curvepoint_doc); - if (!pvpos) { - g_print("Warning! Possible error?\n"); - return false; - } - - Geom::Point nearest = pathv[pvpos->path_nr].pointAt(pvpos->t); - Geom::Point delta = nearest - this->curvepoint_doc; - - delta = desktop->d2w(delta); - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - double stroke_tolerance = - (( !SP_OBJECT_STYLE(item)->stroke.isNone() ? - desktop->current_zoom() * - SP_OBJECT_STYLE (item)->stroke_width.computed * 0.5 * - to_2geom(sp_item_i2d_affine(item)).descrim() - : 0.0) - + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100)) / to_2geom(sp_item_i2d_affine(item)).descrim(); - bool close = (Geom::L2 (delta) < stroke_tolerance); - - if (remember && close) { - // calculate index for nodepath's representation. - double int_part; - double t = std::modf(pvpos->t, &int_part); - unsigned int segment_index = (unsigned int)int_part + 1; - for (unsigned int i = 0; i < pvpos->path_nr; ++i) { - segment_index += pathv[i].size() + 1; - if (pathv[i].closed()) - segment_index += 1; - } - - this->curvepoint_event[Geom::X] = (gint) event_p [Geom::X]; - this->curvepoint_event[Geom::Y] = (gint) event_p [Geom::Y]; - this->hit = true; - this->grab_t = t; - this->grab_node = segment_index; - } - - return close; -} - -void ShapeEditor::add_node_near_point() { - if (this->nodepath) { - sp_nodepath_add_node_near_point(this->nodepath, this->curvepoint_doc); - } else if (this->knotholder) { - // we do not add nodes in knotholder... yet - } -} - -void ShapeEditor::select_segment_near_point(bool toggle) { - if (this->nodepath) { - sp_nodepath_select_segment_near_point(this->nodepath, this->curvepoint_doc, toggle); - } - if (this->knotholder) { - // we do not select segments in knotholder... yet? - } -} - -void ShapeEditor::cancel_hit() { - this->hit = false; -} - -bool ShapeEditor::hits_curve() { - return (this->hit); -} - - -void ShapeEditor::curve_drag(gdouble eventx, gdouble eventy) { - if (this->nodepath && !this->nodepath->straight_path) { - - if (this->grab_node == -1) // don't know which segment to drag - return; - - // We round off the extra precision in the motion coordinates provided - // by some input devices (like tablets). As we'll store the coordinates - // as integers in curvepoint_event we need to do this rounding before - // comparing them with the last coordinates from curvepoint_event. - // See bug #1593499 for details. - - gint x = (gint) Inkscape::round(eventx); - gint y = (gint) Inkscape::round(eventy); - - - // The coordinates hasn't changed since the last motion event, abort - if (this->curvepoint_event[Geom::X] == x && - this->curvepoint_event[Geom::Y] == y) - return; - - Geom::Point const delta_w(eventx - this->curvepoint_event[Geom::X], - eventy - this->curvepoint_event[Geom::Y]); - Geom::Point const delta_dt(this->desktop->w2d(delta_w)); - - sp_nodepath_curve_drag (this->nodepath, this->grab_node, this->grab_t, delta_dt); - this->curvepoint_event[Geom::X] = x; - this->curvepoint_event[Geom::Y] = y; - - } - if (this->knotholder) { - // we do not drag curve in knotholder - } - -} - -void ShapeEditor::finish_drag() { - if (this->nodepath && this->hit) { - sp_nodepath_update_repr (this->nodepath, _("Drag curve")); - } -} - -void ShapeEditor::select_rect(Geom::Rect const &rect, bool add) { - if (this->nodepath) { - sp_nodepath_select_rect(this->nodepath, rect, add); - } } bool ShapeEditor::has_selection() { - if (this->nodepath) - return this->nodepath->selected; return false; // so far, knotholder cannot have selection } -void ShapeEditor::deselect() { - if (this->nodepath) - sp_nodepath_deselect(this->nodepath); -} - -void ShapeEditor::add_node () { - sp_node_selected_add_node(this->nodepath); -} - -void ShapeEditor::delete_nodes () { - sp_node_selected_delete(this->nodepath); -} - -void ShapeEditor::delete_nodes_preserving_shape () { - if (this->nodepath && this->nodepath->selected) { - sp_node_delete_preserve(g_list_copy(this->nodepath->selected)); - } -} - -void ShapeEditor::delete_segment () { - sp_node_selected_delete_segment(this->nodepath); -} - -void ShapeEditor::set_node_type(int type) { - sp_node_selected_set_type(this->nodepath, (Inkscape::NodePath::NodeType) type); -} - -void ShapeEditor::break_at_nodes() { - sp_node_selected_break(this->nodepath); -} - -void ShapeEditor::join_nodes() { - sp_node_selected_join(this->nodepath); -} - -void ShapeEditor::join_segments() { - sp_node_selected_join_segment(this->nodepath); -} - -void ShapeEditor::duplicate_nodes() { - sp_node_selected_duplicate(this->nodepath); -} - -void ShapeEditor::set_type_of_segments(NRPathcode code) { - sp_node_selected_set_line_type(this->nodepath, code); -} - -void ShapeEditor::move_nodes_screen(SPDesktop *desktop, gdouble dx, gdouble dy) { - sp_node_selected_move_screen(desktop, this->nodepath, dx, dy); -} -void ShapeEditor::move_nodes(gdouble dx, gdouble dy) { - sp_node_selected_move(this->nodepath, dx, dy); -} - -void ShapeEditor::rotate_nodes(gdouble angle, int which, bool screen) { - if (this->nodepath) - sp_nodepath_selected_nodes_rotate (this->nodepath, angle, which, screen); -} - -void ShapeEditor::scale_nodes(gdouble const grow, int const which) { - sp_nodepath_selected_nodes_scale(this->nodepath, grow, which); -} -void ShapeEditor::scale_nodes_screen(gdouble const grow, int const which) { - sp_nodepath_selected_nodes_scale_screen(this->nodepath, grow, which); -} - -void ShapeEditor::select_all (bool invert) { - if (this->nodepath) - sp_nodepath_select_all (this->nodepath, invert); -} -void ShapeEditor::select_all_from_subpath (bool invert) { - if (this->nodepath) - sp_nodepath_select_all_from_subpath (this->nodepath, invert); -} -void ShapeEditor::select_next () { - if (this->nodepath) { - sp_nodepath_select_next (this->nodepath); - if (this->nodepath->numSelected() >= 1) { - this->desktop->scroll_to_point(this->nodepath->singleSelectedCoords(), 1.0); - } - } -} -void ShapeEditor::select_prev () { - if (this->nodepath) { - sp_nodepath_select_prev (this->nodepath); - if (this->nodepath->numSelected() >= 1) { - this->desktop->scroll_to_point(this->nodepath->singleSelectedCoords(), 1.0); - } +/** + * \brief Returns true if this ShapeEditor has a knot above which the mouse currently hovers + */ +bool ShapeEditor::knot_mouseover() +{ + if (this->knotholder) { + return knotholder->knot_mouseover(); } + return false; } -void ShapeEditor::show_handles (bool show) { - if (this->nodepath && !this->nodepath->straight_path) - sp_nodepath_show_handles (this->nodepath, show); -} - -void ShapeEditor::show_helperpath (bool show) { - if (this->nodepath) - sp_nodepath_show_helperpath (this->nodepath, show); -} - -void ShapeEditor::flip (Geom::Dim2 axis, boost::optional<Geom::Point> center) { - if (this->nodepath) - sp_nodepath_flip (this->nodepath, axis, center); -} - -void ShapeEditor::distribute (Geom::Dim2 axis) { - if (this->nodepath) - sp_nodepath_selected_distribute (this->nodepath, axis); -} -void ShapeEditor::align (Geom::Dim2 axis) { - if (this->nodepath) - sp_nodepath_selected_align (this->nodepath, axis); -} - - /* Local Variables: mode:c++ diff --git a/src/shape-editor.h b/src/shape-editor.h index 98dbb35d7..f400244b3 100644 --- a/src/shape-editor.h +++ b/src/shape-editor.h @@ -41,7 +41,6 @@ public: ~ShapeEditor(); void set_item (SPItem *item, SubType type, bool keep_knotholder = false); - void set_item_lpe_path_parameter(SPItem *item, LivePathEffectObject *lpeobject, const char * key); void unset_item (SubType type, bool keep_knotholder = false); bool has_nodepath (); //((deprecated)) @@ -55,116 +54,24 @@ public: void nodepath_destroyed (); - void update_statusbar (); - - bool is_over_stroke (Geom::Point event_p, bool remember); - - void add_node_near_point(); // uses the shapeeditor's remembered point, if any - - void select_segment_near_point(bool toggle); // uses the shapeeditor's remembered point, if any - - void cancel_hit (); - - bool hits_curve (); - - void curve_drag (gdouble eventx, gdouble eventy); - - void finish_drag (); - - void select_rect (Geom::Rect const &rect, bool add); - bool has_selection (); - void deselect (); - - Inkscape::NodePath::Path *get_nodepath() {return nodepath;} //((deprecated)) - ShapeEditorsCollective *get_container() {return container;} - - void add_node(); - - void delete_nodes(); - void delete_nodes_preserving_shape(); - void delete_segment(); - - void set_node_type(int type); - - void break_at_nodes(); - void join_nodes(); - void join_segments(); - - void duplicate_nodes(); - - void set_type_of_segments(NRPathcode code); - - void move_nodes(gdouble dx, gdouble dy); - void move_nodes_screen(SPDesktop *desktop, gdouble dx, gdouble dy); - - void rotate_nodes(gdouble angle, int which, bool screen); - - void scale_nodes(gdouble const grow, int const which); - void scale_nodes_screen(gdouble const grow, int const which); - - void select_all (bool invert); - void select_all_from_subpath (bool invert); - void select_next (); - void select_prev (); - void show_handles (bool show); - void show_helperpath (bool show); - - void flip (Geom::Dim2 axis, boost::optional<Geom::Point> center = boost::optional<Geom::Point>()); - - void distribute (Geom::Dim2 axis); - void align (Geom::Dim2 axis); - - bool nodepath_edits_repr_key(gchar const *name); + Inkscape::NodePath::Path *get_nodepath() {return NULL;} //((deprecated)) + ShapeEditorsCollective *get_container() {return NULL;} // this one is only public because it's called from non-C++ repr changed callback void shapeeditor_event_attr_changed(gchar const *name); + bool knot_mouseover(); + private: bool has_knotholder (); void reset_item (SubType type, bool keep_knotholder = true); const SPItem *get_item (SubType type); SPDesktop *desktop; - - Inkscape::NodePath::Path *nodepath; - - // TODO: std::list<KnotHolder *> knotholders; KnotHolder *knotholder; - - ShapeEditorsCollective *container; - - //Inkscape::XML::Node *lidtened_repr; - - double grab_t; - int grab_node; // number of node grabbed by sp_node_context_is_over_stroke - bool hit; - Geom::Point curvepoint_event; // int coords from event - Geom::Point curvepoint_doc; // same, in doc coords - Inkscape::XML::Node *knotholder_listener_attached_for; - Inkscape::XML::Node *nodepath_listener_attached_for; -}; - - -/* As the next stage, this will be a collection of multiple ShapeEditors, -with the same interface as the single ShapeEditor, passing the actions to all its -contained ShapeEditors. Thus it should be easy to switch node context from -using a single ShapeEditor to using a ShapeEditorsCollective. */ - -class ShapeEditorsCollective { -public: - - ShapeEditorsCollective(SPDesktop *desktop); - ~ShapeEditorsCollective(); - - void update_statusbar(); - -private: - std::vector<ShapeEditor> editors; - - SPNodeContext *nc; // who holds us }; #endif diff --git a/src/snap-candidate.h b/src/snap-candidate.h new file mode 100644 index 000000000..bd378bec8 --- /dev/null +++ b/src/snap-candidate.h @@ -0,0 +1,113 @@ +#ifndef SEEN_SNAP_CANDIDATE_H +#define SEEN_SNAP_CANDIDATE_H + +/** + * \file snap-candidate.h + * \brief some utility classes to store various kinds of snap candidates. + * + * Authors: + * Diederik van Lierop <mail@diedenrezi.nl> + * + * Copyright (C) 2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +//#include "snapped-point.h" +#include "snap-enums.h" + +struct SPItem; // forward declaration + +namespace Inkscape { + +/// Class to store data for points which are snap candidates, either as a source or as a target +class SnapCandidatePoint +{ +public: + SnapCandidatePoint(Geom::Point const &point, Inkscape::SnapSourceType const source, long const source_num, Inkscape::SnapTargetType const target, Geom::OptRect const &bbox) + : _point(point), + _source_type(source), + _target_type(target), + _source_num(source_num), + _target_bbox(bbox) + { + }; + + SnapCandidatePoint(Geom::Point const &point, Inkscape::SnapSourceType const source, Inkscape::SnapTargetType const target) + : _point(point), + _source_type(source), + _target_type(target) + { + _source_num = 0; + _target_bbox = Geom::OptRect(); + } + + SnapCandidatePoint(Geom::Point const &point, Inkscape::SnapSourceType const source, long const source_num = 0) + : _point(point), + _source_type(source), + _target_type(Inkscape::SNAPTARGET_UNDEFINED), + _source_num(source_num) + { + _target_bbox = Geom::OptRect(); + } + + inline Geom::Point const & getPoint() const {return _point;} + inline Inkscape::SnapSourceType getSourceType() const {return _source_type;} + inline Inkscape::SnapTargetType getTargetType() const {return _target_type;} + inline long getSourceNum() const {return _source_num;} + inline Geom::OptRect const getTargetBBox() const {return _target_bbox;} + +private: + // Coordinates of the point + Geom::Point _point; + + // If this SnapCandidatePoint is a snap source, then _source_type must be defined. If it + // is a snap target, then _target_type must be defined. If it's yet unknown whether it will + // be a source or target, then both may be defined + Inkscape::SnapSourceType _source_type; + Inkscape::SnapTargetType _target_type; + + //Sequence number of the source point within the set of points that is to be snapped. Starting at zero + long _source_num; + + // If this is a target and it belongs to a bounding box, e.g. when the target type is + // SNAPTARGET_BBOX_EDGE_MIDPOINT, then _target_bbox stores the relevant bounding box + Geom::OptRect _target_bbox; +}; + +class SnapCandidateItem +{ +public: + SnapCandidateItem(SPItem* item, bool clip_or_mask, Geom::Matrix additional_affine) + : item(item), clip_or_mask(clip_or_mask), additional_affine(additional_affine) {} + ~SnapCandidateItem() {}; + + SPItem* item; // An item that is to be considered for snapping to + bool clip_or_mask; // If true, then item refers to a clipping path or a mask + + /* To find out the absolute position of a clipping path or mask, we not only need to know + * the transformation of the clipping path or mask itself, but also the transformation of + * the object to which the clip or mask is being applied; that transformation is stored here + */ + Geom::Matrix additional_affine; +} +; + +class SnapCandidatePath +{ + +public: + SnapCandidatePath(Geom::PathVector* path, SnapTargetType target, Geom::OptRect bbox, bool edited = false) + : path_vector(path), target_type(target), target_bbox(bbox), currently_being_edited(edited) {}; + ~SnapCandidatePath() {}; + + Geom::PathVector* path_vector; + SnapTargetType target_type; + Geom::OptRect target_bbox; + bool currently_being_edited; // true for the path that's currently being edited in the node tool (if any) + +}; + +} // end of namespace Inkscape + +#endif /* !SEEN_SNAP_CANDIDATE_H */ diff --git a/src/snap-enums.h b/src/snap-enums.h new file mode 100644 index 000000000..cf09ba45b --- /dev/null +++ b/src/snap-enums.h @@ -0,0 +1,83 @@ +#ifndef SNAPENUMS_H_ +#define SNAPENUMS_H_ + +/** + * \file snap-enums.h + * \brief enumerations of snap source types and snap target types + * + * Authors: + * Diederik van Lierop <mail@diedenrezi.nl> + * + * Copyright (C) 2010 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +namespace Inkscape { + +enum SnapTargetType { + SNAPTARGET_UNDEFINED = 0, + SNAPTARGET_GRID, + SNAPTARGET_GRID_INTERSECTION, + SNAPTARGET_GUIDE, + SNAPTARGET_GUIDE_INTERSECTION, + SNAPTARGET_GUIDE_ORIGIN, + SNAPTARGET_GRID_GUIDE_INTERSECTION, + SNAPTARGET_NODE_SMOOTH, + SNAPTARGET_NODE_CUSP, + SNAPTARGET_LINE_MIDPOINT, + SNAPTARGET_OBJECT_MIDPOINT, + SNAPTARGET_ROTATION_CENTER, + SNAPTARGET_HANDLE, + SNAPTARGET_PATH, + SNAPTARGET_PATH_INTERSECTION, + SNAPTARGET_BBOX_CORNER, + SNAPTARGET_BBOX_EDGE, + SNAPTARGET_BBOX_EDGE_MIDPOINT, + SNAPTARGET_BBOX_MIDPOINT, + SNAPTARGET_PAGE_BORDER, + SNAPTARGET_PAGE_CORNER, + SNAPTARGET_CONVEX_HULL_CORNER, + SNAPTARGET_ELLIPSE_QUADRANT_POINT, + SNAPTARGET_CENTER, // of ellipse + SNAPTARGET_CORNER, // of image or of rectangle + SNAPTARGET_TEXT_BASELINE, + SNAPTARGET_CONSTRAINED_ANGLE, + SNAPTARGET_CONSTRAINT +}; + +enum SnapSourceType { + SNAPSOURCE_UNDEFINED = 0, + //------------------------------------------------------------------- + // Bbox points can be located at the edge of the stroke (for visual bboxes); they will therefore not snap + // to nodes because these are always located at the center of the stroke + SNAPSOURCE_BBOX_CATEGORY = 256, // will be used as a flag and must therefore be a power of two + SNAPSOURCE_BBOX_CORNER, + SNAPSOURCE_BBOX_MIDPOINT, + SNAPSOURCE_BBOX_EDGE_MIDPOINT, + //------------------------------------------------------------------- + // For the same reason, nodes will not snap to bbox points + SNAPSOURCE_NODE_CATEGORY = 512, // will be used as a flag and must therefore be a power of two + SNAPSOURCE_NODE_SMOOTH, + SNAPSOURCE_NODE_CUSP, + SNAPSOURCE_LINE_MIDPOINT, + SNAPSOURCE_PATH_INTERSECTION, + SNAPSOURCE_CORNER, // of image or of rectangle + SNAPSOURCE_CONVEX_HULL_CORNER, + SNAPSOURCE_ELLIPSE_QUADRANT_POINT, + SNAPSOURCE_NODE_HANDLE, // eg. nodes in the path editor, handles of stars or rectangles, etc. (tied to a stroke) + //------------------------------------------------------------------- + // Other points (e.g. guides, gradient knots) will snap to both bounding boxes and nodes + SNAPSOURCE_OTHER_CATEGORY = 1024, // will be used as a flag and must therefore be a power of two + SNAPSOURCE_OBJECT_MIDPOINT, + SNAPSOURCE_ROTATION_CENTER, + SNAPSOURCE_CENTER, // of ellipse + SNAPSOURCE_GUIDE, + SNAPSOURCE_GUIDE_ORIGIN, + SNAPSOURCE_TEXT_BASELINE, + SNAPSOURCE_OTHER_HANDLE, // eg. the handle of a gradient of a connector (ie not being tied to a stroke) + SNAPSOURCE_GRID_PITCH, // eg. when pasting or alt-dragging in the selector tool; not realy a snap source +}; + +} +#endif /* SNAPENUMS_H_ */ diff --git a/src/snap-preferences.cpp b/src/snap-preferences.cpp index 3e396a216..aa967349d 100644 --- a/src/snap-preferences.cpp +++ b/src/snap-preferences.cpp @@ -15,24 +15,19 @@ #include "inkscape.h" #include "snap-preferences.h" -Inkscape::SnapPreferences::PointType const Inkscape::SnapPreferences::SNAPPOINT_NODE = 0x1; -Inkscape::SnapPreferences::PointType const Inkscape::SnapPreferences::SNAPPOINT_BBOX = 0x2; -Inkscape::SnapPreferences::PointType const Inkscape::SnapPreferences::SNAPPOINT_GUIDE = 0x4; - - Inkscape::SnapPreferences::SnapPreferences() : - _include_item_center(false), - _intersectionGG(true), - _snap_to_grids(true), - _snap_to_guides(true), + _include_item_center(false), + _intersectionGG(true), + _snap_to_grids(true), + _snap_to_guides(true), _snap_enabled_globally(true), _snap_postponed_globally(false), _snap_to_itemnode(true), _snap_to_itempath(true), - _snap_to_bboxnode(true), _snap_to_bboxpath(true), - _snap_to_page_border(false), - _strict_snapping(true) + _snap_to_bboxnode(true), _snap_to_bboxpath(true), + _snap_to_page_border(false), + _strict_snapping(true) { - setSnapFrom(SNAPPOINT_BBOX | SNAPPOINT_NODE | SNAPPOINT_GUIDE, true); //Snap any point. In v0.45 and earlier, this was controlled in the preferences tab + setSnapFrom(SnapSourceType(SNAPSOURCE_BBOX_CATEGORY | SNAPSOURCE_NODE_CATEGORY | SNAPSOURCE_OTHER_CATEGORY), true); //Snap any point. In v0.45 and earlier, this was controlled in the preferences tab } /* @@ -47,54 +42,54 @@ Inkscape::SnapPreferences::SnapPreferences() : void Inkscape::SnapPreferences::setSnapModeBBox(bool enabled) { - if (enabled) { - _snap_from |= Inkscape::SnapPreferences::SNAPPOINT_BBOX; + if (enabled) { + _snap_from = SnapSourceType(_snap_from | Inkscape::SNAPSOURCE_BBOX_CATEGORY); } else { - _snap_from &= ~Inkscape::SnapPreferences::SNAPPOINT_BBOX; + _snap_from = SnapSourceType(_snap_from & ~Inkscape::SNAPSOURCE_BBOX_CATEGORY); } } bool Inkscape::SnapPreferences::getSnapModeBBox() const { - return (_snap_from & Inkscape::SnapPreferences::SNAPPOINT_BBOX); + return (_snap_from & Inkscape::SNAPSOURCE_BBOX_CATEGORY); } void Inkscape::SnapPreferences::setSnapModeNode(bool enabled) { - if (enabled) { - _snap_from |= Inkscape::SnapPreferences::SNAPPOINT_NODE; + if (enabled) { + _snap_from = SnapSourceType(_snap_from | Inkscape::SNAPSOURCE_NODE_CATEGORY); } else { - _snap_from &= ~Inkscape::SnapPreferences::SNAPPOINT_NODE; + _snap_from = SnapSourceType(_snap_from & ~Inkscape::SNAPSOURCE_NODE_CATEGORY); } } bool Inkscape::SnapPreferences::getSnapModeNode() const { - return (_snap_from & Inkscape::SnapPreferences::SNAPPOINT_NODE); + return (_snap_from & Inkscape::SNAPSOURCE_NODE_CATEGORY); } bool Inkscape::SnapPreferences::getSnapModeBBoxOrNodes() const { - return (_snap_from & (Inkscape::SnapPreferences::SNAPPOINT_BBOX | Inkscape::SnapPreferences::SNAPPOINT_NODE) ); + return (_snap_from & (Inkscape::SNAPSOURCE_BBOX_CATEGORY | Inkscape::SNAPSOURCE_NODE_CATEGORY) ); } bool Inkscape::SnapPreferences::getSnapModeAny() const { - return (_snap_from != 0); + return (_snap_from != 0); } void Inkscape::SnapPreferences::setSnapModeGuide(bool enabled) { - if (enabled) { - _snap_from |= Inkscape::SnapPreferences::SNAPPOINT_GUIDE; + if (enabled) { + _snap_from = SnapSourceType(_snap_from | Inkscape::SNAPSOURCE_OTHER_CATEGORY); } else { - _snap_from &= ~Inkscape::SnapPreferences::SNAPPOINT_GUIDE; + _snap_from = SnapSourceType(_snap_from & ~Inkscape::SNAPSOURCE_OTHER_CATEGORY); } } bool Inkscape::SnapPreferences::getSnapModeGuide() const { - return (_snap_from & Inkscape::SnapPreferences::SNAPPOINT_GUIDE); + return (_snap_from & Inkscape::SNAPSOURCE_OTHER_CATEGORY); } /** @@ -102,12 +97,12 @@ bool Inkscape::SnapPreferences::getSnapModeGuide() const * \param t Point type. * \param s true to snap to this point type, otherwise false; */ -void Inkscape::SnapPreferences::setSnapFrom(PointType t, bool s) +void Inkscape::SnapPreferences::setSnapFrom(Inkscape::SnapSourceType t, bool s) { if (s) { - _snap_from |= t; + _snap_from = SnapSourceType(_snap_from | t); } else { - _snap_from &= ~t; + _snap_from = SnapSourceType(_snap_from & ~t); } } @@ -115,7 +110,7 @@ void Inkscape::SnapPreferences::setSnapFrom(PointType t, bool s) * \param t Point type. * \return true if snapper will snap this type of point, otherwise false. */ -bool Inkscape::SnapPreferences::getSnapFrom(PointType t) const +bool Inkscape::SnapPreferences::getSnapFrom(Inkscape::SnapSourceType t) const { return (_snap_from & t); } diff --git a/src/snap-preferences.h b/src/snap-preferences.h index 63d7fba15..49d52cdb2 100644 --- a/src/snap-preferences.h +++ b/src/snap-preferences.h @@ -8,12 +8,13 @@ * Authors: * Diederik van Lierop <mail@diedenrezi.nl> * - * Copyright (C) 2008 Authors + * Copyright (C) 2008 - 2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ #include "helper/units.h" +#include "snap-enums.h" namespace Inkscape { @@ -21,13 +22,7 @@ namespace Inkscape class SnapPreferences { public: - SnapPreferences(); - - /// Point types to snap. - typedef int PointType; - static const PointType SNAPPOINT_NODE; - static const PointType SNAPPOINT_BBOX; - static const PointType SNAPPOINT_GUIDE; + SnapPreferences(); void setSnapModeBBox(bool enabled); void setSnapModeNode(bool enabled); @@ -44,20 +39,20 @@ public: void setSnapLineMidpoints(bool enabled) {_line_midpoints = enabled;} void setSnapObjectMidpoints(bool enabled) {_object_midpoints = enabled;} void setSnapBBoxEdgeMidpoints(bool enabled) {_bbox_edge_midpoints = enabled;} - void setSnapBBoxMidpoints(bool enabled) {_bbox_midpoints = enabled;} + void setSnapBBoxMidpoints(bool enabled) {_bbox_midpoints = enabled;} bool getSnapIntersectionGG() const {return _intersectionGG;} bool getSnapIntersectionCS() const {return _intersectionCS;} bool getSnapSmoothNodes() const {return _smoothNodes;} bool getSnapLineMidpoints() const {return _line_midpoints;} bool getSnapObjectMidpoints() const {return _object_midpoints;} bool getSnapBBoxEdgeMidpoints() const {return _bbox_edge_midpoints;} - bool getSnapBBoxMidpoints() const {return _bbox_midpoints;} + bool getSnapBBoxMidpoints() const {return _bbox_midpoints;} - void setSnapToGrids(bool enabled) {_snap_to_grids = enabled;} + void setSnapToGrids(bool enabled) {_snap_to_grids = enabled;} bool getSnapToGrids() const {return _snap_to_grids;} void setSnapToGuides(bool enabled) {_snap_to_guides = enabled;} - bool getSnapToGuides() const {return _snap_to_guides;} + bool getSnapToGuides() const {return _snap_to_guides;} void setIncludeItemCenter(bool enabled) {_include_item_center = enabled;} bool getIncludeItemCenter() const {return _include_item_center;} @@ -68,29 +63,29 @@ public: void setSnapPostponedGlobally(bool postponed) {_snap_postponed_globally = postponed;} bool getSnapPostponedGlobally() const {return _snap_postponed_globally;} - void setSnapFrom(PointType t, bool s); - bool getSnapFrom(PointType t) const; + void setSnapFrom(Inkscape::SnapSourceType t, bool s); + bool getSnapFrom(Inkscape::SnapSourceType t) const; // These will only be used for the object snapper void setSnapToItemNode(bool s) {_snap_to_itemnode = s;} - bool getSnapToItemNode() const {return _snap_to_itemnode;} - void setSnapToItemPath(bool s) {_snap_to_itempath = s;} - bool getSnapToItemPath() const {return _snap_to_itempath;} - void setSnapToBBoxNode(bool s) {_snap_to_bboxnode = s;} - bool getSnapToBBoxNode() const {return _snap_to_bboxnode;} - void setSnapToBBoxPath(bool s) {_snap_to_bboxpath = s;} - bool getSnapToBBoxPath() const {return _snap_to_bboxpath;} - void setSnapToPageBorder(bool s) {_snap_to_page_border = s;} - bool getSnapToPageBorder() const {return _snap_to_page_border;} - bool getStrictSnapping() const {return _strict_snapping;} - - gdouble getGridTolerance() const {return _grid_tolerance;} - gdouble getGuideTolerance() const {return _guide_tolerance;} - gdouble getObjectTolerance() const {return _object_tolerance;} - - void setGridTolerance(gdouble val) {_grid_tolerance = val;} - void setGuideTolerance(gdouble val) {_guide_tolerance = val;} - void setObjectTolerance(gdouble val) {_object_tolerance = val;} + bool getSnapToItemNode() const {return _snap_to_itemnode;} + void setSnapToItemPath(bool s) {_snap_to_itempath = s;} + bool getSnapToItemPath() const {return _snap_to_itempath;} + void setSnapToBBoxNode(bool s) {_snap_to_bboxnode = s;} + bool getSnapToBBoxNode() const {return _snap_to_bboxnode;} + void setSnapToBBoxPath(bool s) {_snap_to_bboxpath = s;} + bool getSnapToBBoxPath() const {return _snap_to_bboxpath;} + void setSnapToPageBorder(bool s) {_snap_to_page_border = s;} + bool getSnapToPageBorder() const {return _snap_to_page_border;} + bool getStrictSnapping() const {return _strict_snapping;} + + gdouble getGridTolerance() const {return _grid_tolerance;} + gdouble getGuideTolerance() const {return _guide_tolerance;} + gdouble getObjectTolerance() const {return _object_tolerance;} + + void setGridTolerance(gdouble val) {_grid_tolerance = val;} + void setGuideTolerance(gdouble val) {_guide_tolerance = val;} + void setObjectTolerance(gdouble val) {_object_tolerance = val;} private: @@ -101,28 +96,28 @@ private: bool _line_midpoints; bool _object_midpoints; // the midpoint of shapes (e.g. a circle, rect, polygon) or of any other shape (at [h/2, w/2]) bool _bbox_edge_midpoints; - bool _bbox_midpoints; - bool _snap_to_grids; - bool _snap_to_guides; + bool _bbox_midpoints; + bool _snap_to_grids; + bool _snap_to_guides; bool _snap_enabled_globally; // Toggles ALL snapping bool _snap_postponed_globally; // Hold all snapping temporarily when the mouse is moving fast - PointType _snap_from; ///< bitmap of point types that we will snap from + SnapSourceType _snap_from; ///< bitmap of point types that we will snap from // These will only be used for the object snapper bool _snap_to_itemnode; - bool _snap_to_itempath; - bool _snap_to_bboxnode; - bool _snap_to_bboxpath; - bool _snap_to_page_border; - //If enabled, then bbox corners will only snap to bboxes, - //and nodes will only snap to nodes and paths. We will not - //snap bbox corners to nodes, or nodes to bboxes. - //(snapping to grids and guides is not affected by this) - bool _strict_snapping; - - gdouble _grid_tolerance; - gdouble _guide_tolerance; - gdouble _object_tolerance; + bool _snap_to_itempath; + bool _snap_to_bboxnode; + bool _snap_to_bboxpath; + bool _snap_to_page_border; + //If enabled, then bbox corners will only snap to bboxes, + //and nodes will only snap to nodes and paths. We will not + //snap bbox corners to nodes, or nodes to bboxes. + //(snapping to grids and guides is not affected by this) + bool _strict_snapping; + + gdouble _grid_tolerance; + gdouble _guide_tolerance; + gdouble _object_tolerance; }; } diff --git a/src/snap.cpp b/src/snap.cpp index 2e38e4f14..b8b08dad5 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -13,7 +13,7 @@ * * Copyright (C) 2006-2007 Johan Engelen <johan@shouraizou.nl> * Copyrigth (C) 2004 Nathan Hurst - * Copyright (C) 1999-2009 Authors + * Copyright (C) 1999-2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -30,6 +30,7 @@ #include "inkscape.h" #include "desktop.h" +#include "selection.h" #include "sp-guide.h" #include "preferences.h" #include "event-context.h" @@ -158,24 +159,23 @@ bool SnapManager::gridSnapperMightSnap() const * because the original position should not be touched, then freeSnap() should be * called instead. * - * PS: SnapManager::setup() must have been called before calling this method, + * PS: + * 1) SnapManager::setup() must have been called before calling this method, * but only once for a set of points + * 2) Only to be used when a single source point is to be snapped; it assumes + * that source_num = 0, which is inefficient when snapping sets our source points * - * \param point_type Category of points to which the source point belongs: node, guide or bounding box * \param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred * \param source_type Detailed description of the source type, will be used by the snap indicator - * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation */ -void SnapManager::freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, - Geom::Point &p, +void SnapManager::freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, - long source_num, Geom::OptRect const &bbox_to_snap) const { - //TODO: PointType and source_type are somewhat redundant; can't we get rid of the point_type parameter? - Inkscape::SnappedPoint const s = freeSnap(point_type, p, source_type, source_num, bbox_to_snap); + //TODO: SnapCandidatePoint and point_type are somewhat redundant; can't we get rid of the point_type parameter? + Inkscape::SnappedPoint const s = freeSnap(Inkscape::SnapCandidatePoint(p, source_type), bbox_to_snap); s.getPoint(p); } @@ -191,47 +191,43 @@ void SnapManager::freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point * PS: SnapManager::setup() must have been called before calling this method, * but only once for a set of points * - * \param point_type Category of points to which the source point belongs: node, guide or bounding box - * \param p Current position of the snap source - * \param source_type Detailed description of the source type, will be used by the snap indicator - * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero + * \param p Source point to be snapped * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics */ -Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapPreferences::PointType point_type, - Geom::Point const &p, - Inkscape::SnapSourceType const &source_type, - long source_num, +Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap) const { if (!someSnapperMightSnap()) { - return Inkscape::SnappedPoint(p, source_type, 0, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); - } - - std::vector<SPItem const *> *items_to_ignore; - if (_item_to_ignore) { // If we have only a single item to ignore - // then build a list containing this single item; - // This single-item list will prevail over any other _items_to_ignore list, should that exist - items_to_ignore = new std::vector<SPItem const *>; - items_to_ignore->push_back(_item_to_ignore); - } else { - items_to_ignore = _items_to_ignore; + return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); } SnappedConstraints sc; SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->freeSnap(sc, point_type, p, source_type, source_num, bbox_to_snap, items_to_ignore, _unselected_nodes); + (*i)->freeSnap(sc, p, bbox_to_snap, &_items_to_ignore, _unselected_nodes); } - if (_item_to_ignore) { - delete items_to_ignore; - } + return findBestSnap(p, sc, false); +} + +void SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p) +{ + // setup() must have been called before calling this method! - return findBestSnap(p, source_type, sc, false); + if (_snapindicator) { + _snapindicator = false; // prevent other methods from drawing a snap indicator; we want to control this here + Inkscape::SnappedPoint s = freeSnap(p); + if (s.getSnapped()) { + _desktop->snapindicator->set_new_snaptarget(s, true); + } else { + _desktop->snapindicator->remove_snaptarget(true); + } + _snapindicator = true; // restore the original value + } } /** @@ -253,15 +249,16 @@ Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapPreferences::PointTyp * \return Offset vector after snapping to the closest multiple of a grid pitch */ -Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t) const +Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point const &origin) { - if (!snapprefs.getSnapEnabledGlobally()) // No need to check for snapprefs.getSnapPostponedGlobally() here + if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally()) return t; if (_desktop && _desktop->gridsEnabled()) { bool success = false; Geom::Point nearest_multiple; Geom::Coord nearest_distance = NR_HUGE; + Inkscape::SnappedPoint bestSnappedPoint(t); // It will snap to the grid for which we find the closest snap. This might be a different // grid than to which the objects were initially aligned. I don't see an easy way to fix @@ -280,21 +277,28 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t) const Geom::Point const t_offset = t + grid->origin; SnappedConstraints sc; // Only the first three parameters are being used for grid snappers - snapper->freeSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_NODE, t_offset, Inkscape::SNAPSOURCE_UNDEFINED, 0, Geom::OptRect(), NULL, NULL); + snapper->freeSnap(sc, Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH),Geom::OptRect(), NULL, NULL); // Find the best snap for this grid, including intersections of the grid-lines - Inkscape::SnappedPoint s = findBestSnap(t_offset, Inkscape::SNAPSOURCE_UNDEFINED, sc, false); + bool old_val = _snapindicator; + _snapindicator = false; + Inkscape::SnappedPoint s = findBestSnap(Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH), sc, false, false, true); + _snapindicator = old_val; if (s.getSnapped() && (s.getSnapDistance() < nearest_distance)) { // use getSnapDistance() instead of getWeightedDistance() here because the pointer's position // doesn't tell us anything about which node to snap success = true; nearest_multiple = s.getPoint() - to_2geom(grid->origin); nearest_distance = s.getSnapDistance(); + bestSnappedPoint = s; } } } - if (success) + if (success) { + bestSnappedPoint.setPoint(origin + nearest_multiple); + _desktop->snapindicator->set_new_snaptarget(bestSnappedPoint); return nearest_multiple; + } } return t; @@ -316,25 +320,25 @@ Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t) const * because the original position should not be touched, then constrainedSnap() should * be called instead. * - * PS: SnapManager::setup() must have been called before calling this method, + * PS: + * 1) SnapManager::setup() must have been called before calling this method, * but only once for a set of points + * 2) Only to be used when a single source point is to be snapped; it assumes + * that source_num = 0, which is inefficient when snapping sets our source points + * - * \param point_type Category of points to which the source point belongs: node, guide or bounding box * \param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred * \param source_type Detailed description of the source type, will be used by the snap indicator * \param constraint The direction or line along which snapping must occur - * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation */ -void SnapManager::constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, - Geom::Point &p, +void SnapManager::constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Inkscape::Snapper::ConstraintLine const &constraint, - long source_num, Geom::OptRect const &bbox_to_snap) const { - Inkscape::SnappedPoint const s = constrainedSnap(point_type, p, source_type, constraint, source_num, bbox_to_snap); + Inkscape::SnappedPoint const s = constrainedSnap(Inkscape::SnapCandidatePoint(p, source_type, 0), constraint, bbox_to_snap); s.getPoint(p); } @@ -349,51 +353,41 @@ void SnapManager::constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointTyp * PS: SnapManager::setup() must have been called before calling this method, * but only once for a set of points * - * \param point_type Category of points to which the source point belongs: node, guide or bounding box - * \param p Current position of the snap source - * \param source_type Detailed description of the source type, will be used by the snap indicator + * \param p Source point to be snapped * \param constraint The direction or line along which snapping must occur - * \param source_num Sequence number of the source point within the set of points that is to be snapped. Starting at zero * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation */ -Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapPreferences::PointType point_type, - Geom::Point const &p, - Inkscape::SnapSourceType const &source_type, +Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint const &p, Inkscape::Snapper::ConstraintLine const &constraint, - long source_num, Geom::OptRect const &bbox_to_snap) const { - if (!someSnapperMightSnap()) { - return Inkscape::SnappedPoint(p, source_type, 0, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); - } - - std::vector<SPItem const *> *items_to_ignore; - if (_item_to_ignore) { // If we have only a single item to ignore - // then build a list containing this single item; - // This single-item list will prevail over any other _items_to_ignore list, should that exist - items_to_ignore = new std::vector<SPItem const *>; - items_to_ignore->push_back(_item_to_ignore); - } else { - items_to_ignore = _items_to_ignore; - } + // First project the mouse pointer onto the constraint + Geom::Point pp = constraint.projection(p.getPoint()); + Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::L2(pp - p.getPoint()), 0, false, false); - // First project the mouse pointer onto the constraint - Geom::Point pp = constraint.projection(p); - // Then try to snap the projected point + if (!someSnapperMightSnap()) { + // Always return point on constraint + return no_snap; + } SnappedConstraints sc; SnapperList const snappers = getSnappers(); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, point_type, pp, source_type, source_num, bbox_to_snap, constraint, items_to_ignore); + (*i)->constrainedSnap(sc, p, bbox_to_snap, constraint, &_items_to_ignore); } - if (_item_to_ignore) { - delete items_to_ignore; - } + Inkscape::SnappedPoint result = findBestSnap(p, sc, true); - return findBestSnap(pp, source_type, sc, true); + if (result.getSnapped()) { + // only change the snap indicator if we really snapped to something + if (_snapindicator) { + _desktop->snapindicator->set_new_snaptarget(result); + } + return result; + } + return no_snap; } /** @@ -418,9 +412,9 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, return; } - Inkscape::SnapSourceType source_type = Inkscape::SNAPSOURCE_GUIDE_ORIGIN; + Inkscape::SnapCandidatePoint candidate(p, Inkscape::SNAPSOURCE_GUIDE_ORIGIN); if (drag_type == SP_DRAG_ROTATE) { - source_type = Inkscape::SNAPSOURCE_GUIDE; + candidate = Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_GUIDE); } // Snap to nodes @@ -433,11 +427,11 @@ void SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SnapperList snappers = getGridSnappers(); snappers.push_back(&guide); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->freeSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, 0, Geom::OptRect(), NULL, NULL); + (*i)->freeSnap(sc, candidate, Geom::OptRect(), NULL, NULL); } // Snap to intersections of curves, but not to the curves themselves! (see _snapTranslatingGuideToNodes in object-snapper.cpp) - Inkscape::SnappedPoint const s = findBestSnap(p, source_type, sc, false, true); + Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false, true); s.getPoint(p); } @@ -466,23 +460,23 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) return; } - Inkscape::SnapSourceType source_type = Inkscape::SNAPSOURCE_GUIDE_ORIGIN; + Inkscape::SnapCandidatePoint candidate(p, Inkscape::SNAPSOURCE_GUIDE_ORIGIN, Inkscape::SNAPTARGET_UNDEFINED); // Snap to nodes or paths SnappedConstraints sc; Inkscape::Snapper::ConstraintLine cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line)); if (object.ThisSnapperMightSnap()) { - object.constrainedSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, 0, Geom::OptRect(), cl, NULL); + object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL); } // Snap to guides & grid lines SnapperList snappers = getGridSnappers(); snappers.push_back(&guide); for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) { - (*i)->constrainedSnap(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, source_type, 0, Geom::OptRect(), cl, NULL); + (*i)->constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL); } - Inkscape::SnappedPoint const s = findBestSnap(p, source_type, sc, false); + Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false); s.getPoint(p); } @@ -499,7 +493,6 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) * some metrics to quantify the snap "distance", and see if it's better than the * previous snap. Finally, the best ("nearest") snap from all these points is returned. * - * \param type Category of points to which the source point belongs: node or bounding box. * \param points Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param constrained true if the snap is constrained, e.g. for stretching or for purely horizontal translation. @@ -513,8 +506,7 @@ void SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) */ Inkscape::SnappedPoint SnapManager::_snapTransformed( - Inkscape::SnapPreferences::PointType type, - std::vector<std::pair<Geom::Point, int> > const &points, + std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer, bool constrained, Inkscape::Snapper::ConstraintLine const &constraint, @@ -533,13 +525,14 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( ** Also used to globally disable all snapping */ if (someSnapperMightSnap() == false) { - return Inkscape::SnappedPoint(); + return Inkscape::SnappedPoint(pointer); } - std::vector<std::pair<Geom::Point, int> > transformed_points; + std::vector<Inkscape::SnapCandidatePoint> transformed_points; Geom::Rect bbox; - for (std::vector<std::pair<Geom::Point, int> >::const_iterator i = points.begin(); i != points.end(); i++) { + long source_num = 0; + for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) { /* Work out the transformed version of this point */ Geom::Point transformed = _transformPoint(*i, transformation_type, transformation, origin, dim, uniform); @@ -551,7 +544,8 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( bbox.expandTo(transformed); } - transformed_points.push_back(std::make_pair(transformed, (*i).second)); + transformed_points.push_back(Inkscape::SnapCandidatePoint(transformed, (*i).getSourceType(), source_num)); + source_num++; } /* The current best transformation */ @@ -565,16 +559,16 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point g_assert(best_snapped_point.getAtIntersection() == false); - std::vector<std::pair<Geom::Point, int> >::const_iterator j = transformed_points.begin(); - long source_num = 0; + std::vector<Inkscape::SnapCandidatePoint>::const_iterator j = transformed_points.begin(); + // std::cout << std::endl; - for (std::vector<std::pair<Geom::Point, int> >::const_iterator i = points.begin(); i != points.end(); i++) { + for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) { /* Snap it */ Inkscape::SnappedPoint snapped_point; Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - Geom::Point const b = ((*i).first - origin); // vector to original point + Geom::Point const b = ((*i).getPoint() - origin); // vector to original point if (constrained) { if ((transformation_type == SCALE || transformation_type == STRETCH) && uniform) { @@ -583,18 +577,18 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // calculate that line here dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, b); } else if (transformation_type == STRETCH) { // when non-uniform stretching { - dedicated_constraint = Inkscape::Snapper::ConstraintLine((*i).first, component_vectors[dim]); + dedicated_constraint = Inkscape::Snapper::ConstraintLine((*i).getPoint(), component_vectors[dim]); } else if (transformation_type == TRANSLATION) { // When doing a constrained translation, all points will move in the same direction, i.e. // either horizontally or vertically. The lines along which they move are therefore all // parallel, but might not be colinear. Therefore we will have to set the point through // which the constraint-line runs here, for each point individually. - dedicated_constraint.setPoint((*i).first); + dedicated_constraint.setPoint((*i).getPoint()); } // else: leave the original constraint, e.g. for skewing if (transformation_type == SCALE && !uniform) { g_warning("Non-uniform constrained scaling is not supported!"); } - snapped_point = constrainedSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), dedicated_constraint, source_num, bbox); + snapped_point = constrainedSnap(*j, dedicated_constraint, bbox); } else { bool const c1 = fabs(b[Geom::X]) < 1e-6; bool const c2 = fabs(b[Geom::Y]) < 1e-6; @@ -603,16 +597,15 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // move in that specific direction; therefore it should only snap in that direction, otherwise // we will get snapped points with an invalid transformation dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]); - snapped_point = constrainedSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), dedicated_constraint, source_num, bbox); + snapped_point = constrainedSnap(*j, dedicated_constraint, bbox); } else { - snapped_point = freeSnap(type, (*j).first, static_cast<Inkscape::SnapSourceType>((*j).second), source_num, bbox); + snapped_point = freeSnap(*j, bbox); } } // std::cout << "dist = " << snapped_point.getSnapDistance() << std::endl; - snapped_point.setPointerDistance(Geom::L2(pointer - (*i).first)); + snapped_point.setPointerDistance(Geom::L2(pointer - (*i).getPoint())); Geom::Point result; - Geom::Point scale_metric(NR_HUGE, NR_HUGE); if (snapped_point.getSnapped()) { /* We snapped. Find the transformation that describes where the snapped point has @@ -623,7 +616,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( switch (transformation_type) { case TRANSLATION: - result = snapped_point.getPoint() - (*i).first; + result = snapped_point.getPoint() - (*i).getPoint(); /* Consider the case in which a box is almost aligned with a grid in both * horizontal and vertical directions. The distance to the intersection of * the grid lines will always be larger then the distance to a single grid @@ -653,8 +646,17 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // if scaling didn't occur in the other direction } } + if (uniform) { + if (fabs(result[0]) < fabs(result[1])) { + result[1] = result[0]; + } else { + result[0] = result[1]; + } + } // Compare the resulting scaling with the desired scaling - scale_metric = result - transformation; // One or both of its components might be NR_HUGE + Geom::Point scale_metric = Geom::abs(result - transformation); // One or both of its components might be NR_HUGE + snapped_point.setSnapDistance(std::min(scale_metric[0], scale_metric[1])); + snapped_point.setSecondSnapDistance(std::max(scale_metric[0], scale_metric[1])); break; } case STRETCH: @@ -673,7 +675,7 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( snapped_point.setSecondSnapDistance(NR_HUGE); break; case SKEW: - result[0] = (snapped_point.getPoint()[dim] - ((*i).first)[dim]) / (((*i).first)[1 - dim] - origin[1 - dim]); // skew factor + result[0] = (snapped_point.getPoint()[dim] - ((*i).getPoint())[dim]) / (((*i).getPoint())[1 - dim] - origin[1 - dim]); // skew factor result[1] = transformation[1]; // scale factor // Store the metric for this transformation as a virtual distance snapped_point.setSnapDistance(std::abs(result[0] - transformation[0])); @@ -686,37 +688,13 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( // When scaling, we're considering the best transformation in each direction separately. We will have a metric in each // direction, whereas for all other transformation we only a single one-dimensional metric. That's why we need to handle // the scaling metric differently - if (transformation_type == SCALE) { - for (int index = 0; index < 2; index++) { - if (fabs(scale_metric[index]) < fabs(best_scale_metric[index])) { - best_transformation[index] = result[index]; - best_scale_metric[index] = fabs(scale_metric[index]); - // When scaling, we're considering the best transformation in each direction separately - // Therefore two different snapped points might together make a single best transformation - // We will however return only a single snapped point (e.g. to display the snapping indicator) - best_snapped_point = snapped_point; - // std::cout << "SEL "; - } // else { std::cout << " ";} - } - if (uniform) { - if (best_scale_metric[0] < best_scale_metric[1]) { - best_transformation[1] = best_transformation[0]; - best_scale_metric[1] = best_scale_metric[0]; - } else { - best_transformation[0] = best_transformation[1]; - best_scale_metric[0] = best_scale_metric[1]; - } - } - } else { // For all transformations other than scaling - if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) { - best_transformation = result; - best_snapped_point = snapped_point; - } + if (best_snapped_point.isOtherSnapBetter(snapped_point, true)) { + best_transformation = result; + best_snapped_point = snapped_point; } } j++; - source_num++; } Geom::Coord best_metric; @@ -731,11 +709,9 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( } } } - best_metric = std::min(best_scale_metric[0], best_scale_metric[1]); - } else { // For all transformations other than scaling - best_metric = best_snapped_point.getSnapDistance(); } + best_metric = best_snapped_point.getSnapDistance(); best_snapped_point.setTransformation(best_transformation); // Using " < 1e6" instead of " < NR_HUGE" for catching some rounding errors // These rounding errors might be caused by NRRects, see bug #1584301 @@ -747,23 +723,22 @@ Inkscape::SnappedPoint SnapManager::_snapTransformed( /** * \brief Apply a translation to a set of points and try to snap freely in 2 degrees-of-freedom * - * \param point_type Category of points to which the source point belongs: node or bounding box. * \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source. * \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed). * \param tr Proposed translation; the final translation can only be calculated after snapping has occurred * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::freeSnapTranslation(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, +Inkscape::SnappedPoint SnapManager::freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Point const &tr) const { if (p.size() == 1) { - _displaySnapsource(point_type, std::make_pair(_transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false), (p.at(0)).second)); + Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(point_type, p, pointer, false, Geom::Point(0,0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); } /** @@ -777,17 +752,17 @@ Inkscape::SnappedPoint SnapManager::freeSnapTranslation(Inkscape::SnapPreference * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::constrainedSnapTranslation(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, +Inkscape::SnappedPoint SnapManager::constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Inkscape::Snapper::ConstraintLine const &constraint, Geom::Point const &tr) const { if (p.size() == 1) { - _displaySnapsource(point_type, std::make_pair(_transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false), (p.at(0)).second)); + Geom::Point pt = _transformPoint(p.at(0), TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(point_type, p, pointer, true, constraint, TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); + return _snapTransformed(p, pointer, true, constraint, TRANSLATION, tr, Geom::Point(0,0), Geom::X, false); } @@ -802,17 +777,17 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapTranslation(Inkscape::SnapPre * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::freeSnapScale(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, +Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Scale const &s, Geom::Point const &o) const { if (p.size() == 1) { - _displaySnapsource(point_type, std::make_pair(_transformPoint(p.at(0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false), (p.at(0)).second)); + Geom::Point pt = _transformPoint(p.at(0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(point_type, p, pointer, false, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false); + return _snapTransformed(p, pointer, false, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false); } @@ -827,18 +802,18 @@ Inkscape::SnappedPoint SnapManager::freeSnapScale(Inkscape::SnapPreferences::Poi * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::constrainedSnapScale(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, +Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Scale const &s, Geom::Point const &o) const { // When constrained scaling, only uniform scaling is supported. if (p.size() == 1) { - _displaySnapsource(point_type, std::make_pair(_transformPoint(p.at(0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true), (p.at(0)).second)); + Geom::Point pt = _transformPoint(p.at(0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(point_type, p, pointer, true, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true); + return _snapTransformed(p, pointer, true, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true); } /** @@ -854,8 +829,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapScale(Inkscape::SnapPreferenc * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, +Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Coord const &s, Geom::Point const &o, @@ -863,10 +837,11 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(Inkscape::SnapPrefere bool u) const { if (p.size() == 1) { - _displaySnapsource(point_type, std::make_pair(_transformPoint(p.at(0), STRETCH, Geom::Point(s, s), o, d, u), (p.at(0)).second)); + Geom::Point pt = _transformPoint(p.at(0), STRETCH, Geom::Point(s, s), o, d, u); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(point_type, p, pointer, true, Geom::Point(0,0), STRETCH, Geom::Point(s, s), o, d, u); + return _snapTransformed(p, pointer, true, Geom::Point(0,0), STRETCH, Geom::Point(s, s), o, d, u); } /** @@ -882,8 +857,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(Inkscape::SnapPrefere * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics. */ -Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, +Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Inkscape::Snapper::ConstraintLine const &constraint, Geom::Point const &s, @@ -897,32 +871,35 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(Inkscape::SnapPreference // NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew, // so it's corners have a different transformation. The snappers cannot handle this, therefore snapping // of bounding boxes is not allowed here. - g_assert(!(point_type & Inkscape::SnapPreferences::SNAPPOINT_BBOX)); + if (p.size() > 0) { + g_assert(!(p.at(0).getSourceType() & Inkscape::SNAPSOURCE_BBOX_CATEGORY)); + } if (p.size() == 1) { - _displaySnapsource(point_type, std::make_pair(_transformPoint(p.at(0), SKEW, s, o, d, false), (p.at(0)).second)); + Geom::Point pt = _transformPoint(p.at(0), SKEW, s, o, d, false); + _displaySnapsource(Inkscape::SnapCandidatePoint(pt, p.at(0).getSourceType())); } - return _snapTransformed(point_type, p, pointer, true, constraint, SKEW, s, o, d, false); + return _snapTransformed(p, pointer, true, constraint, SKEW, s, o, d, false); } /** * \brief Given a set of possible snap targets, find the best target (which is not necessarily * also the nearest target), and show the snap indicator if requested * - * \param p Current position of the snap source - * \param source_type Detailed description of the source type, will be used by the snap indicator + * \param p Source point to be snapped * \param sc A structure holding all snap targets that have been found so far * \param constrained True if the snap is constrained, e.g. for stretching or for purely horizontal translation. - * \param noCurves If true, then do consider snapping to intersections of curves, but not to the curves themself + * \param noCurves If true, then do consider snapping to intersections of curves, but not to the curves themselves + * \param allowOffScreen If true, then snapping to points which are off the screen is allowed (needed for example when pasting to the grid) * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics */ -Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, - Inkscape::SnapSourceType const source_type, - SnappedConstraints &sc, +Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint const &p, + SnappedConstraints const &sc, bool constrained, - bool noCurves) const + bool noCurves, + bool allowOffScreen) const { /* @@ -954,8 +931,8 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, if (snapprefs.getSnapIntersectionCS()) { // search for the closest snapped intersection of curves Inkscape::SnappedPoint closestCurvesIntersection; - if (getClosestIntersectionCS(sc.curves, p, closestCurvesIntersection, _desktop->dt2doc())) { - closestCurvesIntersection.setSource(source_type); + if (getClosestIntersectionCS(sc.curves, p.getPoint(), closestCurvesIntersection, _desktop->dt2doc())) { + closestCurvesIntersection.setSource(p.getSourceType()); sp_list.push_back(closestCurvesIntersection); } } @@ -982,7 +959,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, // search for the closest snapped intersection of grid lines Inkscape::SnappedPoint closestGridPoint; if (getClosestIntersectionSL(sc.grid_lines, closestGridPoint)) { - closestGridPoint.setSource(source_type); + closestGridPoint.setSource(p.getSourceType()); closestGridPoint.setTarget(Inkscape::SNAPTARGET_GRID_INTERSECTION); sp_list.push_back(closestGridPoint); } @@ -990,7 +967,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, // search for the closest snapped intersection of guide lines Inkscape::SnappedPoint closestGuidePoint; if (getClosestIntersectionSL(sc.guide_lines, closestGuidePoint)) { - closestGuidePoint.setSource(source_type); + closestGuidePoint.setSource(p.getSourceType()); closestGuidePoint.setTarget(Inkscape::SNAPTARGET_GUIDE_INTERSECTION); sp_list.push_back(closestGuidePoint); } @@ -999,7 +976,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, if (snapprefs.getSnapIntersectionGG()) { Inkscape::SnappedPoint closestGridGuidePoint; if (getClosestIntersectionSL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) { - closestGridGuidePoint.setSource(source_type); + closestGridGuidePoint.setSource(p.getSourceType()); closestGridGuidePoint.setTarget(Inkscape::SNAPTARGET_GRID_GUIDE_INTERSECTION); sp_list.push_back(closestGridGuidePoint); } @@ -1007,16 +984,18 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, } // now let's see which snapped point gets a thumbs up - Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_UNDEFINED, 0, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false); + Inkscape::SnappedPoint bestSnappedPoint(p.getPoint()); // std::cout << "Finding the best snap..." << std::endl; for (std::list<Inkscape::SnappedPoint>::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) { - // first find out if this snapped point is within snapping range - // std::cout << "sp = " << from_2geom((*i).getPoint()); - if ((*i).getSnapDistance() <= (*i).getTolerance()) { - // if it's the first point, or if it is closer than the best snapped point so far - if (i == sp_list.begin() || bestSnappedPoint.isOtherSnapBetter(*i, false)) { - // then prefer this point over the previous one - bestSnappedPoint = *i; + // std::cout << "sp = " << (*i).getPoint() << " | source = " << (*i).getSource() << " | target = " << (*i).getTarget(); + bool onScreen = _desktop->get_display_area().contains((*i).getPoint()); + if (onScreen || allowOffScreen) { // Only snap to points which are not off the screen + if ((*i).getSnapDistance() <= (*i).getTolerance()) { // Only snap to points within snapping range + // if it's the first point, or if it is closer than the best snapped point so far + if (i == sp_list.begin() || bestSnappedPoint.isOtherSnapBetter(*i, false)) { + // then prefer this point over the previous one + bestSnappedPoint = *i; + } } } // std::cout << std::endl; @@ -1035,31 +1014,16 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Geom::Point const &p, return bestSnappedPoint; } -/** - * \brief Prepare the snap manager for the actual snapping, which includes building a list of snap targets - * to ignore and toggling the snap indicator - * - * There are two overloaded setup() methods, of which this one only allows for a single item to be ignored - * whereas the other one will take a list of items to ignore - * - * \param desktop Reference to the desktop to which this snap manager is attached - * \param snapindicator If true then a snap indicator will be displayed automatically (when enabled in the preferences) - * \param item_to_ignore This item will not be snapped to, e.g. the item that is currently being dragged. This avoids "self-snapping" - * \param unselected_nodes Stationary nodes of the path that is currently being edited in the node tool and - * that can be snapped too. Nodes not in this list will not be snapped to, to avoid "self-snapping". Of each - * unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored - * \param guide_to_ignore Guide that is currently being dragged and should not be snapped to - */ - +/// Convenience shortcut when there is only one item to ignore void SnapManager::setup(SPDesktop const *desktop, bool snapindicator, SPItem const *item_to_ignore, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes, SPGuide *guide_to_ignore) { g_assert(desktop != NULL); - _item_to_ignore = item_to_ignore; - _items_to_ignore = NULL; + _items_to_ignore.clear(); + _items_to_ignore.push_back(item_to_ignore); _desktop = desktop; _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; @@ -1085,16 +1049,34 @@ void SnapManager::setup(SPDesktop const *desktop, void SnapManager::setup(SPDesktop const *desktop, bool snapindicator, std::vector<SPItem const *> &items_to_ignore, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes, SPGuide *guide_to_ignore) { g_assert(desktop != NULL); - _item_to_ignore = NULL; - _items_to_ignore = &items_to_ignore; + _items_to_ignore = items_to_ignore; + _desktop = desktop; + _snapindicator = snapindicator; + _unselected_nodes = unselected_nodes; + _guide_to_ignore = guide_to_ignore; +} + +/// Setup, taking the list of items to ignore from the desktop's selection. +void SnapManager::setupIgnoreSelection(SPDesktop const *desktop, + bool snapindicator, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes, + SPGuide *guide_to_ignore) +{ _desktop = desktop; _snapindicator = snapindicator; _unselected_nodes = unselected_nodes; _guide_to_ignore = guide_to_ignore; + _items_to_ignore.clear(); + + Inkscape::Selection *sel = _desktop->selection; + GSList const *items = sel->itemList(); + for (GSList *i = const_cast<GSList*>(items); i; i = i->next) { + _items_to_ignore.push_back(static_cast<SPItem const *>(i->data)); + } } SPDocument *SnapManager::getDocument() const @@ -1114,7 +1096,7 @@ SPDocument *SnapManager::getDocument() const * \return The position of the point after transformation */ -Geom::Point SnapManager::_transformPoint(std::pair<Geom::Point, int> const &p, +Geom::Point SnapManager::_transformPoint(Inkscape::SnapCandidatePoint const &p, Transformation const transformation_type, Geom::Point const &transformation, Geom::Point const &origin, @@ -1125,10 +1107,10 @@ Geom::Point SnapManager::_transformPoint(std::pair<Geom::Point, int> const &p, Geom::Point transformed; switch (transformation_type) { case TRANSLATION: - transformed = p.first + transformation; + transformed = p.getPoint() + transformation; break; case SCALE: - transformed = (p.first - origin) * Geom::Scale(transformation[Geom::X], transformation[Geom::Y]) + origin; + transformed = (p.getPoint() - origin) * Geom::Scale(transformation[Geom::X], transformation[Geom::Y]) + origin; break; case STRETCH: { @@ -1139,15 +1121,15 @@ Geom::Point SnapManager::_transformPoint(std::pair<Geom::Point, int> const &p, s[dim] = transformation[dim]; s[1 - dim] = 1; } - transformed = ((p.first - origin) * s) + origin; + transformed = ((p.getPoint() - origin) * s) + origin; break; } case SKEW: // Apply the skew factor - transformed[dim] = (p.first)[dim] + transformation[0] * ((p.first)[1 - dim] - origin[1 - dim]); + transformed[dim] = (p.getPoint())[dim] + transformation[0] * ((p.getPoint())[1 - dim] - origin[1 - dim]); // While skewing, mirroring and scaling (by integer multiples) in the opposite direction is also allowed. // Apply that scale factor here - transformed[1-dim] = (p.first - origin)[1 - dim] * transformation[1] + origin[1 - dim]; + transformed[1-dim] = (p.getPoint() - origin)[1 - dim] * transformation[1] + origin[1 - dim]; break; default: g_assert_not_reached(); @@ -1163,12 +1145,13 @@ Geom::Point SnapManager::_transformPoint(std::pair<Geom::Point, int> const &p, * \param p The transformed position of the source point, paired with an identifier of the type of the snap source. */ -void SnapManager::_displaySnapsource(Inkscape::SnapPreferences::PointType point_type, std::pair<Geom::Point, int> const &p) const { +void SnapManager::_displaySnapsource(Inkscape::SnapCandidatePoint const &p) const { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (prefs->getBool("/options/snapclosestonly/value")) { - bool p_is_a_node = point_type & Inkscape::SnapPreferences::SNAPPOINT_NODE; - bool p_is_a_bbox = point_type & Inkscape::SnapPreferences::SNAPPOINT_BBOX; + bool p_is_a_node = p.getSourceType() & Inkscape::SNAPSOURCE_NODE_CATEGORY; + bool p_is_a_bbox = p.getSourceType() & Inkscape::SNAPSOURCE_BBOX_CATEGORY; + if (snapprefs.getSnapEnabledGlobally() && ((p_is_a_node && snapprefs.getSnapModeNode()) || (p_is_a_bbox && snapprefs.getSnapModeBBox()))) { _desktop->snapindicator->set_new_snapsource(p); } else { diff --git a/src/snap.h b/src/snap.h index a3e463092..8a5688bea 100644 --- a/src/snap.h +++ b/src/snap.h @@ -1,17 +1,7 @@ -#ifndef SEEN_SNAP_H -#define SEEN_SNAP_H - /** * \file snap.h - * \brief SnapManager class. - * - * The SnapManager class handles most (if not all) of the interfacing of the snapping mechanisms with the - * other parts of the code base. It stores the references to the various types of snappers for grid, guides - * and objects, and it stores most of the snapping preferences. Besides that it provides methods to setup - * the snapping environment (e.g. keeps a list of the items to ignore when looking for snap target candidates, - * and toggling of the snap indicator), and it provides many different methods for the snapping itself (free - * snapping vs. constrained snapping, returning the result by reference or through a return statement, etc.) - * + * \brief Per-desktop object that handles snapping queries + *//* * Authors: * Lauris Kaplinski <lauris@kaplinski.com> * Frank Felfe <innerspace@iname.com> @@ -20,13 +10,15 @@ * * Copyright (C) 2006-2007 Johan Engelen <johan@shouraizou.nl> * Copyright (C) 2000-2002 Lauris Kaplinski - * Copyright (C) 2000-2009 Authors + * Copyright (C) 2000-2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <vector> +#ifndef SEEN_SNAP_H +#define SEEN_SNAP_H +#include <vector> #include "guide-snapper.h" #include "object-snapper.h" #include "snap-preferences.h" @@ -42,11 +34,33 @@ enum SPGuideDragType { // used both here and in desktop-events.cpp class SPNamedView; /// Class to coordinate snapping operations - /** - * Each SPNamedView has one of these. It offers methods to snap points to whatever - * snappers are defined (e.g. grid, guides etc.). It also allows callers to snap - * points which have undergone some transformation (e.g. translation, scaling etc.) + * The SnapManager class handles most (if not all) of the interfacing of the snapping mechanisms + * with the other parts of the code base. It stores the references to the various types of snappers + * for grid, guides and objects, and it stores most of the snapping preferences. Besides that + * it provides methods to setup the snapping environment (e.g. keeps a list of the items to ignore + * when looking for snap target candidates, and toggling of the snap indicator), and it provides + * many different methods for snapping queries (free snapping vs. constrained snapping, + * returning the result by reference or through a return statement, etc.) + * + * Each SPNamedView has one of these. It offers methods to snap points to whatever + * snappers are defined (e.g. grid, guides etc.). It also allows callers to snap + * points which have undergone some transformation (e.g. translation, scaling etc.) + * + * \par How snapping is implemented in Inkscape + * \par + * The snapping system consists of two key elements. The first one is the snap manager + * (this class), which keeps some data about objects in the document and answers queries + * of the type "given this point and type of transformation, what is the best place + * to snap to?". + * + * The second is in event-context.cpp and implements the snapping timeout. Whenever a motion + * events happens over the canvas, it stores it for later use and initiates a timeout. + * This timeout is discarded whenever a new motion event occurs. When the timeout expires, + * a global flag in SnapManager, accessed via getSnapPostponedGlobally(), is set to true + * and the stored event is replayed, but this time with snapping enabled. This way you can + * write snapping code directly in your control point's dragged handler as if there was + * no timeout. */ class SnapManager @@ -69,84 +83,73 @@ public: void setup(SPDesktop const *desktop, bool snapindicator = true, SPItem const *item_to_ignore = NULL, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL, SPGuide *guide_to_ignore = NULL); void setup(SPDesktop const *desktop, - bool snapindicator, - std::vector<SPItem const *> &items_to_ignore, - std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL, - SPGuide *guide_to_ignore = NULL); + bool snapindicator, + std::vector<SPItem const *> &items_to_ignore, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL, + SPGuide *guide_to_ignore = NULL); + void setupIgnoreSelection(SPDesktop const *desktop, + bool snapindicator = true, + std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL, + SPGuide *guide_to_ignore = NULL); // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a // point if snapping has occurred (by overwriting p); otherwise p is untouched - void freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, - Geom::Point &p, + void freeSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, - long source_num = 0, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; - - Inkscape::SnappedPoint freeSnap(Inkscape::SnapPreferences::PointType point_type, - Geom::Point const &p, - Inkscape::SnapSourceType const &source_type, - long source_num = 0, + Inkscape::SnappedPoint freeSnap(Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap = Geom::OptRect() ) const; - Geom::Point multipleOfGridPitch(Geom::Point const &t) const; + void preSnap(Inkscape::SnapCandidatePoint const &p); + + Geom::Point multipleOfGridPitch(Geom::Point const &t, Geom::Point const &origin); // constrainedSnapReturnByRef() is preferred over constrainedSnap(), because it only returns a // point, by overwriting p, if snapping has occurred; otherwise p is untouched - void constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, - Geom::Point &p, + void constrainedSnapReturnByRef(Geom::Point &p, Inkscape::SnapSourceType const source_type, Inkscape::Snapper::ConstraintLine const &constraint, - long source_num = 0, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; - Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapPreferences::PointType point_type, - Geom::Point const &p, - Inkscape::SnapSourceType const &source_type, + Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapCandidatePoint const &p, Inkscape::Snapper::ConstraintLine const &constraint, - long source_num = 0, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const; void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const; - Inkscape::SnappedPoint freeSnapTranslation(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, + Inkscape::SnappedPoint freeSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Point const &tr) const; - Inkscape::SnappedPoint constrainedSnapTranslation(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, + Inkscape::SnappedPoint constrainedSnapTranslation(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Inkscape::Snapper::ConstraintLine const &constraint, Geom::Point const &tr) const; - Inkscape::SnappedPoint freeSnapScale(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, + Inkscape::SnappedPoint freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Scale const &s, Geom::Point const &o) const; - Inkscape::SnappedPoint constrainedSnapScale(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, + Inkscape::SnappedPoint constrainedSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Scale const &s, Geom::Point const &o) const; - Inkscape::SnappedPoint constrainedSnapStretch(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, + Inkscape::SnappedPoint constrainedSnapStretch(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Geom::Coord const &s, Geom::Point const &o, Geom::Dim2 d, bool uniform) const; - Inkscape::SnappedPoint constrainedSnapSkew(Inkscape::SnapPreferences::PointType point_type, - std::vector<std::pair<Geom::Point, int> > const &p, + Inkscape::SnappedPoint constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p, Geom::Point const &pointer, Inkscape::Snapper::ConstraintLine const &constraint, Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor @@ -167,22 +170,22 @@ public: bool getSnapIndicator() const {return _snapindicator;} + Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, SnappedConstraints const &sc, bool constrained, bool noCurves = false, bool allowOffScreen = false) const; + protected: SPNamedView const *_named_view; private: - std::vector<SPItem const *> *_items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method - SPItem const *_item_to_ignore; ///< Single item that should not be snapped to. If not NULL then this takes precedence over _items_to_ignore. Set using the setup() method + std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged SPDesktop const *_desktop; bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to - std::vector<std::pair<Geom::Point, int> > *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored + std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored //TODO: Make _unselected_nodes type safe; in the line above int is used for Inkscape::SnapTargetType, but if I remember //correctly then in other cases the int is being used for Inkscape::SnapSourceType, or for both. How to make //this type safe? - Inkscape::SnappedPoint _snapTransformed(Inkscape::SnapPreferences::PointType type, - std::vector<std::pair<Geom::Point, int> > const &points, + Inkscape::SnappedPoint _snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points, Geom::Point const &pointer, bool constrained, Inkscape::Snapper::ConstraintLine const &constraint, @@ -192,16 +195,14 @@ private: Geom::Dim2 dim, bool uniform) const; - Geom::Point _transformPoint(std::pair<Geom::Point, int> const &p, + Geom::Point _transformPoint(Inkscape::SnapCandidatePoint const &p, Transformation const transformation_type, Geom::Point const &transformation, Geom::Point const &origin, Geom::Dim2 const dim, bool const uniform) const; - void _displaySnapsource(Inkscape::SnapPreferences::PointType point_type, std::pair<Geom::Point, int> const &p) const; - - Inkscape::SnappedPoint findBestSnap(Geom::Point const &p, Inkscape::SnapSourceType const source_type, SnappedConstraints &sc, bool constrained, bool noCurves = false) const; + void _displaySnapsource(Inkscape::SnapCandidatePoint const &p) const; }; #endif /* !SEEN_SNAP_H */ diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp index 334038638..d4ef0a83f 100644 --- a/src/snapped-curve.cpp +++ b/src/snapped-curve.cpp @@ -12,7 +12,7 @@ #include <2geom/crossing.h> #include <2geom/path-intersection.h> -Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target) +Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox) { _distance = snapped_distance; _tolerance = std::max(snapped_tolerance, 1.0); @@ -27,6 +27,7 @@ Inkscape::SnappedCurve::SnappedCurve(Geom::Point const &snapped_point, Geom::Coo _source = source; _source_num = source_num; _target = target; + _target_bbox = target_bbox; } Inkscape::SnappedCurve::SnappedCurve() @@ -44,6 +45,7 @@ Inkscape::SnappedCurve::SnappedCurve() _source = SNAPSOURCE_UNDEFINED; _source_num = 0; _target = SNAPTARGET_UNDEFINED; + _target_bbox = Geom::OptRect(); } Inkscape::SnappedCurve::~SnappedCurve() @@ -114,22 +116,27 @@ bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geo bool success = false; for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) { - std::list<Inkscape::SnappedCurve>::const_iterator j = i; - j++; - for (; j != list.end(); j++) { - Inkscape::SnappedPoint sp = (*i).intersect(*j, p, dt2doc); - if (sp.getAtIntersection()) { - // if it's the first point - bool const c1 = !success; - // or, if it's closer - bool const c2 = sp.getSnapDistance() < result.getSnapDistance(); - // or, if it's just as close then look at the other distance - // (only relevant for snapped points which are at an intersection) - bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); - // then prefer this point over the previous one - if (c1 || c2 || c3) { - result = sp; - success = true; + if ((*i).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes, + // as this would require two bboxes two be flashed in the snap indicator + std::list<Inkscape::SnappedCurve>::const_iterator j = i; + j++; + for (; j != list.end(); j++) { + if ((*j).getTarget() != Inkscape::SNAPTARGET_BBOX_EDGE) { // We don't support snapping to intersections of bboxes + Inkscape::SnappedPoint sp = (*i).intersect(*j, p, dt2doc); + if (sp.getAtIntersection()) { + // if it's the first point + bool const c1 = !success; + // or, if it's closer + bool const c2 = sp.getSnapDistance() < result.getSnapDistance(); + // or, if it's just as close then look at the other distance + // (only relevant for snapped points which are at an intersection) + bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); + // then prefer this point over the previous one + if (c1 || c2 || c3) { + result = sp; + success = true; + } + } } } } diff --git a/src/snapped-curve.h b/src/snapped-curve.h index 4eea6e734..21124c678 100644 --- a/src/snapped-curve.h +++ b/src/snapped-curve.h @@ -24,7 +24,7 @@ class SnappedCurve : public SnappedPoint { public: SnappedCurve(); - SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target); + SnappedCurve(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, bool const &fully_constrained, Geom::Curve const *curve, SnapSourceType source, long source_num, SnapTargetType target, Geom::OptRect target_bbox); ~SnappedCurve(); Inkscape::SnappedPoint intersect(SnappedCurve const &curve, Geom::Point const &p, Geom::Matrix dt2doc) const; //intersect with another SnappedCurve diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp index 102e761b9..089aa4323 100644 --- a/src/snapped-point.cpp +++ b/src/snapped-point.cpp @@ -14,8 +14,8 @@ #include "preferences.h" // overloaded constructor -Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained) - : _point(p), _source(source), _source_num(source_num), _target(target), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a) +Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained, Geom::OptRect target_bbox) + : _point(p), _source(source), _source_num(source_num), _target(target), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a), _target_bbox(target_bbox) { // tolerance should never be smaller than 1 px, as it is used for normalization in isOtherSnapBetter. We don't want a division by zero. _at_intersection = false; @@ -27,6 +27,23 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const _pointer_distance = NR_HUGE; } +Inkscape::SnappedPoint::SnappedPoint(Inkscape::SnapCandidatePoint const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained) + : _target(target), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a) +{ + _point = p.getPoint(); + _source = p.getSourceType(); + _source_num = p.getSourceNum(); + _at_intersection = false; + _fully_constrained = fully_constrained; + _second_distance = NR_HUGE; + _second_tolerance = 1; + _second_always_snap = false; + _transformation = Geom::Point(1,1); + _pointer_distance = NR_HUGE; + _target_bbox = p.getTargetBBox(); + +} + Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2) : _point(p), _source(source), _source_num(source_num), _target(target), _at_intersection(at_intersection), _fully_constrained(fully_constrained), _distance(d), _tolerance(std::max(t,1.0)), _always_snap(a), _second_distance(d2), _second_tolerance(std::max(t2,1.0)), _second_always_snap(a2) @@ -35,6 +52,7 @@ Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p, SnapSourceType const // isOtherSnapBetter. We don't want a division by zero. _transformation = Geom::Point(1,1); _pointer_distance = NR_HUGE; + _target_bbox = Geom::OptRect(); } Inkscape::SnappedPoint::SnappedPoint() @@ -53,6 +71,26 @@ Inkscape::SnappedPoint::SnappedPoint() _second_always_snap = false; _transformation = Geom::Point(1,1); _pointer_distance = NR_HUGE; + _target_bbox = Geom::OptRect(); +} + +Inkscape::SnappedPoint::SnappedPoint(Geom::Point const &p) +{ + _point = p; + _source = SNAPSOURCE_UNDEFINED, + _source_num = 0, + _target = SNAPTARGET_UNDEFINED, + _at_intersection = false; + _fully_constrained = false; + _distance = NR_HUGE; + _tolerance = 1; + _always_snap = false; + _second_distance = NR_HUGE; + _second_tolerance = 1; + _second_always_snap = false; + _transformation = Geom::Point(1,1); + _pointer_distance = NR_HUGE; + _target_bbox = Geom::OptRect(); } Inkscape::SnappedPoint::~SnappedPoint() @@ -65,11 +103,11 @@ void Inkscape::SnappedPoint::getPoint(Geom::Point &p) const if (getSnapped()) { // then return the snapped point by overwriting p p = _point; - } //otherwise p will be left untouched; this way the caller doesn't have to check wether we've snapped + } //otherwise p will be left untouched; this way the caller doesn't have to check whether we've snapped } // search for the closest snapped point -bool getClosestSP(std::list<Inkscape::SnappedPoint> &list, Inkscape::SnappedPoint &result) +bool getClosestSP(std::list<Inkscape::SnappedPoint> const &list, Inkscape::SnappedPoint &result) { bool success = false; @@ -86,6 +124,10 @@ bool getClosestSP(std::list<Inkscape::SnappedPoint> &list, Inkscape::SnappedPoin bool Inkscape::SnappedPoint::isOtherSnapBetter(Inkscape::SnappedPoint const &other_one, bool weighted) const { + if (!other_one.getSnapped()) { + return false; + } + double dist_other = other_one.getSnapDistance(); double dist_this = getSnapDistance(); diff --git a/src/snapped-point.h b/src/snapped-point.h index 70d353a73..10d36f57e 100644 --- a/src/snapped-point.h +++ b/src/snapped-point.h @@ -16,70 +16,21 @@ #include <list> #include <libnr/nr-values.h> //Because of NR_HUGE #include <2geom/geom.h> +#include <snap-candidate.h> namespace Inkscape { -enum SnapTargetType { - SNAPTARGET_UNDEFINED = 0, - SNAPTARGET_GRID, - SNAPTARGET_GRID_INTERSECTION, - SNAPTARGET_GUIDE, - SNAPTARGET_GUIDE_INTERSECTION, - SNAPTARGET_GUIDE_ORIGIN, - SNAPTARGET_GRID_GUIDE_INTERSECTION, - SNAPTARGET_NODE_SMOOTH, - SNAPTARGET_NODE_CUSP, - SNAPTARGET_LINE_MIDPOINT, - SNAPTARGET_OBJECT_MIDPOINT, - SNAPTARGET_ROTATION_CENTER, - SNAPTARGET_HANDLE, - SNAPTARGET_PATH, - SNAPTARGET_PATH_INTERSECTION, - SNAPTARGET_BBOX_CORNER, - SNAPTARGET_BBOX_EDGE, - SNAPTARGET_BBOX_EDGE_MIDPOINT, - SNAPTARGET_BBOX_MIDPOINT, - SNAPTARGET_GRADIENTS_PARENT_BBOX, - SNAPTARGET_PAGE_BORDER, - SNAPTARGET_PAGE_CORNER, - SNAPTARGET_CONVEX_HULL_CORNER, - SNAPTARGET_ELLIPSE_QUADRANT_POINT, - SNAPTARGET_CENTER, // of ellipse - SNAPTARGET_CORNER, // of image or of rectangle - SNAPTARGET_TEXT_BASELINE -}; - -enum SnapSourceType { - SNAPSOURCE_UNDEFINED = 0, - SNAPSOURCE_BBOX_CORNER, - SNAPSOURCE_BBOX_MIDPOINT, - SNAPSOURCE_BBOX_EDGE_MIDPOINT, - SNAPSOURCE_NODE_SMOOTH, - SNAPSOURCE_NODE_CUSP, - SNAPSOURCE_LINE_MIDPOINT, - SNAPSOURCE_OBJECT_MIDPOINT, - SNAPSOURCE_ROTATION_CENTER, - SNAPSOURCE_HANDLE, - SNAPSOURCE_PATH_INTERSECTION, - SNAPSOURCE_GUIDE, - SNAPSOURCE_GUIDE_ORIGIN, - SNAPSOURCE_CONVEX_HULL_CORNER, - SNAPSOURCE_ELLIPSE_QUADRANT_POINT, - SNAPSOURCE_CENTER, // of ellipse - SNAPSOURCE_CORNER, // of image or of rectangle - SNAPSOURCE_TEXT_BASELINE -}; - - /// Class describing the result of an attempt to snap. class SnappedPoint { public: SnappedPoint(); + SnappedPoint(Geom::Point const &p); SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &at_intersection, bool const &fully_constrained, Geom::Coord const &d2, Geom::Coord const &t2, bool const &a2); - SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained); + SnappedPoint(Geom::Point const &p, SnapSourceType const &source, long source_num, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained, Geom::OptRect target_bbox = Geom::OptRect()); + SnappedPoint(SnapCandidatePoint const &p, SnapTargetType const &target, Geom::Coord const &d, Geom::Coord const &t, bool const &a, bool const &fully_constrained); ~SnappedPoint(); Geom::Coord getSnapDistance() const {return _distance;} @@ -106,6 +57,7 @@ public: * determine itself which point is most appropriate */ Geom::Point getPoint() const {return _point;} + void setPoint(Geom::Point const &p) {_point = p;} bool getAtIntersection() const {return _at_intersection;} bool getFullyConstrained() const {return _fully_constrained;} @@ -114,6 +66,8 @@ public: void setTransformation(Geom::Point const t) {_transformation = t;} void setTarget(SnapTargetType const target) {_target = target;} SnapTargetType getTarget() const {return _target;} + void setTargetBBox(Geom::OptRect const target) {_target_bbox = target;} + Geom::OptRect const getTargetBBox() const {return _target_bbox;} void setSource(SnapSourceType const source) {_source = source;} SnapSourceType getSource() const {return _source;} long getSourceNum() const {return _source_num;} @@ -164,13 +118,15 @@ protected: bool _second_always_snap; /* The transformation (translation, scale, skew, or stretch) from the original point to the snapped point */ Geom::Point _transformation; + /* The bounding box we've snapped to (when applicable); will be used by the snapindicator */ + Geom::OptRect _target_bbox; /* Distance from the un-transformed point to the mouse pointer, measured at the point in time when dragging started */ Geom::Coord _pointer_distance; }; }// end of namespace Inkscape -bool getClosestSP(std::list<Inkscape::SnappedPoint> &list, Inkscape::SnappedPoint &result); +bool getClosestSP(std::list<Inkscape::SnappedPoint> const &list, Inkscape::SnappedPoint &result); #endif /* !SEEN_SNAPPEDPOINT_H */ diff --git a/src/snapper.cpp b/src/snapper.cpp index 751b663e3..fb7281c30 100644 --- a/src/snapper.cpp +++ b/src/snapper.cpp @@ -19,9 +19,9 @@ * \param d Snap tolerance. */ Inkscape::Snapper::Snapper(SnapManager *sm, Geom::Coord const /*t*/) : - _snapmanager(sm), - _snap_enabled(true), - _snap_visible_only(true) + _snapmanager(sm), + _snap_enabled(true), + _snap_visible_only(true) { g_assert(_snapmanager != NULL); } diff --git a/src/snapper.h b/src/snapper.h index dcc0fbb81..b5bb17de9 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -20,6 +20,7 @@ #include "snapped-line.h" #include "snapped-curve.h" #include "snap-preferences.h" +#include "snap-candidate.h" struct SnappedConstraints { std::list<Inkscape::SnappedPoint> points; @@ -34,7 +35,6 @@ struct SPItem; namespace Inkscape { - /// Parent for classes that can snap points to something class Snapper { @@ -58,19 +58,17 @@ public: bool getSnapVisibleOnly() const {return _snap_visible_only;} virtual void freeSnap(SnappedConstraints &/*sc*/, - SnapPreferences::PointType const &/*t*/, - Geom::Point const &/*p*/, - SnapSourceType const &/*source_type*/, - long /*source_num*/, + Inkscape::SnapCandidatePoint const &/*p*/, Geom::OptRect const &/*bbox_to_snap*/, std::vector<SPItem const *> const */*it*/, - std::vector<std::pair<Geom::Point, int> > */*unselected_nodes*/) const {}; + std::vector<SnapCandidatePoint> */*unselected_nodes*/) const {}; class ConstraintLine { public: ConstraintLine(Geom::Point const &d) : _has_point(false), _direction(d) {} ConstraintLine(Geom::Point const &p, Geom::Point const &d) : _has_point(true), _point(p), _direction(d) {} + ConstraintLine(Geom::Line const &l) : _has_point(true), _point(l.origin()), _direction(l.versor()) {} bool hasPoint() const { return _has_point; @@ -103,10 +101,7 @@ public: }; virtual void constrainedSnap(SnappedConstraints &/*sc*/, - SnapPreferences::PointType const &/*t*/, - Geom::Point const &/*p*/, - SnapSourceType const &/*source_type*/, - long /*source_num*/, + Inkscape::SnapCandidatePoint const &/*p*/, Geom::OptRect const &/*bbox_to_snap*/, ConstraintLine const &/*c*/, std::vector<SPItem const *> const */*it*/) const {}; diff --git a/src/sp-clippath.h b/src/sp-clippath.h index 199b29f3b..02395f3d2 100644 --- a/src/sp-clippath.h +++ b/src/sp-clippath.h @@ -22,9 +22,10 @@ class SPClipPathView; #include "display/nr-arena-forward.h" +#include "libnr/nr-forward.h" #include "sp-object-group.h" #include "uri-references.h" -#include <libnr/nr-forward.h> +#include "xml/node.h" struct SPClipPath : public SPObjectGroup { class Reference; @@ -48,8 +49,39 @@ public: return (SPClipPath *)URIReference::getObject(); } protected: + /** + * If the owner element of this reference (the element with <... clippath="...">) + * is a child of the clippath it refers to, return false. + * \return false if obj is not a clippath or if obj is a parent of this + * reference's owner element. True otherwise. + */ virtual bool _acceptObject(SPObject *obj) const { - return SP_IS_CLIPPATH(obj); + if (!SP_IS_CLIPPATH(obj)) { + return false; + } + SPObject * const owner = this->getOwner(); + if (obj->isAncestorOf(owner)) { + Inkscape::XML::Node * const owner_repr = owner->repr; + Inkscape::XML::Node * const obj_repr = obj->repr; + gchar const * owner_name = NULL; + gchar const * owner_clippath = NULL; + gchar const * obj_name = NULL; + gchar const * obj_id = NULL; + if (owner_repr != NULL) { + owner_name = owner_repr->name(); + owner_clippath = owner_repr->attribute("clippath"); + } + if (obj_repr != NULL) { + obj_name = obj_repr->name(); + obj_id = obj_repr->attribute("id"); + } + g_warning("Ignoring recursive clippath reference " + "<%s clippath=\"%s\"> in <%s id=\"%s\">", + owner_name, owner_clippath, + obj_name, obj_id); + return false; + } + return true; } }; diff --git a/src/sp-conn-end-pair.cpp b/src/sp-conn-end-pair.cpp index 98b2aec26..7f019979a 100644 --- a/src/sp-conn-end-pair.cpp +++ b/src/sp-conn-end-pair.cpp @@ -63,7 +63,7 @@ SPConnEndPair::release() this->_connEnd[handle_ix]->ref.detach(); } - // If the document is being destroyed then the router instance + // If the document is being destroyed then the router instance // and the ConnRefs will have been destroyed with it. const bool routerInstanceExists = (_path->document->router != NULL); @@ -81,7 +81,9 @@ sp_conn_end_pair_build(SPObject *object) { sp_object_read_attr(object, "inkscape:connector-type"); sp_object_read_attr(object, "inkscape:connection-start"); + sp_object_read_attr(object, "inkscape:connection-start-point"); sp_object_read_attr(object, "inkscape:connection-end"); + sp_object_read_attr(object, "inkscape:connection-end-point"); sp_object_read_attr(object, "inkscape:connector-curvature"); } @@ -99,31 +101,18 @@ avoid_conn_transformed(Geom::Matrix const */*mp*/, SPItem *moved_item) void SPConnEndPair::setAttr(unsigned const key, gchar const *const value) { - if (key == SP_ATTR_CONNECTOR_TYPE) { - if (value && (strcmp(value, "polyline") == 0 || strcmp(value, "orthogonal") == 0)) { - int newconnType = strcmp(value, "polyline") ? SP_CONNECTOR_ORTHOGONAL : SP_CONNECTOR_POLYLINE; - - if (!_connRef) - { - _connType = newconnType; - Avoid::Router *router = _path->document->router; - GQuark itemID = g_quark_from_string(SP_OBJECT(_path)->id); - _connRef = new Avoid::ConnRef(router, itemID); - switch (newconnType) - { - case SP_CONNECTOR_POLYLINE: - _connRef->setRoutingType(Avoid::ConnType_PolyLine); - break; - case SP_CONNECTOR_ORTHOGONAL: - _connRef->setRoutingType(Avoid::ConnType_Orthogonal); - } - _transformed_connection = _path->connectTransformed( - sigc::ptr_fun(&avoid_conn_transformed)); - } - else - if (newconnType != _connType) + switch (key) + { + case SP_ATTR_CONNECTOR_TYPE: + if (value && (strcmp(value, "polyline") == 0 || strcmp(value, "orthogonal") == 0)) { + int newconnType = strcmp(value, "polyline") ? SP_CONNECTOR_ORTHOGONAL : SP_CONNECTOR_POLYLINE; + + if (!_connRef) { _connType = newconnType; + Avoid::Router *router = _path->document->router; + GQuark itemID = g_quark_from_string(_path->getId()); + _connRef = new Avoid::ConnRef(router, itemID); switch (newconnType) { case SP_CONNECTOR_POLYLINE: @@ -132,51 +121,68 @@ SPConnEndPair::setAttr(unsigned const key, gchar const *const value) case SP_CONNECTOR_ORTHOGONAL: _connRef->setRoutingType(Avoid::ConnType_Orthogonal); } - sp_conn_reroute_path(_path); + _transformed_connection = _path->connectTransformed( + sigc::ptr_fun(&avoid_conn_transformed)); } - } - else { - _connType = SP_CONNECTOR_NOAVOID; + else + if (newconnType != _connType) + { + _connType = newconnType; + switch (newconnType) + { + case SP_CONNECTOR_POLYLINE: + _connRef->setRoutingType(Avoid::ConnType_PolyLine); + break; + case SP_CONNECTOR_ORTHOGONAL: + _connRef->setRoutingType(Avoid::ConnType_Orthogonal); + } + sp_conn_reroute_path(_path); + } + } + else { + _connType = SP_CONNECTOR_NOAVOID; - if (_connRef) { - _connRef->removeFromGraph(); - delete _connRef; - _connRef = NULL; - _transformed_connection.disconnect(); + if (_connRef) { + _connRef->removeFromGraph(); + delete _connRef; + _connRef = NULL; + _transformed_connection.disconnect(); + } } - } - return; - } - else if (key == SP_ATTR_CONNECTOR_CURVATURE) { - if (value) { - _connCurvature = g_strtod(value, NULL); - if (_connRef && _connRef->isInitialised()) { - // Redraw the connector, but only if it has been initialised. - sp_conn_reroute_path(_path); + break; + case SP_ATTR_CONNECTOR_CURVATURE: + if (value) { + _connCurvature = g_strtod(value, NULL); + if (_connRef && _connRef->isInitialised()) { + // Redraw the connector, but only if it has been initialised. + sp_conn_reroute_path(_path); + } } - } - return; + break; + case SP_ATTR_CONNECTION_START: + case SP_ATTR_CONNECTION_END: + this->_connEnd[(key == SP_ATTR_CONNECTION_START ? 0 : 1)]->setAttacherHref(value, _path); + break; + case SP_ATTR_CONNECTION_START_POINT: + case SP_ATTR_CONNECTION_END_POINT: + this->_connEnd[(key == SP_ATTR_CONNECTION_START_POINT ? 0 : 1)]->setAttacherEndpoint(value, _path); + break; } - unsigned const handle_ix = key - SP_ATTR_CONNECTION_START; - g_assert( handle_ix <= 1 ); - this->_connEnd[handle_ix]->setAttacherHref(value, _path); } void SPConnEndPair::writeRepr(Inkscape::XML::Node *const repr) const { + char const * const attr_strs[] = {"inkscape:connection-start", "inkscape:connection-start-point", + "inkscape:connection-end", "inkscape:connection-end-point"}; for (unsigned handle_ix = 0; handle_ix < 2; ++handle_ix) { if (this->_connEnd[handle_ix]->ref.getURI()) { - char const * const attr_strs[] = {"inkscape:connection-start", - "inkscape:connection-end"}; + repr->setAttribute(attr_strs[2*handle_ix], this->_connEnd[handle_ix]->ref.getURI()->toString()); std::ostringstream ostr; - ostr<<this->_connEnd[handle_ix]->ref.getURI()->toString()<<"_"<< - (this->_connEnd[handle_ix]->type == ConnPointDefault ? "d":"u") << - "_" << this->_connEnd[handle_ix]->id; - - - repr->setAttribute(attr_strs[handle_ix], ostr.str().c_str()); + ostr<<(this->_connEnd[handle_ix]->type == ConnPointDefault ? "d":"u") << + this->_connEnd[handle_ix]->id; + repr->setAttribute(attr_strs[2*handle_ix+1], ostr.str().c_str()); } } repr->setAttribute("inkscape:connector-curvature", Glib::Ascii::dtostr(_connCurvature).c_str()); @@ -287,25 +293,19 @@ SPConnEndPair::update(void) void SPConnEndPair::storeIds(void) { if (_connEnd[0]->href) { - gchar ** href_strarray = NULL; - href_strarray = g_strsplit(_connEnd[0]->href, "_", 0); // href begins with a '#' which we don't want. - const char *startId = href_strarray[0] + 1; + const char *startId = _connEnd[0]->href + 1; GQuark itemId = g_quark_from_string(startId); _connRef->setEndPointId(Avoid::VertID::src, itemId); - g_strfreev(href_strarray); } else { _connRef->setEndPointId(Avoid::VertID::src, 0); } if (_connEnd[1]->href) { - gchar ** href_strarray = NULL; - href_strarray = g_strsplit(_connEnd[1]->href, "_", 0); // href begins with a '#' which we don't want. - const char *endId = href_strarray[0] + 1; + const char *endId = _connEnd[1]->href + 1; GQuark itemId = g_quark_from_string(endId); _connRef->setEndPointId(Avoid::VertID::tar, itemId); - g_strfreev(href_strarray); } else { _connRef->setEndPointId(Avoid::VertID::tar, 0); @@ -339,7 +339,7 @@ void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, const gdouble curvat if (!straight) route = route.curvedPolyline(curvature); connRef->calcRouteDist(); - + curve->reset(); curve->moveto( Geom::Point(route.ps[0].x, route.ps[0].y) ); @@ -359,7 +359,7 @@ void recreateCurve(SPCurve *curve, Avoid::ConnRef *connRef, const gdouble curvat break; case 'C': g_assert( i+2<pn ); - curve->curveto( p, Geom::Point(route.ps[i+1].x, route.ps[i+1].y), + curve->curveto( p, Geom::Point(route.ps[i+1].x, route.ps[i+1].y), Geom::Point(route.ps[i+2].x, route.ps[i+2].y) ); i+=2; break; @@ -404,10 +404,10 @@ SPConnEndPair::reroutePathFromLibavoid(void) SPCurve *curve = _path->original_curve ?_path->original_curve : _path->curve; recreateCurve( curve, _connRef, _connCurvature ); - + Geom::Matrix doc2item = sp_item_i2doc_affine(SP_ITEM(_path)).inverse(); - curve->transform(doc2item); - + curve->transform(doc2item); + return true; } diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 224442eb8..3ad6954a2 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -21,6 +21,9 @@ static void change_endpts(SPCurve *const curve, double const endPos[2]); SPConnEnd::SPConnEnd(SPObject *const owner) : ref(owner), href(NULL), + // Default to center connection endpoint + type(ConnPointDefault), + id(4), _changed_connection(), _delete_connection(), _transformed_connection() @@ -228,9 +231,10 @@ sp_conn_end_deleted(SPObject *, SPObject *const owner, unsigned const handle_ix) // todo: The first argument is the deleted object, or just NULL if // called by sp_conn_end_detach. g_return_if_fail(handle_ix < 2); - char const *const attr_str[] = {"inkscape:connection-start", - "inkscape:connection-end"}; - SP_OBJECT_REPR(owner)->setAttribute(attr_str[handle_ix], NULL); + char const * const attr_strs[] = {"inkscape:connection-start", "inkscape:connection-start-point", + "inkscape:connection-end", "inkscape:connection-end-point"}; + SP_OBJECT_REPR(owner)->setAttribute(attr_strs[2*handle_ix], NULL); + SP_OBJECT_REPR(owner)->setAttribute(attr_strs[2*handle_ix+1], NULL); /* I believe this will trigger sp_conn_end_href_changed. */ } @@ -245,7 +249,9 @@ SPConnEnd::setAttacherHref(gchar const *value, SPPath* /*path*/) { if ( value && href && ( strcmp(value, href) == 0 ) ) { /* No change, do nothing. */ - } else { + } + else + { if (!value) { ref.detach(); @@ -254,138 +260,104 @@ SPConnEnd::setAttacherHref(gchar const *value, SPPath* /*path*/) } else { - - /* References to the connection points have the following format - #svguri_t_id, where #svguri is the id of the item the - connector is attached to, t is the type of the point, which - can be either "d" for default or "u" for user-defined, and - id is the local (inside the item) id of the connection point. - In the case of default points id represents the position on the - item (i.e. Top-Left, Centre-Centre, etc.). - */ - - gchar ** href_strarray = NULL; - if (href) - href_strarray = g_strsplit(href, "_", 0); - gchar ** value_strarray = g_strsplit(value, "_", 0); - - g_free(href); - href = NULL; - - bool changed = false; bool validRef = true; + href = g_strdup(value); + // Now do the attaching, which emits the changed signal. + try { + ref.attach(Inkscape::URI(value)); + } catch (Inkscape::BadURIException &e) { + /* TODO: Proper error handling as per + * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. (Also needed for + * sp-use.) */ + g_warning("%s", e.what()); + validRef = false; + } - if ( !href_strarray || g_strcmp0(href_strarray[0], value_strarray[0]) != 0 ) + if ( !validRef ) { - // The href has changed, so update it. - changed = true; - // Set the href field, because sp_conn_end_href_changed will need it. - href = g_strdup(value); - // Now do the attaching, which emits the changed signal. - try { - ref.attach(Inkscape::URI(value_strarray[0])); - } catch (Inkscape::BadURIException &e) { - /* TODO: Proper error handling as per - * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. (Also needed for - * sp-use.) */ - g_warning("%s", e.what()); - validRef = false; - } + ref.detach(); + g_free(href); + href = NULL; } - // Check to see if the connection point changed and update it. - // + } + } +} - if ( !value_strarray[1] ) - { - /* Treat the old references to connection points - as default points that connect to the centre - of the item. - */ - if ( type != ConnPointDefault ) +void +SPConnEnd::setAttacherEndpoint(gchar const *value, SPPath* /*path*/) +{ + + /* References to the connection points have the following format + <t><id>, where t is the type of the point, which + can be either "d" for default or "u" for user-defined, and + id is the local (inside the item) id of the connection point. + In the case of default points id represents the position on the + item (i.e. Top-Left, Center-Center, etc.). + */ + + bool changed = false; + ConnPointType newtype = type; + + if (!value) + { + // Default to center endpoint + type = ConnPointDefault; + id = 4; + } + else + { + switch (value[0]) + { + case 'd': + if ( newtype != ConnPointDefault ) { - type = ConnPointDefault; + newtype = ConnPointDefault; changed = true; } - if ( id != ConnPointPosCC ) + break; + case 'u': + if ( newtype != ConnPointUserDefined) { - id = ConnPointPosCC; + newtype = ConnPointUserDefined; changed = true; } - } - else + break; + default: + g_warning("Bad reference to a connection point."); + } + + int newid = (int) g_ascii_strtod( value+1, 0 ); + if ( id != newid ) + { + id = newid; + changed = true; + } + + // We have to verify that the reference to the + // connection point is a valid one. + + if ( changed ) + { + + // Get the item the connector is attached to + SPItem* item = ref.getObject(); + if ( item ) { - switch (value_strarray[1][0]) - { - case 'd': - if ( type != ConnPointDefault ) - { - type = ConnPointDefault; - changed = true; - } - break; - case 'u': - if ( type != ConnPointUserDefined) - { - type = ConnPointUserDefined; - changed = true; - } - break; - default: - g_warning("Bad reference to a connection point."); - validRef = false; - } - if ( value_strarray[2] ) + if (!item->avoidRef->isValidConnPointId( newtype, newid ) ) { - int newId = (int) g_ascii_strtod( value_strarray[2], 0 ); - if ( id != newId ) - { - id = newId; - changed = true; - } - + g_warning("Bad reference to a connection point."); } else { - // We have a malformed reference to a connection point, - // emit a warning, clear href and detach ref. - changed = true; - g_warning("Bad reference to a connection point.");\ - validRef = false; + type = newtype; + id = newid; } - } - - if ( changed ) - { - // We still have to verify that the reference to the - // connection point is a valid one. - - // Get the item the connector is attached to - SPItem* item = ref.getObject(); - if ( item && !item->avoidRef->isValidConnPointId( type, id ) ) - { - g_warning("Bad reference to a connection point."); - validRef = false; + /* // Update the connector + if (path->connEndPair.isAutoRoutingConn()) { + path->connEndPair.tellLibavoidNewEndpoints(); } -/* else - // Update the connector - if (path->connEndPair.isAutoRoutingConn()) { - path->connEndPair.tellLibavoidNewEndpoints(); - } -*/ - } - - if ( !validRef ) - { - ref.detach(); - g_free(href); - href = NULL; + */ } - else - if (!href) - href = g_strdup(value); - - g_strfreev(href_strarray); - g_strfreev(value_strarray); } } } diff --git a/src/sp-conn-end.h b/src/sp-conn-end.h index 5e9dbb9da..16a611ec4 100644 --- a/src/sp-conn-end.h +++ b/src/sp-conn-end.h @@ -35,6 +35,8 @@ public: sigc::connection _transformed_connection; void setAttacherHref(gchar const *, SPPath *); + void setAttacherEndpoint(gchar const *, SPPath *); + private: SPConnEnd(SPConnEnd const &); diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index 12ba0ed0e..88fc59f17 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -29,11 +29,10 @@ #include <glibmm/i18n.h> #include <2geom/transforms.h> #include <2geom/pathvector.h> - #include "document.h" #include "sp-ellipse.h" - #include "preferences.h" +#include "snap-candidate.h" /* Common parent class */ @@ -73,7 +72,7 @@ static void sp_genericellipse_init(SPGenericEllipse *ellipse); static void sp_genericellipse_update(SPObject *object, SPCtx *ctx, guint flags); -static void sp_genericellipse_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_genericellipse_set_shape(SPShape *shape); static void sp_genericellipse_update_patheffect (SPLPEItem *lpeitem, bool write); @@ -270,15 +269,15 @@ static void sp_genericellipse_set_shape(SPShape *shape) curve->unref(); } -static void sp_genericellipse_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_genericellipse_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { g_assert(item != NULL); g_assert(SP_IS_GENERICELLIPSE(item)); // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes - if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { - return; - } + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item); sp_genericellipse_normalize(ellipse); @@ -305,19 +304,19 @@ static void sp_genericellipse_snappoints(SPItem const *item, bool const target, // Snap to the 4 quadrant points of the ellipse, but only if the arc // spans far enough to include them if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this? - double angle = 0; - for (angle = 0; angle < SP_2PI; angle += M_PI_2) { - if (angle >= ellipse->start && angle <= ellipse->end) { - pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d; - p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT) : int(Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT))); - } - } + double angle = 0; + for (angle = 0; angle < SP_2PI; angle += M_PI_2) { + if (angle >= ellipse->start && angle <= ellipse->end) { + pt = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_ELLIPSE_QUADRANT_POINT, Inkscape::SNAPTARGET_ELLIPSE_QUADRANT_POINT)); + } + } } // Add the centre, if we have a closed slice or when explicitly asked for if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) { - pt = Geom::Point(cx, cy) * i2d; - p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_CENTER) : int(Inkscape::SNAPSOURCE_CENTER))); + pt = Geom::Point(cx, cy) * i2d; + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_CENTER, Inkscape::SNAPTARGET_CENTER)); } // And if we have a slice, also snap to the endpoints @@ -325,12 +324,12 @@ static void sp_genericellipse_snappoints(SPItem const *item, bool const target, // Add the start point, if it's not coincident with a quadrant point if (fmod(ellipse->start, M_PI_2) != 0.0 ) { pt = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d; - p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP))); + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } // Add the end point, if it's not coincident with a quadrant point if (fmod(ellipse->end, M_PI_2) != 0.0 ) { pt = Geom::Point(cx + cos(ellipse->end)*rx, cy + sin(ellipse->end)*ry) * i2d; - p.push_back(std::make_pair(pt, target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP))); + p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } } } diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index 53bcd425d..b01146d60 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -49,7 +49,7 @@ static void sp_flowtext_set(SPObject *object, unsigned key, gchar const *value); static void sp_flowtext_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); static void sp_flowtext_print(SPItem *item, SPPrintContext *ctx); static gchar *sp_flowtext_description(SPItem *item); -static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_flowtext_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static NRArenaItem *sp_flowtext_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags); static void sp_flowtext_hide(SPItem *item, unsigned key); @@ -384,7 +384,7 @@ static gchar *sp_flowtext_description(SPItem *item) } } -static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_flowtext_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) { // Choose a point on the baseline for snapping from or to, with the horizontal position // of this point depending on the text alignment (left vs. right) @@ -392,8 +392,7 @@ static void sp_flowtext_snappoints(SPItem const *item, bool const target, SnapPo if (layout != NULL && layout->outputExists()) { boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); if (pt) { - int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); - p.push_back(std::make_pair((*pt) * sp_item_i2d_affine(item), type)); + p.push_back(Inkscape::SnapCandidatePoint((*pt) * sp_item_i2d_affine(item), Inkscape::SNAPSOURCE_TEXT_BASELINE, Inkscape::SNAPTARGET_TEXT_BASELINE)); } } } diff --git a/src/sp-gradient-fns.h b/src/sp-gradient-fns.h index 25729f5d7..dafa1646f 100644 --- a/src/sp-gradient-fns.h +++ b/src/sp-gradient-fns.h @@ -13,18 +13,6 @@ class SPGradient; -namespace Inkscape { -namespace XML { -class Node; -} -} - -#define SP_TYPE_GRADIENT (sp_gradient_get_type()) -#define SP_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_GRADIENT, SPGradient)) -#define SP_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_GRADIENT, SPGradientClass)) -#define SP_IS_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_GRADIENT)) -#define SP_IS_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_GRADIENT)) - #define SP_GRADIENT_STATE_IS_SET(g) (SP_GRADIENT(g)->state != SP_GRADIENT_STATE_UNKNOWN) #define SP_GRADIENT_IS_VECTOR(g) (SP_GRADIENT(g)->state == SP_GRADIENT_STATE_VECTOR) #define SP_GRADIENT_IS_PRIVATE(g) (SP_GRADIENT(g)->state == SP_GRADIENT_STATE_PRIVATE) @@ -32,8 +20,6 @@ class Node; #define SP_GRADIENT_SPREAD(g) (SP_GRADIENT(g)->spread) #define SP_GRADIENT_UNITS(g) (SP_GRADIENT(g)->units) -GType sp_gradient_get_type(); - /** Forces vector to be built, if not present (i.e. changed) */ void sp_gradient_ensure_vector(SPGradient *gradient); @@ -43,7 +29,6 @@ void sp_gradient_ensure_colors(SPGradient *gradient); void sp_gradient_set_units(SPGradient *gr, SPGradientUnits units); void sp_gradient_set_spread(SPGradient *gr, SPGradientSpread spread); -SPGradient *sp_gradient_get_vector (SPGradient *gradient, bool force_private); SPGradientSpread sp_gradient_get_spread (SPGradient *gradient); /* Gradient repr methods */ diff --git a/src/sp-gradient-reference.cpp b/src/sp-gradient-reference.cpp index 618e1085a..39ef9915a 100644 --- a/src/sp-gradient-reference.cpp +++ b/src/sp-gradient-reference.cpp @@ -1,5 +1,5 @@ #include "sp-gradient-reference.h" -#include "sp-gradient-fns.h" +#include "sp-gradient.h" bool SPGradientReference::_acceptObject(SPObject *obj) const diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index 84a0a9870..3d4d69672 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -638,6 +638,28 @@ sp_gradient_modified(SPObject *object, guint flags) } } +SPStop* SPGradient::getFirstStop() +{ + SPStop* first = 0; + for (SPObject *ochild = sp_object_first_child(this); ochild && !first; ochild = SP_OBJECT_NEXT(ochild)) { + if (SP_IS_STOP(ochild)) { + first = SP_STOP(ochild); + } + } + return first; +} + +int SPGradient::getStopCount() const +{ + int count = 0; + + for (SPStop *stop = const_cast<SPGradient*>(this)->getFirstStop(); stop && stop->getNextStop(); stop = stop->getNextStop()) { + count++; + } + + return count; +} + /** * Write gradient attributes to repr. */ @@ -793,8 +815,7 @@ chase_hrefs(SPGradient *const src, bool (*match)(SPGradient const *)) /** * True if gradient has stops. */ -static bool -has_stops(SPGradient const *gr) +static bool has_stopsFN(SPGradient const *gr) { return SP_GRADIENT_HAS_STOPS(gr); } @@ -818,23 +839,14 @@ has_units_set(SPGradient const *gr) } -/** - * Returns private vector of given gradient (the gradient at the end of the href chain which has - * stops), optionally normalizing it. - * - * \pre SP_IS_GRADIENT(gradient). - * \pre There exists a gradient in the chain that has stops. - */ -SPGradient * -sp_gradient_get_vector(SPGradient *gradient, bool force_vector) +SPGradient *SPGradient::getVector(bool force_vector) { - g_return_val_if_fail(gradient != NULL, NULL); - g_return_val_if_fail(SP_IS_GRADIENT(gradient), NULL); + SPGradient * src = chase_hrefs(this, has_stopsFN); - SPGradient *const src = chase_hrefs(gradient, has_stops); - return ( force_vector - ? sp_gradient_ensure_vector_normalized(src) - : src ); + if (force_vector) { + src = sp_gradient_ensure_vector_normalized(src); + } + return src; } /** diff --git a/src/sp-gradient.h b/src/sp-gradient.h index abd44538e..e7488673d 100644 --- a/src/sp-gradient.h +++ b/src/sp-gradient.h @@ -1,5 +1,5 @@ -#ifndef __SP_GRADIENT_H__ -#define __SP_GRADIENT_H__ +#ifndef SEEN_SP_GRADIENT_H +#define SEEN_SP_GRADIENT_H /** \file * SVG <stop> <linearGradient> and <radialGradient> implementation @@ -7,7 +7,9 @@ * Authors: * Lauris Kaplinski <lauris@kaplinski.com> * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org> * + * Copyrigt (C) 2010 Jon A. Cruz * Copyright (C) 2007 Johan Engelen * Copyright (C) 1999-2002 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. @@ -26,16 +28,25 @@ struct SPGradientReference; + +#define SP_TYPE_GRADIENT (sp_gradient_get_type()) +#define SP_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_GRADIENT, SPGradient)) +#define SP_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_GRADIENT, SPGradientClass)) +#define SP_IS_GRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_GRADIENT)) +#define SP_IS_GRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_GRADIENT)) + +GType sp_gradient_get_type(); + typedef enum { - SP_GRADIENT_TYPE_UNKNOWN, - SP_GRADIENT_TYPE_LINEAR, - SP_GRADIENT_TYPE_RADIAL + SP_GRADIENT_TYPE_UNKNOWN, + SP_GRADIENT_TYPE_LINEAR, + SP_GRADIENT_TYPE_RADIAL } SPGradientType; typedef enum { - SP_GRADIENT_STATE_UNKNOWN, - SP_GRADIENT_STATE_VECTOR, - SP_GRADIENT_STATE_PRIVATE + SP_GRADIENT_STATE_UNKNOWN, + SP_GRADIENT_STATE_VECTOR, + SP_GRADIENT_STATE_PRIVATE } SPGradientState; typedef enum { @@ -49,7 +60,7 @@ typedef enum { POINT_RG_MID1, POINT_RG_MID2, // insert new point types here. - + POINT_G_INVALID } GrPointType; @@ -61,47 +72,60 @@ typedef enum { */ struct SPGradient : public SPPaintServer { - /** Reference (href) */ - SPGradientReference *ref; + /** Reference (href) */ + SPGradientReference *ref; + + /** State in Inkscape gradient system */ + guint state : 2; - /** State in Inkscape gradient system */ - guint state : 2; + /** gradientUnits attribute */ + SPGradientUnits units; + guint units_set : 1; - /** gradientUnits attribute */ - SPGradientUnits units; - guint units_set : 1; + /** gradientTransform attribute */ + Geom::Matrix gradientTransform; + guint gradientTransform_set : 1; - /** gradientTransform attribute */ - Geom::Matrix gradientTransform; - guint gradientTransform_set : 1; + /** spreadMethod attribute */ + SPGradientSpread spread; + guint spread_set : 1; - /** spreadMethod attribute */ - SPGradientSpread spread; - guint spread_set : 1; + /** Gradient stops */ + guint has_stops : 1; - /** Gradient stops */ - guint has_stops : 1; + /** Composed vector */ + SPGradientVector vector; - /** Composed vector */ - SPGradientVector vector; + /** Rendered color array (4 * 1024 bytes) */ + guchar *color; - /** Rendered color array (4 * 1024 bytes) */ - guchar *color; + sigc::connection modified_connection; - sigc::connection modified_connection; + + SPStop* getFirstStop(); + int getStopCount() const; + +/** + * Returns private vector of given gradient (the gradient at the end of the href chain which has + * stops), optionally normalizing it. + * + * \pre SP_IS_GRADIENT(gradient). + * \pre There exists a gradient in the chain that has stops. + */ + SPGradient *getVector(bool force_private = false); }; /** * The SPGradient vtable. */ struct SPGradientClass { - SPPaintServerClass parent_class; + SPPaintServerClass parent_class; }; #include "sp-gradient-fns.h" -#endif /* !__SP_GRADIENT_H__ */ +#endif // SEEN_SP_GRADIENT_H /* Local Variables: diff --git a/src/sp-image.cpp b/src/sp-image.cpp index e3f708142..bb867e969 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -44,7 +44,7 @@ #include <glibmm/i18n.h> #include "xml/quote.h" #include <xml/repr.h> - +#include "snap-candidate.h" #include "libnr/nr-matrix-fns.h" #include "io/sys.h" @@ -86,7 +86,7 @@ static Inkscape::XML::Node *sp_image_write (SPObject *object, Inkscape::XML::Doc static void sp_image_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); static void sp_image_print (SPItem * item, SPPrintContext *ctx); static gchar * sp_image_description (SPItem * item); -static void sp_image_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_image_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static NRArenaItem *sp_image_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags); static Geom::Matrix sp_image_set_transform (SPItem *item, Geom::Matrix const &xform); static void sp_image_set_curve(SPImage *image); @@ -262,6 +262,161 @@ void user_flush_data( png_structp /*png_ptr*/ ) //g_message( "user_flush_data" ); } + +static bool readPngAndHeaders( PushPull &youme, gint & dpiX, gint & dpiY ) +{ + bool good = true; + + gboolean isPng = !png_sig_cmp( youme.scratch + youme.offset, 0, youme.available() ); + //g_message( " png? %s", (isPng ? "Yes":"No") ); + if ( isPng ) { + png_structp pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING, + 0, //(png_voidp)user_error_ptr, + 0, //user_error_fn, + 0 //user_warning_fn + ); + png_infop infoPtr = pngPtr ? png_create_info_struct( pngPtr ) : 0; + + if ( pngPtr && infoPtr ) { + if ( setjmp(png_jmpbuf(pngPtr)) ) { + // libpng calls longjmp to return here if an error occurs. + good = false; + } + + if (good) { + png_set_read_fn( pngPtr, &youme, user_read_data ); + //g_message( "In" ); + + //png_read_info( pngPtr, infoPtr ); + png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, 0 ); + + //g_message("out"); + + /* + if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) ) + { + g_message("pHYs chunk now valid" ); + } + if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) ) + { + g_message("sCAL chunk now valid" ); + } + */ + + png_uint_32 res_x = 0; + png_uint_32 res_y = 0; + int unit_type = 0; + if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) ) { +// g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type, +// (unit_type == 1? "per meter" : "unknown") +// ); + +// g_message( " dpi: (%d, %d)", +// (int)(0.5 + ((double)res_x)/39.37), +// (int)(0.5 + ((double)res_y)/39.37) ); + if ( unit_type == PNG_RESOLUTION_METER ) + { + // TODO come up with a more accurate DPI setting + dpiX = (int)(0.5 + ((double)res_x)/39.37); + dpiY = (int)(0.5 + ((double)res_y)/39.37); + } + } else { +// g_message( "pHYs no" ); + } + +/* + double width = 0; + double height = 0; + int unit = 0; + if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) ) + { + gchar* vals[] = { + "unknown", // PNG_SCALE_UNKNOWN + "meter", // PNG_SCALE_METER + "radian", // PNG_SCALE_RADIAN + "last", // + NULL + }; + + g_message( "sCAL: (%f, %f) %d (%s)", + width, height, unit, + ((unit >= 0 && unit < 3) ? vals[unit]:"???") + ); + } +*/ + +#if defined(PNG_sRGB_SUPPORTED) + { + int intent = 0; + if ( png_get_sRGB(pngPtr, infoPtr, &intent) ) { +// g_message("Found an sRGB png chunk"); + } + } +#endif // defined(PNG_sRGB_SUPPORTED) + +#if defined(PNG_cHRM_SUPPORTED) + { + double white_x = 0; + double white_y = 0; + double red_x = 0; + double red_y = 0; + double green_x = 0; + double green_y = 0; + double blue_x = 0; + double blue_y = 0; + + if ( png_get_cHRM(pngPtr, infoPtr, + &white_x, &white_y, + &red_x, &red_y, + &green_x, &green_y, + &blue_x, &blue_y) ) { +// g_message("Found a cHRM png chunk"); + } + } +#endif // defined(PNG_cHRM_SUPPORTED) + +#if defined(PNG_gAMA_SUPPORTED) + { + double file_gamma = 0; + if ( png_get_gAMA(pngPtr, infoPtr, &file_gamma) ) { +// g_message("Found a gAMA png chunk"); + } + } +#endif // defined(PNG_gAMA_SUPPORTED) + +#if defined(PNG_iCCP_SUPPORTED) + { + char* name = 0; + int compression_type = 0; + char* profile = 0; + png_uint_32 proflen = 0; + if ( png_get_iCCP(pngPtr, infoPtr, &name, &compression_type, &profile, &proflen) ) { +// g_message("Found an iCCP chunk named [%s] with %d bytes and comp %d", name, proflen, compression_type); + } + } +#endif // defined(PNG_iCCP_SUPPORTED) + + } + } else { + g_message("Error when creating PNG read struct"); + } + + // now clean it up. + if (pngPtr && infoPtr) { + png_destroy_read_struct( &pngPtr, &infoPtr, 0 ); + pngPtr = 0; + infoPtr = 0; + } else if (pngPtr) { + png_destroy_read_struct( &pngPtr, 0, 0 ); + pngPtr = 0; + } + } else { + good = false; // Was not a png file + } + + return good; +} + static GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, gchar*& pixPath, GError **/*error*/ ) { GdkPixbuf* buf = NULL; @@ -297,10 +452,6 @@ static GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, g // short buffer guchar scratch[1024]; gboolean latter = FALSE; - gboolean isPng = FALSE; - png_structp pngPtr = NULL; - png_infop infoPtr = NULL; - //png_infop endPtr = NULL; youme.fp = fp; youme.scratch = scratch; @@ -311,155 +462,15 @@ static GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, g while ( !feof(fp) ) { - if ( youme.readMore() ) - { - if ( youme.first ) - { + if ( youme.readMore() ) { + if ( youme.first ) { //g_message( "First data chunk" ); youme.first = FALSE; - isPng = !png_sig_cmp( scratch + youme.offset, 0, youme.available() ); - //g_message( " png? %s", (isPng ? "Yes":"No") ); - if ( isPng ) + if (readPngAndHeaders(youme, dpiX, dpiY)) { - pngPtr = png_create_read_struct( PNG_LIBPNG_VER_STRING, - NULL,//(png_voidp)user_error_ptr, - NULL,//user_error_fn, - NULL//user_warning_fn - ); - if ( pngPtr ) - { - infoPtr = png_create_info_struct( pngPtr ); - //endPtr = png_create_info_struct( pngPtr ); - - png_set_read_fn( pngPtr, &youme, user_read_data ); - //g_message( "In" ); - - //png_read_info( pngPtr, infoPtr ); - png_read_png( pngPtr, infoPtr, PNG_TRANSFORM_IDENTITY, NULL ); - - //g_message("out"); - - //png_read_end(pngPtr, endPtr); - - /* - if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_pHYs ) ) - { - g_message("pHYs chunk now valid" ); - } - if ( png_get_valid( pngPtr, infoPtr, PNG_INFO_sCAL ) ) - { - g_message("sCAL chunk now valid" ); - } - */ - - png_uint_32 res_x = 0; - png_uint_32 res_y = 0; - int unit_type = 0; - if ( png_get_pHYs( pngPtr, infoPtr, &res_x, &res_y, &unit_type) ) - { -// g_message( "pHYs yes (%d, %d) %d (%s)", (int)res_x, (int)res_y, unit_type, -// (unit_type == 1? "per meter" : "unknown") -// ); - -// g_message( " dpi: (%d, %d)", -// (int)(0.5 + ((double)res_x)/39.37), -// (int)(0.5 + ((double)res_y)/39.37) ); - if ( unit_type == PNG_RESOLUTION_METER ) - { - // TODO come up with a more accurate DPI setting - dpiX = (int)(0.5 + ((double)res_x)/39.37); - dpiY = (int)(0.5 + ((double)res_y)/39.37); - } - } - else - { -// g_message( "pHYs no" ); - } - -/* - double width = 0; - double height = 0; - int unit = 0; - if ( png_get_sCAL(pngPtr, infoPtr, &unit, &width, &height) ) - { - gchar* vals[] = { - "unknown", // PNG_SCALE_UNKNOWN - "meter", // PNG_SCALE_METER - "radian", // PNG_SCALE_RADIAN - "last", // - NULL - }; - - g_message( "sCAL: (%f, %f) %d (%s)", - width, height, unit, - ((unit >= 0 && unit < 3) ? vals[unit]:"???") - ); - } -*/ - -#if defined(PNG_sRGB_SUPPORTED) - { - int intent = 0; - if ( png_get_sRGB(pngPtr, infoPtr, &intent) ) { -// g_message("Found an sRGB png chunk"); - } - } -#endif // defined(PNG_sRGB_SUPPORTED) - -#if defined(PNG_cHRM_SUPPORTED) - { - double white_x = 0; - double white_y = 0; - double red_x = 0; - double red_y = 0; - double green_x = 0; - double green_y = 0; - double blue_x = 0; - double blue_y = 0; - - if ( png_get_cHRM(pngPtr, infoPtr, - &white_x, &white_y, - &red_x, &red_y, - &green_x, &green_y, - &blue_x, &blue_y) ) { -// g_message("Found a cHRM png chunk"); - } - } -#endif // defined(PNG_cHRM_SUPPORTED) - -#if defined(PNG_gAMA_SUPPORTED) - { - double file_gamma = 0; - if ( png_get_gAMA(pngPtr, infoPtr, &file_gamma) ) { -// g_message("Found a gAMA png chunk"); - } - } -#endif // defined(PNG_gAMA_SUPPORTED) - -#if defined(PNG_iCCP_SUPPORTED) - { - char* name = 0; - int compression_type = 0; - char* profile = 0; - png_uint_32 proflen = 0; - if ( png_get_iCCP(pngPtr, infoPtr, &name, &compression_type, &profile, &proflen) ) { -// g_message("Found an iCCP chunk named [%s] with %d bytes and comp %d", name, proflen, compression_type); - } - } -#endif // defined(PNG_iCCP_SUPPORTED) - - - // now clean it up. - png_destroy_read_struct( &pngPtr, &infoPtr, NULL );//&endPtr ); - } - else - { -// g_message("Error when creating PNG read struct"); - } + // TODO set the dpi to be read elsewhere } - } - else if ( !latter ) - { + } else if ( !latter ) { latter = TRUE; //g_message(" READing latter"); } @@ -470,52 +481,40 @@ static GdkPixbuf* pixbuf_new_from_file( const char *filename, time_t &modTime, g } gboolean ok = gdk_pixbuf_loader_close(loader, &err); - if ( ok ) - { + if ( ok ) { buf = gdk_pixbuf_loader_get_pixbuf( loader ); - if ( buf ) - { + if ( buf ) { g_object_ref(buf); - if ( dpiX ) - { + if ( dpiX ) { gchar *tmp = g_strdup_printf( "%d", dpiX ); - if ( tmp ) - { -// g_message("Need to set DpiX: %s", tmp); + if ( tmp ) { + //g_message("Need to set DpiX: %s", tmp); //gdk_pixbuf_set_option( buf, "Inkscape::DpiX", tmp ); g_free( tmp ); } } - if ( dpiY ) - { + if ( dpiY ) { gchar *tmp = g_strdup_printf( "%d", dpiY ); - if ( tmp ) - { -// g_message("Need to set DpiY: %s", tmp); + if ( tmp ) { + //g_message("Need to set DpiY: %s", tmp); //gdk_pixbuf_set_option( buf, "Inkscape::DpiY", tmp ); g_free( tmp ); } } } - } - else - { + } else { // do something g_message("error loading pixbuf at close"); } g_object_unref(loader); - } - else - { + } else { g_message("error when creating pixbuf loader"); } fclose( fp ); - fp = NULL; - } - else - { + fp = 0; + } else { g_warning ("Unable to open linked file: %s", filename); } @@ -1332,7 +1331,7 @@ sp_image_update_canvas_image (SPImage *image) } } -static void sp_image_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_image_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) { /* An image doesn't have any nodes to snap, but still we want to be able snap one image to another. Therefore we will create some snappoints at the corner, similar to a rect. If @@ -1354,12 +1353,10 @@ static void sp_image_snappoints(SPItem const *item, bool const target, SnapPoint double const x1 = x0 + image.width.computed; double const y1 = y0 + image.height.computed; Geom::Matrix const i2d (sp_item_i2d_affine (item)); - Geom::Point pt; - int type = target ? int(Inkscape::SNAPTARGET_CORNER) : int(Inkscape::SNAPSOURCE_CORNER); - p.push_back(std::make_pair(Geom::Point(x0, y0) * i2d, type)); - p.push_back(std::make_pair(Geom::Point(x0, y1) * i2d, type)); - p.push_back(std::make_pair(Geom::Point(x1, y1) * i2d, type)); - p.push_back(std::make_pair(Geom::Point(x1, y0) * i2d, type)); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(x0, y0) * i2d, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(x0, y1) * i2d, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(x1, y1) * i2d, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(x1, y0) * i2d, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); } } @@ -1582,6 +1579,40 @@ sp_image_get_curve (SPImage *image) return result; } +void +sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring const &mime_in) +{ + Glib::ustring format, mime; + if (mime_in == "image/jpeg") { + mime = mime_in; + format = "jpeg"; + } else { + mime = "image/png"; + format = "png"; + } + + gchar *data; + gsize length; + gdk_pixbuf_save_to_buffer(pb, &data, &length, format.data(), NULL, NULL); + + // Save base64 encoded data in image node + // this formula taken from Glib docs + guint needed_size = length * 4 / 3 + length * 4 / (3 * 72) + 7; + needed_size += 5 + 8 + mime.size(); // 5 bytes for data:, 8 for ;base64, + + gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer; + buf_work += g_sprintf(buffer, "data:%s;base64,", mime.data()); + + gint state = 0, save = 0; + gsize written = 0; + written += g_base64_encode_step((guchar*) data, length, TRUE, buf_work, &state, &save); + written += g_base64_encode_close(TRUE, buf_work + written, &state, &save); + buf_work[written] = 0; // null terminate + + image_node->setAttribute("xlink:href", buffer); + g_free(buffer); +} + void sp_image_refresh_if_outdated( SPImage* image ) { if ( image->href && image->lastMod ) { diff --git a/src/sp-image.h b/src/sp-image.h index 7b00e0da3..172cd7118 100644 --- a/src/sp-image.h +++ b/src/sp-image.h @@ -26,6 +26,7 @@ class SPImageClass; /* SPImage */ #include <gdk-pixbuf/gdk-pixbuf.h> +#include <glibmm/ustring.h> #include "svg/svg-length.h" #include "sp-item.h" #include "display/display-forward.h" @@ -65,7 +66,7 @@ GType sp_image_get_type (void); /* Return duplicate of curve or NULL */ SPCurve *sp_image_get_curve (SPImage *image); - +void sp_embed_image(Inkscape::XML::Node *imgnode, GdkPixbuf *pb, Glib::ustring const &mime); void sp_image_refresh_if_outdated( SPImage* image ); #endif diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index 3845be232..588427752 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -69,7 +69,7 @@ static void sp_group_print (SPItem * item, SPPrintContext *ctx); static gchar * sp_group_description (SPItem * item); static NRArenaItem *sp_group_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags); static void sp_group_hide (SPItem * item, unsigned int key); -static void sp_group_snappoints (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_group_snappoints (SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_group_update_patheffect(SPLPEItem *lpeitem, bool write); static void sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup, bool write); @@ -258,6 +258,8 @@ sp_group_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XM const char *value; if ( group->_layer_mode == SPGroup::LAYER ) { value = "layer"; + } else if ( group->_layer_mode == SPGroup::MASK_HELPER ) { + value = "maskhelper"; } else if ( flags & SP_OBJECT_WRITE_ALL ) { value = "group"; } else { @@ -296,6 +298,8 @@ static void sp_group_set(SPObject *object, unsigned key, char const *value) { case SP_ATTR_INKSCAPE_GROUPMODE: if ( value && !strcmp(value, "layer") ) { group->setLayerMode(SPGroup::LAYER); + } else if ( value && !strcmp(value, "maskhelper") ) { + group->setLayerMode(SPGroup::MASK_HELPER); } else { group->setLayerMode(SPGroup::GROUP); } @@ -320,14 +324,14 @@ sp_group_hide (SPItem *item, unsigned int key) SP_GROUP(item)->group->hide(key); } -static void sp_group_snappoints (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_group_snappoints (SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { for (SPObject const *o = sp_object_first_child(SP_OBJECT(item)); o != NULL; o = SP_OBJECT_NEXT(o)) { if (SP_IS_ITEM(o)) { - sp_item_snappoints(SP_ITEM(o), target, p, snapprefs); + sp_item_snappoints(SP_ITEM(o), p, snapprefs); } } } @@ -532,7 +536,7 @@ void SPGroup::setLayerMode(LayerMode mode) { if ( _layer_mode != mode ) { if ( mode == LAYER ) { sp_document_add_resource(SP_OBJECT_DOCUMENT(this), "layer", this); - } else { + } else if ( _layer_mode == LAYER ) { sp_document_remove_resource(SP_OBJECT_DOCUMENT(this), "layer", this); } _layer_mode = mode; diff --git a/src/sp-item-group.h b/src/sp-item-group.h index 65a51055a..932241a42 100644 --- a/src/sp-item-group.h +++ b/src/sp-item-group.h @@ -27,7 +27,7 @@ class CGroup; struct SPGroup : public SPLPEItem { - enum LayerMode { GROUP, LAYER }; + enum LayerMode { GROUP, LAYER, MASK_HELPER }; LayerMode _layer_mode; std::map<unsigned int, LayerMode> _display_modes; diff --git a/src/sp-item-notify-moveto.cpp b/src/sp-item-notify-moveto.cpp index 0bc08a235..628d77956 100644 --- a/src/sp-item-notify-moveto.cpp +++ b/src/sp-item-notify-moveto.cpp @@ -24,11 +24,11 @@ void sp_item_notify_moveto(SPItem &item, SPGuide const &mv_g, int const snappoin double const dir_lensq(dot(dir, dir)); g_return_if_fail( dir_lensq != 0 ); - SnapPointsWithType snappoints; - sp_item_snappoints(&item, false, snappoints, NULL); + std::vector<Inkscape::SnapCandidatePoint> snappoints; + sp_item_snappoints(&item, snappoints, NULL); g_return_if_fail( snappoint_ix < int(snappoints.size()) ); - double const pos0 = dot(dir, snappoints[snappoint_ix].first); + double const pos0 = dot(dir, snappoints[snappoint_ix].getPoint()); /// \todo effic: skip if mv_g is already satisfied. /* Translate along dir to make dot(dir, snappoints(item)[snappoint_ix]) == position. */ diff --git a/src/sp-item-rm-unsatisfied-cns.cpp b/src/sp-item-rm-unsatisfied-cns.cpp index 246453241..792a9d3bf 100644 --- a/src/sp-item-rm-unsatisfied-cns.cpp +++ b/src/sp-item-rm-unsatisfied-cns.cpp @@ -14,14 +14,14 @@ void sp_item_rm_unsatisfied_cns(SPItem &item) if (item.constraints.empty()) { return; } - SnapPointsWithType snappoints; - sp_item_snappoints(&item, false, snappoints, NULL); + std::vector<Inkscape::SnapCandidatePoint> snappoints; + sp_item_snappoints(&item, snappoints, NULL); for (unsigned i = item.constraints.size(); i--;) { g_assert( i < item.constraints.size() ); SPGuideConstraint const &cn = item.constraints[i]; int const snappoint_ix = cn.snappoint_ix; g_assert( snappoint_ix < int(snappoints.size()) ); - if (!approx_equal( sp_guide_distance_from_pt(cn.g, snappoints[snappoint_ix].first), 0) ) { + if (!approx_equal( sp_guide_distance_from_pt(cn.g, snappoints[snappoint_ix].getPoint()), 0) ) { remove_last(cn.g->attached_items, SPGuideAttachment(&item, cn.snappoint_ix)); g_assert( i < item.constraints.size() ); vector<SPGuideConstraint>::iterator const ei(&item.constraints[i]); diff --git a/src/sp-item-update-cns.cpp b/src/sp-item-update-cns.cpp index bebd65021..51da1679d 100644 --- a/src/sp-item-update-cns.cpp +++ b/src/sp-item-update-cns.cpp @@ -9,8 +9,8 @@ using std::vector; void sp_item_update_cns(SPItem &item, SPDesktop const &desktop) { - SnapPointsWithType snappoints; - sp_item_snappoints(&item, false, snappoints, NULL); + std::vector<Inkscape::SnapCandidatePoint> snappoints; + sp_item_snappoints(&item, snappoints, NULL); /* TODO: Implement the ordering. */ vector<SPGuideConstraint> found_cns; satisfied_guide_cns(desktop, snappoints, found_cns); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 1a5ca6f77..c4411e47d 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -64,7 +64,7 @@ #include "libnr/nr-scale-translate-ops.h" #include "libnr/nr-translate-scale-ops.h" #include "libnr/nr-convert2geom.h" -#include "algorithms/find-last-if.h" +#include "util/find-last-if.h" #include "util/reverse-list.h" #include <2geom/rect.h> #include <2geom/matrix.h> @@ -90,7 +90,7 @@ static void sp_item_update(SPObject *object, SPCtx *ctx, guint flags); static Inkscape::XML::Node *sp_item_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); static gchar *sp_item_private_description(SPItem *item); -static void sp_item_private_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_item_private_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static SPItemView *sp_item_view_new_prepend(SPItemView *list, SPItem *item, unsigned flags, unsigned key, NRArenaItem *arenaitem); static SPItemView *sp_item_view_list_remove(SPItemView *list, SPItemView *view); @@ -949,7 +949,7 @@ Geom::OptRect sp_item_bbox_desktop(SPItem *item, SPItem::BBoxType type) return rect; } -static void sp_item_private_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_item_private_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) { /* This will only be called if the derived class doesn't override this. * see for example sp_genericellipse_snappoints in sp-ellipse.cpp @@ -962,16 +962,15 @@ static void sp_item_private_snappoints(SPItem const *item, bool const target, Sn Geom::Point p1, p2; p1 = bbox->min(); p2 = bbox->max(); - int type = target ? int(Inkscape::SNAPTARGET_BBOX_CORNER) : int(Inkscape::SNAPSOURCE_BBOX_CORNER); - p.push_back(std::make_pair(p1, type)); - p.push_back(std::make_pair(Geom::Point(p1[Geom::X], p2[Geom::Y]), type)); - p.push_back(std::make_pair(p2, type)); - p.push_back(std::make_pair(Geom::Point(p2[Geom::X], p1[Geom::Y]), type)); + p.push_back(Inkscape::SnapCandidatePoint(p1, Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(p1[Geom::X], p2[Geom::Y]), Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(p2, Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(p2[Geom::X], p1[Geom::Y]), Inkscape::SNAPSOURCE_BBOX_CORNER, Inkscape::SNAPTARGET_BBOX_CORNER)); } } -void sp_item_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +void sp_item_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { g_assert (item != NULL); g_assert (SP_IS_ITEM(item)); @@ -979,12 +978,12 @@ void sp_item_snappoints(SPItem const *item, bool const target, SnapPointsWithTyp // Get the snappoints of the item SPItemClass const &item_class = *(SPItemClass const *) G_OBJECT_GET_CLASS(item); if (item_class.snappoints) { - item_class.snappoints(item, target, p, snapprefs); + item_class.snappoints(item, p, snapprefs); } // Get the snappoints at the item's center if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) { - p.push_back(std::make_pair(item->getCenter(), target ? int(Inkscape::SNAPTARGET_ROTATION_CENTER) : int(Inkscape::SNAPSOURCE_ROTATION_CENTER))); + p.push_back(Inkscape::SnapCandidatePoint(item->getCenter(), Inkscape::SNAPSOURCE_ROTATION_CENTER, Inkscape::SNAPTARGET_ROTATION_CENTER)); } // Get the snappoints of clipping paths and mask, if any @@ -999,15 +998,15 @@ void sp_item_snappoints(SPItem const *item, bool const target, SnapPointsWithTyp // obj is a group object, the children are the actual clippers for (SPObject *child = (*o)->children ; child ; child = child->next) { if (SP_IS_ITEM(child)) { - SnapPointsWithType p_clip_or_mask; + std::vector<Inkscape::SnapCandidatePoint> p_clip_or_mask; // Please note the recursive call here! - sp_item_snappoints(SP_ITEM(child), target, p_clip_or_mask, snapprefs); + sp_item_snappoints(SP_ITEM(child), p_clip_or_mask, snapprefs); // Take into account the transformation of the item being clipped or masked - for (SnapPointsWithType::const_iterator p_orig = p_clip_or_mask.begin(); p_orig != p_clip_or_mask.end(); p_orig++) { + for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator p_orig = p_clip_or_mask.begin(); p_orig != p_clip_or_mask.end(); p_orig++) { // All snappoints are in desktop coordinates, but the item's transformation is // in document coordinates. Hence the awkward construction below - Geom::Point pt = desktop->dt2doc((*p_orig).first) * sp_item_i2d_affine(item); - p.push_back(std::make_pair(pt, (*p_orig).second)); + Geom::Point pt = desktop->dt2doc((*p_orig).getPoint()) * sp_item_i2d_affine(item); + p.push_back(Inkscape::SnapCandidatePoint(pt, (*p_orig).getSourceType(), (*p_orig).getTargetType())); } } } diff --git a/src/sp-item.h b/src/sp-item.h index 639a1b4a2..faf64846e 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -26,14 +26,14 @@ #include <2geom/forward.h> #include <libnr/nr-convert2geom.h> #include <snap-preferences.h> -#include <snapped-point.h> +#include "snap-candidate.h" class SPGuideConstraint; struct SPClipPathReference; struct SPMaskReference; struct SPAvoidRef; struct SPPrintContext; -namespace Inkscape { class URIReference; } +namespace Inkscape { class URIReference;} enum { SP_EVENT_INVALID, @@ -171,8 +171,6 @@ private: mutable EvaluatedStatus _evaluated_status; }; -typedef std::vector<std::pair<Geom::Point, int> > SnapPointsWithType; // int is either of these enums: Inkscape::SnapTargetType or Inkscape::SnapSourceType - /// The SPItem vtable. struct SPItemClass { SPObjectClass parent_class; @@ -193,7 +191,7 @@ struct SPItemClass { /** Write to an iterator the points that should be considered for snapping * as the item's `nodes'. */ - void (* snappoints) (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); + void (* snappoints) (SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); /** Apply the transform optimally, and return any residual transformation */ Geom::Matrix (* set_transform)(SPItem *item, Geom::Matrix const &transform); @@ -226,7 +224,7 @@ unsigned int sp_item_display_key_new(unsigned int numkeys); NRArenaItem *sp_item_invoke_show(SPItem *item, NRArena *arena, unsigned int key, unsigned int flags); void sp_item_invoke_hide(SPItem *item, unsigned int key); -void sp_item_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +void sp_item_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); void sp_item_adjust_pattern(SPItem *item, /* Geom::Matrix const &premul, */ Geom::Matrix const &postmul, bool set = false); void sp_item_adjust_gradient(SPItem *item, /* Geom::Matrix const &premul, */ Geom::Matrix const &postmul, bool set = false); diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 6b71541e6..1bb500dd2 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -33,7 +33,6 @@ #include "message-stack.h" #include "inkscape.h" #include "desktop.h" -#include "node-context.h" #include "shape-editor.h" #include <algorithm> @@ -261,18 +260,7 @@ sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags) } // update the helperpaths of all LPEs applied to the item - // TODO: is there a more canonical place for this, since we don't have instant access to the item's nodepath? - // FIXME: this is called multiple (at least 3) times; how can we avoid this? - - // FIXME: ditch inkscape_active_event_context() - SPEventContext *ec = inkscape_active_event_context(); - if (!SP_IS_NODE_CONTEXT(ec)) return; - ShapeEditor *sh = ec->shape_editor; - g_assert(sh); - if (!sh->has_nodepath()) return; - - Inkscape::NodePath::Path *np = sh->get_nodepath(); - sp_nodepath_update_helperpaths(np); + // TODO: re-add for the new node tool } /** @@ -395,7 +383,8 @@ sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write) if (dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)) { if (!lpe->isVisible()) { // we manually disable text for LPEPathLength - dynamic_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText(); + // use static_cast, because we already checked for the right type above + static_cast<Inkscape::LivePathEffect::LPEPathLength *>(lpe)->hideCanvasText(); } } } diff --git a/src/sp-mask.h b/src/sp-mask.h index d5bddd332..0b995f0ce 100644 --- a/src/sp-mask.h +++ b/src/sp-mask.h @@ -26,6 +26,7 @@ class SPMaskView; #include "libnr/nr-forward.h" #include "sp-object-group.h" #include "uri-references.h" +#include "xml/node.h" struct SPMask : public SPObjectGroup { unsigned int maskUnits_set : 1; @@ -50,8 +51,39 @@ public: return (SPMask *)URIReference::getObject(); } protected: + /** + * If the owner element of this reference (the element with <... mask="...">) + * is a child of the mask it refers to, return false. + * \return false if obj is not a mask or if obj is a parent of this + * reference's owner element. True otherwise. + */ virtual bool _acceptObject(SPObject *obj) const { - return SP_IS_MASK(obj); + if (!SP_IS_MASK(obj)) { + return false; + } + SPObject * const owner = this->getOwner(); + if (obj->isAncestorOf(owner)) { + Inkscape::XML::Node * const owner_repr = owner->repr; + Inkscape::XML::Node * const obj_repr = obj->repr; + gchar const * owner_name = NULL; + gchar const * owner_mask = NULL; + gchar const * obj_name = NULL; + gchar const * obj_id = NULL; + if (owner_repr != NULL) { + owner_name = owner_repr->name(); + owner_mask = owner_repr->attribute("mask"); + } + if (obj_repr != NULL) { + obj_name = obj_repr->name(); + obj_id = obj_repr->attribute("id"); + } + g_warning("Ignoring recursive mask reference " + "<%s mask=\"%s\"> in <%s id=\"%s\">", + owner_name, owner_mask, + obj_name, obj_id); + return false; + } + return true; } }; diff --git a/src/sp-metadata.cpp b/src/sp-metadata.cpp index 920b7d64d..426810c7d 100644 --- a/src/sp-metadata.cpp +++ b/src/sp-metadata.cpp @@ -188,8 +188,7 @@ sp_metadata_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML: debug("0x%08x",(unsigned int)object); //SPMetadata *metadata = SP_METADATA(object); - // only create a repr when we're writing out an Inkscape SVG - if ( flags & SP_OBJECT_WRITE_EXT && repr != SP_OBJECT_REPR(object) ) { + if ( repr != SP_OBJECT_REPR(object) ) { if (repr) { repr->mergeFrom(SP_OBJECT_REPR (object), "id"); } else { @@ -197,8 +196,9 @@ sp_metadata_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML: } } - if (((SPObjectClass *) metadata_parent_class)->write) + if (((SPObjectClass *) metadata_parent_class)->write) { ((SPObjectClass *) metadata_parent_class)->write(object, doc, repr, flags); + } return repr; } diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 9b72a4157..44c3bf620 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -829,7 +829,7 @@ void sp_namedview_document_from_window(SPDesktop *desktop) sp_repr_set_int(view, "inkscape:window-maximized", desktop->is_maximized()); } - view->setAttribute("inkscape:current-layer", SP_OBJECT_ID(desktop->currentLayer())); + view->setAttribute("inkscape:current-layer", desktop->currentLayer()->getId()); // restore undoability sp_document_set_undo_sensitive(sp_desktop_document(desktop), saved); @@ -978,7 +978,7 @@ SPNamedView *sp_document_namedview(SPDocument *document, const gchar *id) return (SPNamedView *) nv; } - while (nv && strcmp(nv->id, id)) { + while (nv && strcmp(nv->getId(), id)) { nv = sp_item_group_get_child_by_name((SPGroup *) document->root, nv, "sodipodi:namedview"); } diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp index 4c3d5196e..62143e3ab 100644 --- a/src/sp-object-repr.cpp +++ b/src/sp-object-repr.cpp @@ -26,7 +26,7 @@ #include "persp3d.h" #include "sp-ellipse.h" #include "sp-star.h" -#include "sp-stop-fns.h" +#include "sp-stop.h" #include "sp-spiral.h" #include "sp-offset.h" #include "sp-line.h" diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 85e8a4e9a..420c7b4a6 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -53,8 +53,8 @@ #include "debug/demangle.h" #include "util/share.h" #include "util/format.h" +#include "util/longest-common-suffix.h" -#include "algorithms/longest-common-suffix.h" using std::memcpy; using std::strchr; using std::strcmp; @@ -112,6 +112,37 @@ Inkscape::XML::NodeEventVector object_event_vector = { sp_object_repr_order_changed }; +// A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API +class SPObjectImpl +{ +public: + +/** + * Null's the id member of an SPObject without attempting to free prior contents. + */ + static void setIdNull( SPObject* obj ) { + if (obj) { + obj->id = 0; + } + } + +/** + * Sets the id member of an object, freeing any prior content. + */ + static void setId( SPObject* obj, gchar const* id ) { + if (obj && (id != obj->id) ) { + if (obj->id) { + g_free(obj->id); + obj->id = 0; + } + if (id) { + obj->id = g_strdup(id); + } + } + } +}; + + static GObjectClass *parent_class; /** @@ -177,7 +208,7 @@ sp_object_init(SPObject *object) object->children = object->_last_child = NULL; object->parent = object->next = NULL; object->repr = NULL; - object->id = NULL; + SPObjectImpl::setIdNull(object); object->_collection_policy = SPObject::COLLECT_WITH_PARENT; @@ -188,8 +219,8 @@ sp_object_init(SPObject *object) object->_successor = NULL; // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute: - // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline, - // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient, + // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline, + // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient, // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject object->style = sp_style_new_from_object(object); @@ -259,6 +290,10 @@ public: } +gchar const* SPObject::getId() const { + return id; +} + /** * Increase reference count of object, with possible debugging. * @@ -808,8 +843,7 @@ sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *rep } } -void -sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned) +void sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned) { debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object)); @@ -820,7 +854,7 @@ sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::No g_assert(object->document == NULL); g_assert(object->repr == NULL); - g_assert(object->id == NULL); + g_assert(object->getId() == NULL); /* Bookkeeping */ @@ -837,27 +871,30 @@ sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::No /* If we are not cloned, and not seeking, force unique id */ gchar const *id = object->repr->attribute("id"); if (!document->isSeeking()) { - gchar *realid = sp_object_get_unique_id(object, id); - g_assert(realid != NULL); + { + gchar *realid = sp_object_get_unique_id(object, id); + g_assert(realid != NULL); - object->document->bindObjectToId(realid, object); - object->id = realid; + object->document->bindObjectToId(realid, object); + SPObjectImpl::setId(object, realid); + g_free(realid); + } /* Redefine ID, if required */ - if ((id == NULL) || (strcmp(id, realid) != 0)) { - object->repr->setAttribute("id", realid); + if ((id == NULL) || (strcmp(id, object->getId()) != 0)) { + object->repr->setAttribute("id", object->getId()); } } else if (id) { // bind if id, but no conflict -- otherwise, we can expect // a subsequent setting of the id attribute if (!object->document->getObjectById(id)) { object->document->bindObjectToId(id, object); - object->id = g_strdup(id); + SPObjectImpl::setId(object, id); } } } } else { - g_assert(object->id == NULL); + g_assert(object->getId() == NULL); } /* Invoke derived methods, if any */ @@ -909,6 +946,17 @@ void SPObject::releaseReferences() { this->repr = NULL; } + +SPObject *SPObject::getNext() +{ + return next; +} + +SPObject *SPObject::getPrev() +{ + return sp_object_prev(this); +} + /** * Callback for child_added node event. */ @@ -982,16 +1030,14 @@ sp_object_private_set(SPObject *object, unsigned int key, gchar const *value) } } - if (object->id) { - document->bindObjectToId(object->id, NULL); - g_free(object->id); + if (object->getId()) { + document->bindObjectToId(object->getId(), NULL); + SPObjectImpl::setId(object, 0); } if (new_id) { - object->id = g_strdup((char const*)new_id); - document->bindObjectToId(object->id, object); - } else { - object->id = NULL; + SPObjectImpl::setId(object, new_id); + document->bindObjectToId(object->getId(), object); } g_free(object->_default_label); @@ -1130,7 +1176,7 @@ sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape repr->setAttribute("inkscape:collect", NULL); } } else { - repr->setAttribute("id", object->id); + repr->setAttribute("id", object->getId()); if (object->xml_space.set) { char const *xml_space; @@ -1145,7 +1191,7 @@ sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape } else { repr->setAttribute("inkscape:collect", NULL); } - + SPStyle const *const obj_style = SP_OBJECT_STYLE(object); if (obj_style) { gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET); @@ -1201,7 +1247,7 @@ SPObject::updateRepr(unsigned int flags) { } } -/** Used both to create reprs in the original document, and to create +/** Used both to create reprs in the original document, and to create * reprs in another document (e.g. a temporary document used when * saving as "Plain SVG" */ @@ -1311,7 +1357,7 @@ SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) } catch(...) { - /** \todo + /** \todo * in case of catching an exception we need to inform the user somehow that the document is corrupted * maybe by implementing an document flag documentOk * or by a modal error dialog @@ -1323,8 +1369,8 @@ SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) } /** - * Request modified always bubbles *up* the tree, as opposed to - * request display update, which trickles down and relies on the + * Request modified always bubbles *up* the tree, as opposed to + * request display update, which trickles down and relies on the * flags set during this pass... */ void @@ -1355,9 +1401,9 @@ SPObject::requestModified(unsigned int flags) } } -/** +/** * Emits the MODIFIED signal with the object's flags. - * The object's mflags are the original set aside during the update pass for + * The object's mflags are the original set aside during the update pass for * later delivery here. Once emitModified() is called, those flags don't * need to be stored any longer. */ @@ -1748,7 +1794,7 @@ SPObject::textualContent() const for (const SPObject *child = firstChild(); child; child = child->next) { Inkscape::XML::NodeType child_type = child->repr->type(); - + if (child_type == Inkscape::XML::ELEMENT_NODE) { GString * new_text = child->textualContent(); g_string_append(text, new_text->str); diff --git a/src/sp-object.h b/src/sp-object.h index bbb8ecbd0..f7ca0f3a3 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -155,7 +155,15 @@ struct SPObject : public GObject { SPObject *_last_child; /* Remembered last child */ SPObject *next; /* Next object in linked list */ Inkscape::XML::Node *repr; /* Our xml representation */ + +private: gchar *id; /* Our very own unique id */ +public: + + /** + * Returns the objects current ID string. + */ + gchar const* getId() const; /** @brief cleans up an SPObject, releasing its references and * requesting that references to it be released @@ -213,6 +221,9 @@ struct SPObject : public GObject { /* A non-const version can be similarly constructed if you want one. * (Don't just cast away the constness, which would be ill-formed.) */ + SPObject *getNext(); + SPObject *getPrev(); + bool hasChildren() const { return ( children != NULL ); } SPObject *firstChild() { return children; } @@ -495,6 +506,8 @@ private: gchar * getTitleOrDesc(gchar const *svg_tagname) const; SPObject * findFirstChild(gchar const *tagname) const; GString * textualContent() const; + + friend class SPObjectImpl; }; /// The SPObject vtable. diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp index ae0f7bf19..556778676 100644 --- a/src/sp-offset.cpp +++ b/src/sp-offset.cpp @@ -83,7 +83,7 @@ static void sp_offset_update (SPObject * object, SPCtx * ctx, guint flags); static void sp_offset_release (SPObject * object); static gchar *sp_offset_description (SPItem * item); -static void sp_offset_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_offset_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_offset_set_shape (SPShape * shape); static void refresh_offset_source(SPOffset* offset); @@ -718,10 +718,10 @@ sp_offset_set_shape(SPShape *shape) /** * Virtual snappoints function. */ -static void sp_offset_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_offset_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { if (((SPItemClass *) parent_class)->snappoints) { - ((SPItemClass *) parent_class)->snappoints (item, target, p, snapprefs); + ((SPItemClass *) parent_class)->snappoints (item, p, snapprefs); } } diff --git a/src/sp-paint-server.cpp b/src/sp-paint-server.cpp index b85b0f279..258323a93 100644 --- a/src/sp-paint-server.cpp +++ b/src/sp-paint-server.cpp @@ -1,20 +1,25 @@ -#define __SP_PAINT_SERVER_C__ - /* * Base class for gradients and patterns * * Author: * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 1999-2002 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <libnr/nr-pixblock-pattern.h> +#include <string.h> +#include "libnr/nr-pixblock-pattern.h" #include "sp-paint-server.h" +#include "sp-gradient.h" +#include "xml/node.h" + static void sp_paint_server_class_init(SPPaintServerClass *psc); static void sp_paint_server_init(SPPaintServer *ps); @@ -31,15 +36,15 @@ GType sp_paint_server_get_type (void) if (!type) { GTypeInfo info = { sizeof(SPPaintServerClass), - NULL, /* base_init */ - NULL, /* base_finalize */ + NULL, /* base_init */ + NULL, /* base_finalize */ (GClassInitFunc) sp_paint_server_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ + NULL, /* class_finalize */ + NULL, /* class_data */ sizeof(SPPaintServer), - 16, /* n_preallocs */ + 16, /* n_preallocs */ (GInstanceInitFunc) sp_paint_server_init, - NULL, /* value_table */ + NULL, /* value_table */ }; type = g_type_register_static(SP_TYPE_OBJECT, "SPPaintServer", &info, (GTypeFlags) 0); } @@ -150,6 +155,38 @@ static void sp_painter_stale_fill(SPPainter */*painter*/, NRPixBlock *pb) nr_pixblock_render_gray_noise(pb, NULL); } +bool SPPaintServer::isSwatch() const +{ + bool swatch = false; + if (SP_IS_GRADIENT(this)) { + SPGradient *grad = SP_GRADIENT(this); + if ( SP_GRADIENT_HAS_STOPS(grad) ) { + gchar const * attr = repr->attribute("osb:paint"); + if (attr && !strcmp(attr, "solid")) { + swatch = true; + } + } + } + return swatch; +} + +bool SPPaintServer::isSolid() const +{ + bool solid = false; + if (SP_IS_GRADIENT(this)) { + SPGradient *grad = SP_GRADIENT(this); + if ( SP_GRADIENT_HAS_STOPS(grad) && (grad->getStopCount() == 0) ) { + gchar const * attr = repr->attribute("osb:paint"); + if (attr && !strcmp(attr, "solid")) { + solid = true; + } + } + } + return solid; +} + + + /* Local Variables: diff --git a/src/sp-paint-server.h b/src/sp-paint-server.h index 998f1556b..a76daf4d1 100644 --- a/src/sp-paint-server.h +++ b/src/sp-paint-server.h @@ -1,14 +1,16 @@ -#ifndef __SP_PAINT_SERVER_H__ -#define __SP_PAINT_SERVER_H__ +#ifndef SEEN_SP_PAINT_SERVER_H +#define SEEN_SP_PAINT_SERVER_H /* * Base class for gradients and patterns * * Author: * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 1999-2002 Lauris Kaplinski * Copyright (C) 2000-2001 Ximian, Inc. + * Copyright (C) 2010 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -26,32 +28,35 @@ class SPPainter; #define SP_IS_PAINT_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_PAINT_SERVER)) typedef enum { - SP_PAINTER_IND, - SP_PAINTER_DEP + SP_PAINTER_IND, + SP_PAINTER_DEP } SPPainterType; typedef void (* SPPainterFillFunc) (SPPainter *painter, NRPixBlock *pb); /* fixme: I do not like that class thingie (Lauris) */ struct SPPainter { - SPPainter *next; - SPPaintServer *server; - GType server_type; - SPPainterType type; - SPPainterFillFunc fill; + SPPainter *next; + SPPaintServer *server; + GType server_type; + SPPainterType type; + SPPainterFillFunc fill; }; struct SPPaintServer : public SPObject { - /* List of paints */ - SPPainter *painters; + /** List of paints */ + SPPainter *painters; + + bool isSwatch() const; + bool isSolid() const; }; struct SPPaintServerClass { - SPObjectClass sp_object_class; - /* Get SPPaint instance */ - SPPainter * (* painter_new) (SPPaintServer *ps, Geom::Matrix const &full_transform, Geom::Matrix const &parent_transform, const NRRect *bbox); - /* Free SPPaint instance */ - void (* painter_free) (SPPaintServer *ps, SPPainter *painter); + SPObjectClass sp_object_class; + /** Get SPPaint instance. */ + SPPainter * (* painter_new) (SPPaintServer *ps, Geom::Matrix const &full_transform, Geom::Matrix const &parent_transform, const NRRect *bbox); + /** Free SPPaint instance. */ + void (* painter_free) (SPPaintServer *ps, SPPainter *painter); }; GType sp_paint_server_get_type (void); @@ -62,15 +67,25 @@ SPPainter *sp_painter_free (SPPainter *painter); class SPPaintServerReference : public Inkscape::URIReference { public: - SPPaintServerReference (SPObject *obj) : URIReference(obj) {} - SPPaintServerReference (SPDocument *doc) : URIReference(doc) {} - SPPaintServer *getObject() const { - return (SPPaintServer *)URIReference::getObject(); - } + SPPaintServerReference (SPObject *obj) : URIReference(obj) {} + SPPaintServerReference (SPDocument *doc) : URIReference(doc) {} + SPPaintServer *getObject() const { + return static_cast<SPPaintServer *>(URIReference::getObject()); + } protected: - virtual bool _acceptObject(SPObject *obj) const { - return SP_IS_PAINT_SERVER (obj); - } + virtual bool _acceptObject(SPObject *obj) const { + return SP_IS_PAINT_SERVER (obj); + } }; -#endif +#endif // SEEN_SP_PAINT_SERVER_H +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/sp-path.cpp b/src/sp-path.cpp index bbcb25356..54d2a201a 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -298,6 +298,8 @@ sp_path_set(SPObject *object, unsigned int key, gchar const *value) case SP_ATTR_CONNECTOR_CURVATURE: case SP_ATTR_CONNECTION_START: case SP_ATTR_CONNECTION_END: + case SP_ATTR_CONNECTION_START_POINT: + case SP_ATTR_CONNECTION_END_POINT: path->connEndPair.setAttr(key, value); break; default: diff --git a/src/sp-rect.cpp b/src/sp-rect.cpp index aa026abb3..bdfae7c99 100644 --- a/src/sp-rect.cpp +++ b/src/sp-rect.cpp @@ -46,7 +46,7 @@ static Geom::Matrix sp_rect_set_transform(SPItem *item, Geom::Matrix const &xfor static void sp_rect_convert_to_guides(SPItem *item); static void sp_rect_set_shape(SPShape *shape); -static void sp_rect_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_rect_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static SPShapeClass *parent_class; @@ -278,10 +278,9 @@ sp_rect_set_shape(SPShape *shape) c->lineto(x + w, y + 0.0); c->lineto(x + w, y + h); c->lineto(x + 0.0, y + h); - c->lineto(x + 0.0, y + 0.0); } - c->closepath_current(); + c->closepath(); sp_shape_set_curve_insync(SP_SHAPE(rect), c, TRUE); c->unref(); } @@ -552,7 +551,7 @@ sp_rect_get_visible_height(SPRect *rect) /** * Sets the snappoint p to the unrounded corners of the rectangle */ -static void sp_rect_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_rect_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { /* This method overrides sp_shape_snappoints, which is the default for any shape. The default method returns all eight points along the path of a rounded rectangle, but not the real corners. Snapping @@ -565,9 +564,9 @@ static void sp_rect_snappoints(SPItem const *item, bool const target, SnapPoints g_assert(SP_IS_RECT(item)); // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes - if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { - return; - } + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } SPRect *rect = SP_RECT(item); @@ -578,28 +577,23 @@ static void sp_rect_snappoints(SPItem const *item, bool const target, SnapPoints Geom::Point p2 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed) * i2d; Geom::Point p3 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed) * i2d; - int type; - if (snapprefs->getSnapToItemNode()) { - type = target ? int(Inkscape::SNAPTARGET_CORNER) : int(Inkscape::SNAPSOURCE_CORNER); - p.push_back(std::make_pair(p0, type)); - p.push_back(std::make_pair(p1, type)); - p.push_back(std::make_pair(p2, type)); - p.push_back(std::make_pair(p3, type)); + p.push_back(Inkscape::SnapCandidatePoint(p0, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(p1, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(p2, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); + p.push_back(Inkscape::SnapCandidatePoint(p3, Inkscape::SNAPSOURCE_CORNER, Inkscape::SNAPTARGET_CORNER)); } - if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) - type = target ? int(Inkscape::SNAPTARGET_LINE_MIDPOINT) : int(Inkscape::SNAPSOURCE_LINE_MIDPOINT); - p.push_back(std::make_pair((p0 + p1)/2, type)); - p.push_back(std::make_pair((p1 + p2)/2, type)); - p.push_back(std::make_pair((p2 + p3)/2, type)); - p.push_back(std::make_pair((p3 + p0)/2, type)); - } - - if (snapprefs->getSnapObjectMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) - type = target ? int(Inkscape::SNAPTARGET_OBJECT_MIDPOINT) : int(Inkscape::SNAPSOURCE_OBJECT_MIDPOINT); - p.push_back(std::make_pair((p0 + p2)/2, type)); - } + if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) + p.push_back(Inkscape::SnapCandidatePoint((p0 + p1)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); + p.push_back(Inkscape::SnapCandidatePoint((p1 + p2)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); + p.push_back(Inkscape::SnapCandidatePoint((p2 + p3)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); + p.push_back(Inkscape::SnapCandidatePoint((p3 + p0)/2, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); + } + + if (snapprefs->getSnapObjectMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) + p.push_back(Inkscape::SnapCandidatePoint((p0 + p2)/2, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT)); + } } diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index 519002e9e..de5648137 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -68,7 +68,7 @@ static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const & void sp_shape_print (SPItem * item, SPPrintContext * ctx); static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags); static void sp_shape_hide (SPItem *item, unsigned int key); -static void sp_shape_snappoints (SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_shape_snappoints (SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai); @@ -1149,7 +1149,7 @@ sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner) /** * Return all nodes in a path that are to be considered for snapping */ -static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_shape_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { g_assert(item != NULL); g_assert(SP_IS_SHAPE(item)); @@ -1161,7 +1161,7 @@ static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPoint // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { - return; + return; } Geom::PathVector const &pathv = shape->curve->get_pathvector(); @@ -1170,20 +1170,16 @@ static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPoint Geom::Matrix const i2d (sp_item_i2d_affine (item)); - int type; - - if (snapprefs->getSnapObjectMidpoints()) { - Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item)); - if (bbox) { - type = target ? int(Inkscape::SNAPTARGET_OBJECT_MIDPOINT) : int(Inkscape::SNAPSOURCE_OBJECT_MIDPOINT); - p.push_back(std::make_pair(bbox->midpoint(), type)); - } - } + if (snapprefs->getSnapObjectMidpoints()) { + Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item)); + if (bbox) { + p.push_back(Inkscape::SnapCandidatePoint(bbox->midpoint(), Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT)); + } + } for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { if (snapprefs->getSnapToItemNode()) { - type = target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP); - p.push_back(std::make_pair(path_it->initialPoint() * i2d, type)); + p.push_back(Inkscape::SnapCandidatePoint(path_it->initialPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve @@ -1202,17 +1198,15 @@ static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPoint bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM); if (c1 || c2) { - type = target ? int(Inkscape::SNAPTARGET_NODE_CUSP) : int(Inkscape::SNAPSOURCE_NODE_CUSP); - p.push_back(std::make_pair(curve_it1->finalPoint() * i2d, type)); + p.push_back(Inkscape::SnapCandidatePoint(curve_it1->finalPoint() * i2d, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } - // Consider midpoints of line segments for snapping - if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) - if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) { - type = target ? int(Inkscape::SNAPTARGET_LINE_MIDPOINT) : int(Inkscape::SNAPSOURCE_LINE_MIDPOINT); - p.push_back(std::make_pair(Geom::middle_point(*line_segment) * i2d, type)); - } - } + // Consider midpoints of line segments for snapping + if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) + if (Geom::LineSegment const* line_segment = dynamic_cast<Geom::LineSegment const*>(&(*curve_it1))) { + p.push_back(Inkscape::SnapCandidatePoint(Geom::middle_point(*line_segment) * i2d, Inkscape::SNAPSOURCE_LINE_MIDPOINT, Inkscape::SNAPTARGET_LINE_MIDPOINT)); + } + } ++curve_it1; ++curve_it2; @@ -1226,8 +1220,7 @@ static void sp_shape_snappoints(SPItem const *item, bool const target, SnapPoint if (cs.size() > 0) { // There might be multiple intersections... for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) { Geom::Point p_ix = (*path_it).pointAt((*i).ta); - type = target ? int(Inkscape::SNAPTARGET_PATH_INTERSECTION) : int(Inkscape::SNAPSOURCE_PATH_INTERSECTION); - p.push_back(std::make_pair(p_ix * i2d, type)); + p.push_back(Inkscape::SnapCandidatePoint(p_ix * i2d, Inkscape::SNAPSOURCE_PATH_INTERSECTION, Inkscape::SNAPTARGET_PATH_INTERSECTION)); } } } diff --git a/src/sp-spiral.cpp b/src/sp-spiral.cpp index 629715332..11e84d9b2 100644 --- a/src/sp-spiral.cpp +++ b/src/sp-spiral.cpp @@ -37,7 +37,7 @@ static void sp_spiral_set (SPObject *object, unsigned int key, const gchar *valu static void sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags); static gchar * sp_spiral_description (SPItem * item); -static void sp_spiral_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_spiral_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_spiral_set_shape (SPShape *shape); static void sp_spiral_update_patheffect (SPLPEItem *lpeitem, bool write); @@ -52,24 +52,24 @@ static SPShapeClass *parent_class; GType sp_spiral_get_type (void) { - static GType spiral_type = 0; - - if (!spiral_type) { - GTypeInfo spiral_info = { - sizeof (SPSpiralClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) sp_spiral_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (SPSpiral), - 16, /* n_preallocs */ - (GInstanceInitFunc) sp_spiral_init, - NULL, /* value_table */ - }; - spiral_type = g_type_register_static (SP_TYPE_SHAPE, "SPSpiral", &spiral_info, (GTypeFlags)0); - } - return spiral_type; + static GType spiral_type = 0; + + if (!spiral_type) { + GTypeInfo spiral_info = { + sizeof (SPSpiralClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_spiral_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (SPSpiral), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_spiral_init, + NULL, /* value_table */ + }; + spiral_type = g_type_register_static (SP_TYPE_SHAPE, "SPSpiral", &spiral_info, (GTypeFlags)0); + } + return spiral_type; } /** @@ -78,27 +78,27 @@ sp_spiral_get_type (void) static void sp_spiral_class_init (SPSpiralClass *klass) { - GObjectClass * gobject_class; - SPObjectClass * sp_object_class; - SPItemClass * item_class; - SPLPEItemClass * lpe_item_class; - SPShapeClass *shape_class; + GObjectClass * gobject_class; + SPObjectClass * sp_object_class; + SPItemClass * item_class; + SPLPEItemClass * lpe_item_class; + SPShapeClass *shape_class; - gobject_class = (GObjectClass *) klass; - sp_object_class = (SPObjectClass *) klass; - item_class = (SPItemClass *) klass; - lpe_item_class = (SPLPEItemClass *) klass; - shape_class = (SPShapeClass *) klass; + gobject_class = (GObjectClass *) klass; + sp_object_class = (SPObjectClass *) klass; + item_class = (SPItemClass *) klass; + lpe_item_class = (SPLPEItemClass *) klass; + shape_class = (SPShapeClass *) klass; - parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE); + parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE); - sp_object_class->build = sp_spiral_build; - sp_object_class->write = sp_spiral_write; - sp_object_class->set = sp_spiral_set; - sp_object_class->update = sp_spiral_update; + sp_object_class->build = sp_spiral_build; + sp_object_class->write = sp_spiral_write; + sp_object_class->set = sp_spiral_set; + sp_object_class->update = sp_spiral_update; - item_class->description = sp_spiral_description; - item_class->snappoints = sp_spiral_snappoints; + item_class->description = sp_spiral_description; + item_class->snappoints = sp_spiral_snappoints; lpe_item_class->update_patheffect = sp_spiral_update_patheffect; @@ -111,13 +111,13 @@ sp_spiral_class_init (SPSpiralClass *klass) static void sp_spiral_init (SPSpiral * spiral) { - spiral->cx = 0.0; - spiral->cy = 0.0; - spiral->exp = 1.0; - spiral->revo = 3.0; - spiral->rad = 1.0; - spiral->arg = 0.0; - spiral->t0 = 0.0; + spiral->cx = 0.0; + spiral->cy = 0.0; + spiral->exp = 1.0; + spiral->revo = 3.0; + spiral->rad = 1.0; + spiral->arg = 0.0; + spiral->t0 = 0.0; } /** @@ -126,16 +126,16 @@ sp_spiral_init (SPSpiral * spiral) static void sp_spiral_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr) { - if (((SPObjectClass *) parent_class)->build) - ((SPObjectClass *) parent_class)->build (object, document, repr); - - sp_object_read_attr (object, "sodipodi:cx"); - sp_object_read_attr (object, "sodipodi:cy"); - sp_object_read_attr (object, "sodipodi:expansion"); - sp_object_read_attr (object, "sodipodi:revolution"); - sp_object_read_attr (object, "sodipodi:radius"); - sp_object_read_attr (object, "sodipodi:argument"); - sp_object_read_attr (object, "sodipodi:t0"); + if (((SPObjectClass *) parent_class)->build) + ((SPObjectClass *) parent_class)->build (object, document, repr); + + sp_object_read_attr (object, "sodipodi:cx"); + sp_object_read_attr (object, "sodipodi:cy"); + sp_object_read_attr (object, "sodipodi:expansion"); + sp_object_read_attr (object, "sodipodi:revolution"); + sp_object_read_attr (object, "sodipodi:radius"); + sp_object_read_attr (object, "sodipodi:argument"); + sp_object_read_attr (object, "sodipodi:t0"); } /** @@ -144,25 +144,25 @@ sp_spiral_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * static Inkscape::XML::Node * sp_spiral_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { - SPSpiral *spiral = SP_SPIRAL (object); - - if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - repr = xml_doc->createElement("svg:path"); - } - - if (flags & SP_OBJECT_WRITE_EXT) { - /* Fixme: we may replace these attributes by - * sodipodi:spiral="cx cy exp revo rad arg t0" - */ - repr->setAttribute("sodipodi:type", "spiral"); - sp_repr_set_svg_double(repr, "sodipodi:cx", spiral->cx); - sp_repr_set_svg_double(repr, "sodipodi:cy", spiral->cy); - sp_repr_set_svg_double(repr, "sodipodi:expansion", spiral->exp); - sp_repr_set_svg_double(repr, "sodipodi:revolution", spiral->revo); - sp_repr_set_svg_double(repr, "sodipodi:radius", spiral->rad); - sp_repr_set_svg_double(repr, "sodipodi:argument", spiral->arg); - sp_repr_set_svg_double(repr, "sodipodi:t0", spiral->t0); - } + SPSpiral *spiral = SP_SPIRAL (object); + + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { + repr = xml_doc->createElement("svg:path"); + } + + if (flags & SP_OBJECT_WRITE_EXT) { + /* Fixme: we may replace these attributes by + * sodipodi:spiral="cx cy exp revo rad arg t0" + */ + repr->setAttribute("sodipodi:type", "spiral"); + sp_repr_set_svg_double(repr, "sodipodi:cx", spiral->cx); + sp_repr_set_svg_double(repr, "sodipodi:cy", spiral->cy); + sp_repr_set_svg_double(repr, "sodipodi:expansion", spiral->exp); + sp_repr_set_svg_double(repr, "sodipodi:revolution", spiral->revo); + sp_repr_set_svg_double(repr, "sodipodi:radius", spiral->rad); + sp_repr_set_svg_double(repr, "sodipodi:argument", spiral->arg); + sp_repr_set_svg_double(repr, "sodipodi:t0", spiral->t0); + } // make sure the curve is rebuilt with all up-to-date parameters sp_spiral_set_shape ((SPShape *) spiral); @@ -190,61 +190,61 @@ sp_spiral_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::X static void sp_spiral_set (SPObject *object, unsigned int key, const gchar *value) { - SPSpiral *spiral; - SPShape *shape; - - spiral = SP_SPIRAL (object); - shape = SP_SHAPE (object); - - /// \todo fixme: we should really collect updates - switch (key) { - case SP_ATTR_SODIPODI_CX: - if (!sp_svg_length_read_computed_absolute (value, &spiral->cx)) { - spiral->cx = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_CY: - if (!sp_svg_length_read_computed_absolute (value, &spiral->cy)) { - spiral->cy = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_EXPANSION: - if (value) { - /** \todo + SPSpiral *spiral; + SPShape *shape; + + spiral = SP_SPIRAL (object); + shape = SP_SHAPE (object); + + /// \todo fixme: we should really collect updates + switch (key) { + case SP_ATTR_SODIPODI_CX: + if (!sp_svg_length_read_computed_absolute (value, &spiral->cx)) { + spiral->cx = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_CY: + if (!sp_svg_length_read_computed_absolute (value, &spiral->cy)) { + spiral->cy = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_EXPANSION: + if (value) { + /** \todo * FIXME: check that value looks like a (finite) * number. Create a routine that uses strtod, and * accepts a default value (if strtod finds an error). * N.B. atof/sscanf/strtod consider "nan" and "inf" * to be valid numbers. */ - spiral->exp = g_ascii_strtod (value, NULL); - spiral->exp = CLAMP (spiral->exp, 0.0, 1000.0); - } else { - spiral->exp = 1.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_REVOLUTION: - if (value) { - spiral->revo = g_ascii_strtod (value, NULL); - spiral->revo = CLAMP (spiral->revo, 0.05, 1024.0); - } else { - spiral->revo = 3.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_RADIUS: - if (!sp_svg_length_read_computed_absolute (value, &spiral->rad)) { - spiral->rad = MAX (spiral->rad, 0.001); - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_ARGUMENT: - if (value) { - spiral->arg = g_ascii_strtod (value, NULL); - /** \todo + spiral->exp = g_ascii_strtod (value, NULL); + spiral->exp = CLAMP (spiral->exp, 0.0, 1000.0); + } else { + spiral->exp = 1.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_REVOLUTION: + if (value) { + spiral->revo = g_ascii_strtod (value, NULL); + spiral->revo = CLAMP (spiral->revo, 0.05, 1024.0); + } else { + spiral->revo = 3.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_RADIUS: + if (!sp_svg_length_read_computed_absolute (value, &spiral->rad)) { + spiral->rad = MAX (spiral->rad, 0.001); + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_ARGUMENT: + if (value) { + spiral->arg = g_ascii_strtod (value, NULL); + /** \todo * FIXME: We still need some bounds on arg, for * numerical reasons. E.g., we don't want inf or NaN, * nor near-infinite numbers. I'm inclined to take @@ -252,32 +252,32 @@ sp_spiral_set (SPObject *object, unsigned int key, const gchar *value) * which use atan2 - revo*2*pi, which typically * results in very negative arg. */ - } else { - spiral->arg = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_T0: - if (value) { - spiral->t0 = g_ascii_strtod (value, NULL); - spiral->t0 = CLAMP (spiral->t0, 0.0, 0.999); - /** \todo + } else { + spiral->arg = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_T0: + if (value) { + spiral->t0 = g_ascii_strtod (value, NULL); + spiral->t0 = CLAMP (spiral->t0, 0.0, 0.999); + /** \todo * Have shared constants for the allowable bounds for * attributes. There was a bug here where we used -1.0 * as the minimum (which leads to NaN via, e.g., * pow(-1.0, 0.5); see sp_spiral_get_xy for * requirements. */ - } else { - spiral->t0 = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - default: - if (((SPObjectClass *) parent_class)->set) - ((SPObjectClass *) parent_class)->set (object, key, value); - break; - } + } else { + spiral->t0 = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + default: + if (((SPObjectClass *) parent_class)->set) + ((SPObjectClass *) parent_class)->set (object, key, value); + break; + } } /** @@ -286,12 +286,12 @@ sp_spiral_set (SPObject *object, unsigned int key, const gchar *value) static void sp_spiral_update (SPObject *object, SPCtx *ctx, guint flags) { - if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { - sp_shape_set_shape ((SPShape *) object); - } + if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + sp_shape_set_shape ((SPShape *) object); + } - if (((SPObjectClass *) parent_class)->update) - ((SPObjectClass *) parent_class)->update (object, ctx, flags); + if (((SPObjectClass *) parent_class)->update) + ((SPObjectClass *) parent_class)->update (object, ctx, flags); } static void @@ -320,9 +320,9 @@ sp_spiral_update_patheffect(SPLPEItem *lpeitem, bool write) static gchar * sp_spiral_description (SPItem * item) { - // TRANSLATORS: since turn count isn't an integer, please adjust the - // string as needed to deal with an localized plural forms. - return g_strdup_printf (_("<b>Spiral</b> with %3f turns"), SP_SPIRAL(item)->revo); + // TRANSLATORS: since turn count isn't an integer, please adjust the + // string as needed to deal with an localized plural forms. + return g_strdup_printf (_("<b>Spiral</b> with %3f turns"), SP_SPIRAL(item)->revo); } @@ -335,34 +335,34 @@ sp_spiral_description (SPItem * item) **/ static void sp_spiral_fit_and_draw (SPSpiral const *spiral, - SPCurve *c, - double dstep, - Geom::Point darray[], - Geom::Point const &hat1, - Geom::Point &hat2, - double *t) + SPCurve *c, + double dstep, + Geom::Point darray[], + Geom::Point const &hat1, + Geom::Point &hat2, + double *t) { #define BEZIER_SIZE 4 #define FITTING_MAX_BEZIERS 4 #define BEZIER_LENGTH (BEZIER_SIZE * FITTING_MAX_BEZIERS) - g_assert (dstep > 0); - g_assert (is_unit_vector (hat1)); - - Geom::Point bezier[BEZIER_LENGTH]; - double d; - int depth, i; - - for (d = *t, i = 0; i <= SAMPLE_SIZE; d += dstep, i++) { - darray[i] = sp_spiral_get_xy(spiral, d); - - /* Avoid useless adjacent dups. (Otherwise we can have all of darray filled with - the same value, which upsets chord_length_parameterize.) */ - if ((i != 0) - && (darray[i] == darray[i - 1]) - && (d < 1.0)) { - i--; - d += dstep; - /** We mustn't increase dstep for subsequent values of + g_assert (dstep > 0); + g_assert (is_unit_vector (hat1)); + + Geom::Point bezier[BEZIER_LENGTH]; + double d; + int depth, i; + + for (d = *t, i = 0; i <= SAMPLE_SIZE; d += dstep, i++) { + darray[i] = sp_spiral_get_xy(spiral, d); + + /* Avoid useless adjacent dups. (Otherwise we can have all of darray filled with + the same value, which upsets chord_length_parameterize.) */ + if ((i != 0) + && (darray[i] == darray[i - 1]) + && (d < 1.0)) { + i--; + d += dstep; + /** We mustn't increase dstep for subsequent values of * i: for large spiral.exp values, rate of growth * increases very rapidly. */ @@ -378,48 +378,48 @@ sp_spiral_fit_and_draw (SPSpiral const *spiral, * value for next iteration to avoid the problem * mentioned above. */ - } - } + } + } - double const next_t = d - 2 * dstep; - /* == t + (SAMPLE_SIZE - 1) * dstep, in absence of dups. */ + double const next_t = d - 2 * dstep; + /* == t + (SAMPLE_SIZE - 1) * dstep, in absence of dups. */ - hat2 = -sp_spiral_get_tangent (spiral, next_t); + hat2 = -sp_spiral_get_tangent (spiral, next_t); - /** \todo + /** \todo * We should use better algorithm to specify maximum error. */ - depth = Geom::bezier_fit_cubic_full (bezier, NULL, darray, SAMPLE_SIZE, - hat1, hat2, - SPIRAL_TOLERANCE*SPIRAL_TOLERANCE, - FITTING_MAX_BEZIERS); - g_assert(depth * BEZIER_SIZE <= gint(G_N_ELEMENTS(bezier))); + depth = Geom::bezier_fit_cubic_full (bezier, NULL, darray, SAMPLE_SIZE, + hat1, hat2, + SPIRAL_TOLERANCE*SPIRAL_TOLERANCE, + FITTING_MAX_BEZIERS); + g_assert(depth * BEZIER_SIZE <= gint(G_N_ELEMENTS(bezier))); #ifdef SPIRAL_DEBUG - if (*t == spiral->t0 || *t == 1.0) - g_print ("[%s] depth=%d, dstep=%g, t0=%g, t=%g, arg=%g\n", - debug_state, depth, dstep, spiral->t0, *t, spiral->arg); + if (*t == spiral->t0 || *t == 1.0) + g_print ("[%s] depth=%d, dstep=%g, t0=%g, t=%g, arg=%g\n", + debug_state, depth, dstep, spiral->t0, *t, spiral->arg); #endif - if (depth != -1) { - for (i = 0; i < 4*depth; i += 4) { - c->curveto(bezier[i + 1], - bezier[i + 2], - bezier[i + 3]); - } - } else { + if (depth != -1) { + for (i = 0; i < 4*depth; i += 4) { + c->curveto(bezier[i + 1], + bezier[i + 2], + bezier[i + 3]); + } + } else { #ifdef SPIRAL_VERBOSE - g_print ("cant_fit_cubic: t=%g\n", *t); + g_print ("cant_fit_cubic: t=%g\n", *t); #endif - for (i = 1; i < SAMPLE_SIZE; i++) - c->lineto(darray[i]); - } - *t = next_t; - g_assert (is_unit_vector (hat2)); + for (i = 1; i < SAMPLE_SIZE; i++) + c->lineto(darray[i]); + } + *t = next_t; + g_assert (is_unit_vector (hat2)); } static void sp_spiral_set_shape (SPShape *shape) { - SPSpiral *spiral = SP_SPIRAL(shape); + SPSpiral *spiral = SP_SPIRAL(shape); if (sp_lpe_item_has_broken_path_effect(SP_LPE_ITEM(shape))) { g_warning ("The spiral shape has unknown LPE on it! Convert to path to make it editable preserving the appearance; editing it as spiral will remove the bad LPE"); @@ -433,40 +433,40 @@ sp_spiral_set_shape (SPShape *shape) return; } - Geom::Point darray[SAMPLE_SIZE + 1]; - double t; + Geom::Point darray[SAMPLE_SIZE + 1]; + double t; - SP_OBJECT (spiral)->requestModified(SP_OBJECT_MODIFIED_FLAG); + SP_OBJECT (spiral)->requestModified(SP_OBJECT_MODIFIED_FLAG); - SPCurve *c = new SPCurve (); + SPCurve *c = new SPCurve (); #ifdef SPIRAL_VERBOSE - g_print ("cx=%g, cy=%g, exp=%g, revo=%g, rad=%g, arg=%g, t0=%g\n", - spiral->cx, - spiral->cy, - spiral->exp, - spiral->revo, - spiral->rad, - spiral->arg, - spiral->t0); + g_print ("cx=%g, cy=%g, exp=%g, revo=%g, rad=%g, arg=%g, t0=%g\n", + spiral->cx, + spiral->cy, + spiral->exp, + spiral->revo, + spiral->rad, + spiral->arg, + spiral->t0); #endif - /* Initial moveto. */ - c->moveto(sp_spiral_get_xy(spiral, spiral->t0)); + /* Initial moveto. */ + c->moveto(sp_spiral_get_xy(spiral, spiral->t0)); - double const tstep = SAMPLE_STEP / spiral->revo; - double const dstep = tstep / (SAMPLE_SIZE - 1); + double const tstep = SAMPLE_STEP / spiral->revo; + double const dstep = tstep / (SAMPLE_SIZE - 1); - Geom::Point hat1 = sp_spiral_get_tangent (spiral, spiral->t0); - Geom::Point hat2; - for (t = spiral->t0; t < (1.0 - tstep);) { - sp_spiral_fit_and_draw (spiral, c, dstep, darray, hat1, hat2, &t); + Geom::Point hat1 = sp_spiral_get_tangent (spiral, spiral->t0); + Geom::Point hat2; + for (t = spiral->t0; t < (1.0 - tstep);) { + sp_spiral_fit_and_draw (spiral, c, dstep, darray, hat1, hat2, &t); - hat1 = -hat2; - } - if ((1.0 - t) > SP_EPSILON) - sp_spiral_fit_and_draw (spiral, c, (1.0 - t)/(SAMPLE_SIZE - 1.0), - darray, hat1, hat2, &t); + hat1 = -hat2; + } + if ((1.0 - t) > SP_EPSILON) + sp_spiral_fit_and_draw (spiral, c, (1.0 - t)/(SAMPLE_SIZE - 1.0), + darray, hat1, hat2, &t); /* Reset the shape'scurve to the "original_curve" * This is very important for LPEs to work properly! (the bbox might be recalculated depending on the curve in shape)*/ @@ -487,59 +487,58 @@ sp_spiral_set_shape (SPShape *shape) */ void sp_spiral_position_set (SPSpiral *spiral, - gdouble cx, - gdouble cy, - gdouble exp, - gdouble revo, - gdouble rad, - gdouble arg, - gdouble t0) + gdouble cx, + gdouble cy, + gdouble exp, + gdouble revo, + gdouble rad, + gdouble arg, + gdouble t0) { - g_return_if_fail (spiral != NULL); - g_return_if_fail (SP_IS_SPIRAL (spiral)); + g_return_if_fail (spiral != NULL); + g_return_if_fail (SP_IS_SPIRAL (spiral)); - /** \todo + /** \todo * Consider applying CLAMP or adding in-bounds assertions for * some of these parameters. */ - spiral->cx = cx; - spiral->cy = cy; - spiral->exp = exp; - spiral->revo = revo; - spiral->rad = MAX (rad, 0.0); - spiral->arg = arg; - spiral->t0 = CLAMP(t0, 0.0, 0.999); - - ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + spiral->cx = cx; + spiral->cy = cy; + spiral->exp = exp; + spiral->revo = revo; + spiral->rad = MAX (rad, 0.0); + spiral->arg = arg; + spiral->t0 = CLAMP(t0, 0.0, 0.999); + + ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } /** * Virtual snappoints callback. */ -static void sp_spiral_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_spiral_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { - // We will determine the spiral's midpoint ourselves, instead of trusting on the base class - // Therefore setSnapObjectMidpoints() is set to false temporarily - Inkscape::SnapPreferences local_snapprefs = *snapprefs; - local_snapprefs.setSnapObjectMidpoints(false); - - if (((SPItemClass *) parent_class)->snappoints) { - ((SPItemClass *) parent_class)->snappoints (item, target, p, &local_snapprefs); - } - - // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes - if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { - return; - } - - if (snapprefs->getSnapObjectMidpoints()) { - Geom::Matrix const i2d (sp_item_i2d_affine (item)); - SPSpiral *spiral = SP_SPIRAL(item); - int type = target ? int(Inkscape::SNAPTARGET_OBJECT_MIDPOINT) : int(Inkscape::SNAPSOURCE_OBJECT_MIDPOINT); - p.push_back(std::make_pair(Geom::Point(spiral->cx, spiral->cy) * i2d, type)); - // This point is the start-point of the spiral, which is also returned when _snap_to_itemnode has been set - // in the object snapper. In that case we will get a duplicate! - } + // We will determine the spiral's midpoint ourselves, instead of trusting on the base class + // Therefore setSnapObjectMidpoints() is set to false temporarily + Inkscape::SnapPreferences local_snapprefs = *snapprefs; + local_snapprefs.setSnapObjectMidpoints(false); + + if (((SPItemClass *) parent_class)->snappoints) { + ((SPItemClass *) parent_class)->snappoints (item, p, &local_snapprefs); + } + + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + + if (snapprefs->getSnapObjectMidpoints()) { + Geom::Matrix const i2d (sp_item_i2d_affine (item)); + SPSpiral *spiral = SP_SPIRAL(item); + p.push_back(Inkscape::SnapCandidatePoint(Geom::Point(spiral->cx, spiral->cy) * i2d, Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT)); + // This point is the start-point of the spiral, which is also returned when _snap_to_itemnode has been set + // in the object snapper. In that case we will get a duplicate! + } } /** @@ -552,19 +551,19 @@ static void sp_spiral_snappoints(SPItem const *item, bool const target, SnapPoin */ Geom::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t) { - g_assert (spiral != NULL); - g_assert (SP_IS_SPIRAL(spiral)); - g_assert (spiral->exp >= 0.0); - /* Otherwise we get NaN for t==0. */ - g_assert (spiral->exp <= 1000.0); - /* Anything much more results in infinities. Even allowing 1000 is somewhat overkill. */ - g_assert (t >= 0.0); - /* Any callers passing -ve t will have a bug for non-integral values of exp. */ - - double const rad = spiral->rad * pow(t, (double) spiral->exp); - double const arg = 2.0 * M_PI * spiral->revo * t + spiral->arg; - - return Geom::Point(rad * cos (arg) + spiral->cx, + g_assert (spiral != NULL); + g_assert (SP_IS_SPIRAL(spiral)); + g_assert (spiral->exp >= 0.0); + /* Otherwise we get NaN for t==0. */ + g_assert (spiral->exp <= 1000.0); + /* Anything much more results in infinities. Even allowing 1000 is somewhat overkill. */ + g_assert (t >= 0.0); + /* Any callers passing -ve t will have a bug for non-integral values of exp. */ + + double const rad = spiral->rad * pow(t, (double) spiral->exp); + double const arg = 2.0 * M_PI * spiral->revo * t + spiral->arg; + + return Geom::Point(rad * cos (arg) + spiral->cx, rad * sin (arg) + spiral->cy); } @@ -581,58 +580,58 @@ Geom::Point sp_spiral_get_xy (SPSpiral const *spiral, gdouble t) static Geom::Point sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t) { - Geom::Point ret(1.0, 0.0); - g_return_val_if_fail (( ( spiral != NULL ) - && SP_IS_SPIRAL(spiral) ), - ret); - g_assert (t >= 0.0); - g_assert (spiral->exp >= 0.0); - /* See above for comments on these assertions. */ - - double const t_scaled = 2.0 * M_PI * spiral->revo * t; - double const arg = t_scaled + spiral->arg; - double const s = sin (arg); - double const c = cos (arg); - - if (spiral->exp == 0.0) { - ret = Geom::Point(-s, c); - } else if (t_scaled == 0.0) { - ret = Geom::Point(c, s); - } else { - Geom::Point unrotated(spiral->exp, t_scaled); - double const s_len = L2 (unrotated); - g_assert (s_len != 0); - /** \todo + Geom::Point ret(1.0, 0.0); + g_return_val_if_fail (( ( spiral != NULL ) + && SP_IS_SPIRAL(spiral) ), + ret); + g_assert (t >= 0.0); + g_assert (spiral->exp >= 0.0); + /* See above for comments on these assertions. */ + + double const t_scaled = 2.0 * M_PI * spiral->revo * t; + double const arg = t_scaled + spiral->arg; + double const s = sin (arg); + double const c = cos (arg); + + if (spiral->exp == 0.0) { + ret = Geom::Point(-s, c); + } else if (t_scaled == 0.0) { + ret = Geom::Point(c, s); + } else { + Geom::Point unrotated(spiral->exp, t_scaled); + double const s_len = L2 (unrotated); + g_assert (s_len != 0); + /** \todo * Check that this isn't being too hopeful of the hypot * function. E.g. test with numbers around 2**-1070 * (denormalized numbers), preferably on a few different * platforms. However, njh says that the usual implementation * does handle both very big and very small numbers. */ - unrotated /= s_len; + unrotated /= s_len; - /* ret = spiral->exp * (c, s) + t_scaled * (-s, c); - alternatively ret = (spiral->exp, t_scaled) * (( c, s), - (-s, c)).*/ - ret = Geom::Point(dot(unrotated, Geom::Point(c, -s)), + /* ret = spiral->exp * (c, s) + t_scaled * (-s, c); + alternatively ret = (spiral->exp, t_scaled) * (( c, s), + (-s, c)).*/ + ret = Geom::Point(dot(unrotated, Geom::Point(c, -s)), dot(unrotated, Geom::Point(s, c))); - /* ret should already be approximately normalized: the - matrix ((c, -s), (s, c)) is orthogonal (it just - rotates by arg), and unrotated has been normalized, - so ret is already of unit length other than numerical - error in the above matrix multiplication. */ + /* ret should already be approximately normalized: the + matrix ((c, -s), (s, c)) is orthogonal (it just + rotates by arg), and unrotated has been normalized, + so ret is already of unit length other than numerical + error in the above matrix multiplication. */ - /** \todo + /** \todo * I haven't checked how important it is for ret to be very * near unit length; we could get rid of the below. */ - ret.normalize(); - /* Proof that ret length is non-zero: see above. (Should be near 1.) */ - } + ret.normalize(); + /* Proof that ret length is non-zero: see above. (Should be near 1.) */ + } - g_assert (is_unit_vector (ret)); - return ret; + g_assert (is_unit_vector (ret)); + return ret; } /** @@ -641,13 +640,13 @@ sp_spiral_get_tangent (SPSpiral const *spiral, gdouble t) void sp_spiral_get_polar (SPSpiral const *spiral, gdouble t, gdouble *rad, gdouble *arg) { - g_return_if_fail (spiral != NULL); - g_return_if_fail (SP_IS_SPIRAL(spiral)); + g_return_if_fail (spiral != NULL); + g_return_if_fail (SP_IS_SPIRAL(spiral)); - if (rad) - *rad = spiral->rad * pow(t, (double) spiral->exp); - if (arg) - *arg = 2.0 * M_PI * spiral->revo * t + spiral->arg; + if (rad) + *rad = spiral->rad * pow(t, (double) spiral->exp); + if (arg) + *arg = 2.0 * M_PI * spiral->revo * t + spiral->arg; } /** @@ -656,19 +655,19 @@ sp_spiral_get_polar (SPSpiral const *spiral, gdouble t, gdouble *rad, gdouble *a bool sp_spiral_is_invalid (SPSpiral const *spiral) { - gdouble rad; - - sp_spiral_get_polar (spiral, 0.0, &rad, NULL); - if (rad < 0.0 || rad > SP_HUGE) { - g_print ("rad(t=0)=%g\n", rad); - return TRUE; - } - sp_spiral_get_polar (spiral, 1.0, &rad, NULL); - if (rad < 0.0 || rad > SP_HUGE) { - g_print ("rad(t=1)=%g\n", rad); - return TRUE; - } - return FALSE; + gdouble rad; + + sp_spiral_get_polar (spiral, 0.0, &rad, NULL); + if (rad < 0.0 || rad > SP_HUGE) { + g_print ("rad(t=0)=%g\n", rad); + return TRUE; + } + sp_spiral_get_polar (spiral, 1.0, &rad, NULL); + if (rad < 0.0 || rad > SP_HUGE) { + g_print ("rad(t=1)=%g\n", rad); + return TRUE; + } + return FALSE; } /* diff --git a/src/sp-star.cpp b/src/sp-star.cpp index 9cffd952c..16c71d030 100644 --- a/src/sp-star.cpp +++ b/src/sp-star.cpp @@ -41,7 +41,7 @@ static void sp_star_set (SPObject *object, unsigned int key, const gchar *value) static void sp_star_update (SPObject *object, SPCtx *ctx, guint flags); static gchar * sp_star_description (SPItem * item); -static void sp_star_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_star_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_star_set_shape (SPShape *shape); static void sp_star_update_patheffect (SPLPEItem *lpeitem, bool write); @@ -51,230 +51,230 @@ static SPShapeClass *parent_class; GType sp_star_get_type (void) { - static GType type = 0; - - if (!type) { - GTypeInfo info = { - sizeof (SPStarClass), - NULL, NULL, - (GClassInitFunc) sp_star_class_init, - NULL, NULL, - sizeof (SPStar), - 16, - (GInstanceInitFunc) sp_star_init, - NULL, /* value_table */ - }; - type = g_type_register_static (SP_TYPE_SHAPE, "SPStar", &info, (GTypeFlags)0); - } - return type; + static GType type = 0; + + if (!type) { + GTypeInfo info = { + sizeof (SPStarClass), + NULL, NULL, + (GClassInitFunc) sp_star_class_init, + NULL, NULL, + sizeof (SPStar), + 16, + (GInstanceInitFunc) sp_star_init, + NULL, /* value_table */ + }; + type = g_type_register_static (SP_TYPE_SHAPE, "SPStar", &info, (GTypeFlags)0); + } + return type; } static void sp_star_class_init (SPStarClass *klass) { - GObjectClass * gobject_class; - SPObjectClass * sp_object_class; - SPItemClass * item_class; - SPLPEItemClass * lpe_item_class; - SPShapeClass * shape_class; + GObjectClass * gobject_class; + SPObjectClass * sp_object_class; + SPItemClass * item_class; + SPLPEItemClass * lpe_item_class; + SPShapeClass * shape_class; - gobject_class = (GObjectClass *) klass; - sp_object_class = (SPObjectClass *) klass; - item_class = (SPItemClass *) klass; - lpe_item_class = (SPLPEItemClass *) klass; - shape_class = (SPShapeClass *) klass; + gobject_class = (GObjectClass *) klass; + sp_object_class = (SPObjectClass *) klass; + item_class = (SPItemClass *) klass; + lpe_item_class = (SPLPEItemClass *) klass; + shape_class = (SPShapeClass *) klass; - parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE); + parent_class = (SPShapeClass *)g_type_class_ref (SP_TYPE_SHAPE); - sp_object_class->build = sp_star_build; - sp_object_class->write = sp_star_write; - sp_object_class->set = sp_star_set; - sp_object_class->update = sp_star_update; + sp_object_class->build = sp_star_build; + sp_object_class->write = sp_star_write; + sp_object_class->set = sp_star_set; + sp_object_class->update = sp_star_update; - item_class->description = sp_star_description; - item_class->snappoints = sp_star_snappoints; + item_class->description = sp_star_description; + item_class->snappoints = sp_star_snappoints; lpe_item_class->update_patheffect = sp_star_update_patheffect; - shape_class->set_shape = sp_star_set_shape; + shape_class->set_shape = sp_star_set_shape; } static void sp_star_init (SPStar * star) { - star->sides = 5; - star->center = Geom::Point(0, 0); - star->r[0] = 1.0; - star->r[1] = 0.001; - star->arg[0] = star->arg[1] = 0.0; - star->flatsided = 0; - star->rounded = 0.0; - star->randomized = 0.0; + star->sides = 5; + star->center = Geom::Point(0, 0); + star->r[0] = 1.0; + star->r[1] = 0.001; + star->arg[0] = star->arg[1] = 0.0; + star->flatsided = 0; + star->rounded = 0.0; + star->randomized = 0.0; } static void sp_star_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr) { - if (((SPObjectClass *) parent_class)->build) - ((SPObjectClass *) parent_class)->build (object, document, repr); - - sp_object_read_attr (object, "sodipodi:cx"); - sp_object_read_attr (object, "sodipodi:cy"); - sp_object_read_attr (object, "sodipodi:sides"); - sp_object_read_attr (object, "sodipodi:r1"); - sp_object_read_attr (object, "sodipodi:r2"); - sp_object_read_attr (object, "sodipodi:arg1"); - sp_object_read_attr (object, "sodipodi:arg2"); - sp_object_read_attr (object, "inkscape:flatsided"); - sp_object_read_attr (object, "inkscape:rounded"); - sp_object_read_attr (object, "inkscape:randomized"); + if (((SPObjectClass *) parent_class)->build) + ((SPObjectClass *) parent_class)->build (object, document, repr); + + sp_object_read_attr (object, "sodipodi:cx"); + sp_object_read_attr (object, "sodipodi:cy"); + sp_object_read_attr (object, "sodipodi:sides"); + sp_object_read_attr (object, "sodipodi:r1"); + sp_object_read_attr (object, "sodipodi:r2"); + sp_object_read_attr (object, "sodipodi:arg1"); + sp_object_read_attr (object, "sodipodi:arg2"); + sp_object_read_attr (object, "inkscape:flatsided"); + sp_object_read_attr (object, "inkscape:rounded"); + sp_object_read_attr (object, "inkscape:randomized"); } static Inkscape::XML::Node * sp_star_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { - SPStar *star = SP_STAR (object); - - if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { - repr = xml_doc->createElement("svg:path"); - } - - if (flags & SP_OBJECT_WRITE_EXT) { - repr->setAttribute("sodipodi:type", "star"); - sp_repr_set_int (repr, "sodipodi:sides", star->sides); - sp_repr_set_svg_double(repr, "sodipodi:cx", star->center[Geom::X]); - sp_repr_set_svg_double(repr, "sodipodi:cy", star->center[Geom::Y]); - sp_repr_set_svg_double(repr, "sodipodi:r1", star->r[0]); - sp_repr_set_svg_double(repr, "sodipodi:r2", star->r[1]); - sp_repr_set_svg_double(repr, "sodipodi:arg1", star->arg[0]); - sp_repr_set_svg_double(repr, "sodipodi:arg2", star->arg[1]); - sp_repr_set_boolean (repr, "inkscape:flatsided", star->flatsided); - sp_repr_set_svg_double(repr, "inkscape:rounded", star->rounded); - sp_repr_set_svg_double(repr, "inkscape:randomized", star->randomized); - } + SPStar *star = SP_STAR (object); + + if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { + repr = xml_doc->createElement("svg:path"); + } + + if (flags & SP_OBJECT_WRITE_EXT) { + repr->setAttribute("sodipodi:type", "star"); + sp_repr_set_int (repr, "sodipodi:sides", star->sides); + sp_repr_set_svg_double(repr, "sodipodi:cx", star->center[Geom::X]); + sp_repr_set_svg_double(repr, "sodipodi:cy", star->center[Geom::Y]); + sp_repr_set_svg_double(repr, "sodipodi:r1", star->r[0]); + sp_repr_set_svg_double(repr, "sodipodi:r2", star->r[1]); + sp_repr_set_svg_double(repr, "sodipodi:arg1", star->arg[0]); + sp_repr_set_svg_double(repr, "sodipodi:arg2", star->arg[1]); + sp_repr_set_boolean (repr, "inkscape:flatsided", star->flatsided); + sp_repr_set_svg_double(repr, "inkscape:rounded", star->rounded); + sp_repr_set_svg_double(repr, "inkscape:randomized", star->randomized); + } sp_star_set_shape ((SPShape *) star); char *d = sp_svg_write_path (((SPShape *) star)->curve->get_pathvector()); repr->setAttribute("d", d); g_free (d); - if (((SPObjectClass *) (parent_class))->write) - ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr, flags); + if (((SPObjectClass *) (parent_class))->write) + ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr, flags); - return repr; + return repr; } static void sp_star_set (SPObject *object, unsigned int key, const gchar *value) { - SVGLength::Unit unit; - - SPStar *star = SP_STAR (object); - - /* fixme: we should really collect updates */ - switch (key) { - case SP_ATTR_SODIPODI_SIDES: - if (value) { - star->sides = atoi (value); - star->sides = NR_CLAMP(star->sides, 3, 1024); - } else { - star->sides = 5; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_CX: - if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->center[Geom::X]) || - (unit == SVGLength::EM) || - (unit == SVGLength::EX) || - (unit == SVGLength::PERCENT)) { - star->center[Geom::X] = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_CY: - if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->center[Geom::Y]) || - (unit == SVGLength::EM) || - (unit == SVGLength::EX) || - (unit == SVGLength::PERCENT)) { - star->center[Geom::Y] = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_R1: - if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->r[0]) || - (unit == SVGLength::EM) || - (unit == SVGLength::EX) || - (unit == SVGLength::PERCENT)) { - star->r[0] = 1.0; - } - /* fixme: Need CLAMP (Lauris) */ - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_R2: - if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->r[1]) || - (unit == SVGLength::EM) || - (unit == SVGLength::EX) || - (unit == SVGLength::PERCENT)) { - star->r[1] = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - return; - case SP_ATTR_SODIPODI_ARG1: - if (value) { - star->arg[0] = g_ascii_strtod (value, NULL); - } else { - star->arg[0] = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_SODIPODI_ARG2: - if (value) { - star->arg[1] = g_ascii_strtod (value, NULL); - } else { - star->arg[1] = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_INKSCAPE_FLATSIDED: - if (value && !strcmp (value, "true")) - star->flatsided = true; - else star->flatsided = false; - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_INKSCAPE_ROUNDED: - if (value) { - star->rounded = g_ascii_strtod (value, NULL); - } else { - star->rounded = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - case SP_ATTR_INKSCAPE_RANDOMIZED: - if (value) { - star->randomized = g_ascii_strtod (value, NULL); - } else { - star->randomized = 0.0; - } - object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - break; - default: - if (((SPObjectClass *) parent_class)->set) - ((SPObjectClass *) parent_class)->set (object, key, value); - break; - } + SVGLength::Unit unit; + + SPStar *star = SP_STAR (object); + + /* fixme: we should really collect updates */ + switch (key) { + case SP_ATTR_SODIPODI_SIDES: + if (value) { + star->sides = atoi (value); + star->sides = NR_CLAMP(star->sides, 3, 1024); + } else { + star->sides = 5; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_CX: + if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->center[Geom::X]) || + (unit == SVGLength::EM) || + (unit == SVGLength::EX) || + (unit == SVGLength::PERCENT)) { + star->center[Geom::X] = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_CY: + if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->center[Geom::Y]) || + (unit == SVGLength::EM) || + (unit == SVGLength::EX) || + (unit == SVGLength::PERCENT)) { + star->center[Geom::Y] = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_R1: + if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->r[0]) || + (unit == SVGLength::EM) || + (unit == SVGLength::EX) || + (unit == SVGLength::PERCENT)) { + star->r[0] = 1.0; + } + /* fixme: Need CLAMP (Lauris) */ + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_R2: + if (!sp_svg_length_read_ldd (value, &unit, NULL, &star->r[1]) || + (unit == SVGLength::EM) || + (unit == SVGLength::EX) || + (unit == SVGLength::PERCENT)) { + star->r[1] = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + return; + case SP_ATTR_SODIPODI_ARG1: + if (value) { + star->arg[0] = g_ascii_strtod (value, NULL); + } else { + star->arg[0] = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_SODIPODI_ARG2: + if (value) { + star->arg[1] = g_ascii_strtod (value, NULL); + } else { + star->arg[1] = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_FLATSIDED: + if (value && !strcmp (value, "true")) + star->flatsided = true; + else star->flatsided = false; + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_ROUNDED: + if (value) { + star->rounded = g_ascii_strtod (value, NULL); + } else { + star->rounded = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_RANDOMIZED: + if (value) { + star->randomized = g_ascii_strtod (value, NULL); + } else { + star->randomized = 0.0; + } + object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + default: + if (((SPObjectClass *) parent_class)->set) + ((SPObjectClass *) parent_class)->set (object, key, value); + break; + } } static void sp_star_update (SPObject *object, SPCtx *ctx, guint flags) { - if (flags & (SP_OBJECT_MODIFIED_FLAG | - SP_OBJECT_STYLE_MODIFIED_FLAG | - SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { - sp_shape_set_shape ((SPShape *) object); - } - - if (((SPObjectClass *) parent_class)->update) - ((SPObjectClass *) parent_class)->update (object, ctx, flags); + if (flags & (SP_OBJECT_MODIFIED_FLAG | + SP_OBJECT_STYLE_MODIFIED_FLAG | + SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { + sp_shape_set_shape ((SPShape *) object); + } + + if (((SPObjectClass *) parent_class)->update) + ((SPObjectClass *) parent_class)->update (object, ctx, flags); } static void @@ -306,13 +306,13 @@ sp_star_description (SPItem *item) // make calls to ngettext because the pluralization may be different // for various numbers >=3. The singular form is used as the index. if (star->flatsided == false ) - return g_strdup_printf (ngettext("<b>Star</b> with %d vertex", - "<b>Star</b> with %d vertices", - star->sides), star->sides); + return g_strdup_printf (ngettext("<b>Star</b> with %d vertex", + "<b>Star</b> with %d vertices", + star->sides), star->sides); else return g_strdup_printf (ngettext("<b>Polygon</b> with %d vertex", - "<b>Polygon</b> with %d vertices", - star->sides), star->sides); + "<b>Polygon</b> with %d vertices", + star->sides), star->sides); } /** @@ -321,7 +321,7 @@ Returns a unit-length vector at 90 degrees to the direction from o to n static Geom::Point rot90_rel (Geom::Point o, Geom::Point n) { - return ((1/Geom::L2(n - o)) * Geom::Point ((n - o)[Geom::Y], (o - n)[Geom::X])); + return ((1/Geom::L2(n - o)) * Geom::Point ((n - o)[Geom::Y], (o - n)[Geom::X])); } /** @@ -333,12 +333,12 @@ Obvious (but acceptable for my purposes) limits to uniqueness: static guint32 point_unique_int (Geom::Point o) { - return ((guint32) - 65536 * - (((int) floor (o[Geom::X] * 64)) % 1024 + ((int) floor (o[Geom::X] * 1024)) % 64) - + - (((int) floor (o[Geom::Y] * 64)) % 1024 + ((int) floor (o[Geom::Y] * 1024)) % 64) - ); + return ((guint32) + 65536 * + (((int) floor (o[Geom::X] * 64)) % 1024 + ((int) floor (o[Geom::X] * 1024)) % 64) + + + (((int) floor (o[Geom::Y] * 64)) % 1024 + ((int) floor (o[Geom::Y] * 1024)) % 64) + ); } /** @@ -349,7 +349,7 @@ i.e. it is guaranteed to go through all integers < 2^32 (see http://random.mat.s static inline guint32 lcg_next(guint32 const prev) { - return (guint32) ( 69069 * prev + 1 ); + return (guint32) ( 69069 * prev + 1 ); } /** @@ -357,68 +357,68 @@ Returns a random number in the range [-0.5, 0.5) from the given seed, stepping t */ static double rnd (guint32 const seed, unsigned steps) { - guint32 lcg = seed; - for (; steps > 0; steps --) - lcg = lcg_next (lcg); + guint32 lcg = seed; + for (; steps > 0; steps --) + lcg = lcg_next (lcg); - return ( lcg / 4294967296. ) - 0.5; + return ( lcg / 4294967296. ) - 0.5; } static Geom::Point sp_star_get_curvepoint (SPStar *star, SPStarPoint point, gint index, bool previ) { - // the point whose neighboring curve handle we're calculating - Geom::Point o = sp_star_get_xy (star, point, index); - - // indices of previous and next points - gint pi = (index > 0)? (index - 1) : (star->sides - 1); - gint ni = (index < star->sides - 1)? (index + 1) : 0; - - // the other point type - SPStarPoint other = (point == SP_STAR_POINT_KNOT2? SP_STAR_POINT_KNOT1 : SP_STAR_POINT_KNOT2); - - // the neighbors of o; depending on flatsided, they're either the same type (polygon) or the other type (star) - Geom::Point prev = (star->flatsided? sp_star_get_xy (star, point, pi) : sp_star_get_xy (star, other, point == SP_STAR_POINT_KNOT2? index : pi)); - Geom::Point next = (star->flatsided? sp_star_get_xy (star, point, ni) : sp_star_get_xy (star, other, point == SP_STAR_POINT_KNOT1? index : ni)); - - // prev-next midpoint - Geom::Point mid = 0.5 * (prev + next); - - // point to which we direct the bissector of the curve handles; - // it's far enough outside the star on the perpendicular to prev-next through mid - Geom::Point biss = mid + 100000 * rot90_rel (mid, next); - - // lengths of vectors to prev and next - gdouble prev_len = Geom::L2 (prev - o); - gdouble next_len = Geom::L2 (next - o); - - // unit-length vector perpendicular to o-biss - Geom::Point rot = rot90_rel (o, biss); - - // multiply rot by star->rounded coefficient and the distance to the star point; flip for next - Geom::Point ret; - if (previ) { - ret = (star->rounded * prev_len) * rot; - } else { - ret = (star->rounded * next_len * -1) * rot; - } - - if (star->randomized == 0) { - // add the vector to o to get the final curvepoint - return o + ret; - } else { - // the seed corresponding to the exact point - guint32 seed = point_unique_int (o); - - // randomly rotate (by step 3 from the seed) and scale (by step 4) the vector - ret = ret * Geom::Matrix (Geom::Rotate (star->randomized * M_PI * rnd (seed, 3))); - ret *= ( 1 + star->randomized * rnd (seed, 4)); - - // the randomized corner point - Geom::Point o_randomized = sp_star_get_xy (star, point, index, true); - - return o_randomized + ret; - } + // the point whose neighboring curve handle we're calculating + Geom::Point o = sp_star_get_xy (star, point, index); + + // indices of previous and next points + gint pi = (index > 0)? (index - 1) : (star->sides - 1); + gint ni = (index < star->sides - 1)? (index + 1) : 0; + + // the other point type + SPStarPoint other = (point == SP_STAR_POINT_KNOT2? SP_STAR_POINT_KNOT1 : SP_STAR_POINT_KNOT2); + + // the neighbors of o; depending on flatsided, they're either the same type (polygon) or the other type (star) + Geom::Point prev = (star->flatsided? sp_star_get_xy (star, point, pi) : sp_star_get_xy (star, other, point == SP_STAR_POINT_KNOT2? index : pi)); + Geom::Point next = (star->flatsided? sp_star_get_xy (star, point, ni) : sp_star_get_xy (star, other, point == SP_STAR_POINT_KNOT1? index : ni)); + + // prev-next midpoint + Geom::Point mid = 0.5 * (prev + next); + + // point to which we direct the bissector of the curve handles; + // it's far enough outside the star on the perpendicular to prev-next through mid + Geom::Point biss = mid + 100000 * rot90_rel (mid, next); + + // lengths of vectors to prev and next + gdouble prev_len = Geom::L2 (prev - o); + gdouble next_len = Geom::L2 (next - o); + + // unit-length vector perpendicular to o-biss + Geom::Point rot = rot90_rel (o, biss); + + // multiply rot by star->rounded coefficient and the distance to the star point; flip for next + Geom::Point ret; + if (previ) { + ret = (star->rounded * prev_len) * rot; + } else { + ret = (star->rounded * next_len * -1) * rot; + } + + if (star->randomized == 0) { + // add the vector to o to get the final curvepoint + return o + ret; + } else { + // the seed corresponding to the exact point + guint32 seed = point_unique_int (o); + + // randomly rotate (by step 3 from the seed) and scale (by step 4) the vector + ret = ret * Geom::Matrix (Geom::Rotate (star->randomized * M_PI * rnd (seed, 3))); + ret *= ( 1 + star->randomized * rnd (seed, 4)); + + // the randomized corner point + Geom::Point o_randomized = sp_star_get_xy (star, point, index, true); + + return o_randomized + ret; + } } @@ -428,7 +428,7 @@ sp_star_get_curvepoint (SPStar *star, SPStarPoint point, gint index, bool previ) static void sp_star_set_shape (SPShape *shape) { - SPStar *star = SP_STAR (shape); + SPStar *star = SP_STAR (shape); // perhaps we should convert all our shapes into LPEs without source path // and with knotholders for parameters, then this situation will be handled automatically @@ -445,67 +445,65 @@ sp_star_set_shape (SPShape *shape) return; } - SPCurve *c = new SPCurve (); - - gint sides = star->sides; - bool not_rounded = (fabs (star->rounded) < 1e-4); - - // note that we pass randomized=true to sp_star_get_xy, because the curve must be randomized; - // other places that call that function (e.g. the knotholder) need the exact point - - // draw 1st segment - c->moveto(sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); - if (star->flatsided == false) { - if (not_rounded) { - c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT2, 0, true)); - } else { - c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, 0, NEXT), - sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, 0, PREV), - sp_star_get_xy (star, SP_STAR_POINT_KNOT2, 0, true)); - } - } - - // draw all middle segments - for (gint i = 1; i < sides; i++) { - if (not_rounded) { - c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT1, i, true)); - } else { - if (star->flatsided == false) { - c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, i - 1, NEXT), - sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i, PREV), - sp_star_get_xy (star, SP_STAR_POINT_KNOT1, i, true)); - } else { - c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i - 1, NEXT), - sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i, PREV), - sp_star_get_xy (star, SP_STAR_POINT_KNOT1, i, true)); - } - } - if (star->flatsided == false) { - - if (not_rounded) { + SPCurve *c = new SPCurve (); + + gint sides = star->sides; + bool not_rounded = (fabs (star->rounded) < 1e-4); + + // note that we pass randomized=true to sp_star_get_xy, because the curve must be randomized; + // other places that call that function (e.g. the knotholder) need the exact point + + // draw 1st segment + c->moveto(sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); + if (star->flatsided == false) { + if (not_rounded) { + c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT2, 0, true)); + } else { + c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, 0, NEXT), + sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, 0, PREV), + sp_star_get_xy (star, SP_STAR_POINT_KNOT2, 0, true)); + } + } + + // draw all middle segments + for (gint i = 1; i < sides; i++) { + if (not_rounded) { + c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT1, i, true)); + } else { + if (star->flatsided == false) { + c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, i - 1, NEXT), + sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i, PREV), + sp_star_get_xy (star, SP_STAR_POINT_KNOT1, i, true)); + } else { + c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i - 1, NEXT), + sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i, PREV), + sp_star_get_xy (star, SP_STAR_POINT_KNOT1, i, true)); + } + } + if (star->flatsided == false) { + + if (not_rounded) { c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT2, i, true)); - } else { - c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i, NEXT), - sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, i, PREV), - sp_star_get_xy (star, SP_STAR_POINT_KNOT2, i, true)); - } - } - } - - // draw last segment - if (not_rounded) { - c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); - } else { - if (star->flatsided == false) { - c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, sides - 1, NEXT), - sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, 0, PREV), - sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); - } else { - c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, sides - 1, NEXT), - sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, 0, PREV), - sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); - } - } + } else { + c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, i, NEXT), + sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, i, PREV), + sp_star_get_xy (star, SP_STAR_POINT_KNOT2, i, true)); + } + } + } + + // draw last segment + if (!not_rounded) { + if (star->flatsided == false) { + c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT2, sides - 1, NEXT), + sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, 0, PREV), + sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); + } else { + c->curveto(sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, sides - 1, NEXT), + sp_star_get_curvepoint (star, SP_STAR_POINT_KNOT1, 0, PREV), + sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); + } + } c->closepath(); @@ -526,46 +524,45 @@ sp_star_set_shape (SPShape *shape) void sp_star_position_set (SPStar *star, gint sides, Geom::Point center, gdouble r1, gdouble r2, gdouble arg1, gdouble arg2, bool isflat, double rounded, double randomized) { - g_return_if_fail (star != NULL); - g_return_if_fail (SP_IS_STAR (star)); - - star->sides = NR_CLAMP(sides, 3, 1024); - star->center = center; - star->r[0] = MAX (r1, 0.001); - if (isflat == false) { - star->r[1] = NR_CLAMP(r2, 0.0, star->r[0]); - } else { - star->r[1] = NR_CLAMP( r1*cos(M_PI/sides) ,0.0, star->r[0] ); - } - star->arg[0] = arg1; - star->arg[1] = arg2; - star->flatsided = isflat; - star->rounded = rounded; - star->randomized = randomized; - SP_OBJECT(star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + g_return_if_fail (star != NULL); + g_return_if_fail (SP_IS_STAR (star)); + + star->sides = NR_CLAMP(sides, 3, 1024); + star->center = center; + star->r[0] = MAX (r1, 0.001); + if (isflat == false) { + star->r[1] = NR_CLAMP(r2, 0.0, star->r[0]); + } else { + star->r[1] = NR_CLAMP( r1*cos(M_PI/sides) ,0.0, star->r[0] ); + } + star->arg[0] = arg1; + star->arg[1] = arg2; + star->flatsided = isflat; + star->rounded = rounded; + star->randomized = randomized; + SP_OBJECT(star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -static void sp_star_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +static void sp_star_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { - // We will determine the star's midpoint ourselves, instead of trusting on the base class - // Therefore setSnapObjectMidpoints() is set to false temporarily - Inkscape::SnapPreferences local_snapprefs = *snapprefs; - local_snapprefs.setSnapObjectMidpoints(false); - - if (((SPItemClass *) parent_class)->snappoints) { - ((SPItemClass *) parent_class)->snappoints (item, target, p, &local_snapprefs); - } - - // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes - if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { - return; - } - - if (snapprefs->getSnapObjectMidpoints()) { - Geom::Matrix const i2d (sp_item_i2d_affine (item)); - int type = target ? int(Inkscape::SNAPTARGET_OBJECT_MIDPOINT) : int(Inkscape::SNAPSOURCE_OBJECT_MIDPOINT); - p.push_back(std::make_pair(SP_STAR(item)->center * i2d, type)); - } + // We will determine the star's midpoint ourselves, instead of trusting on the base class + // Therefore setSnapObjectMidpoints() is set to false temporarily + Inkscape::SnapPreferences local_snapprefs = *snapprefs; + local_snapprefs.setSnapObjectMidpoints(false); + + if (((SPItemClass *) parent_class)->snappoints) { + ((SPItemClass *) parent_class)->snappoints (item, p, &local_snapprefs); + } + + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + + if (snapprefs->getSnapObjectMidpoints()) { + Geom::Matrix const i2d (sp_item_i2d_affine (item)); + p.push_back(Inkscape::SnapCandidatePoint(SP_STAR(item)->center * i2d,Inkscape::SNAPSOURCE_OBJECT_MIDPOINT, Inkscape::SNAPTARGET_OBJECT_MIDPOINT)); + } } /** @@ -582,26 +579,26 @@ static void sp_star_snappoints(SPItem const *item, bool const target, SnapPoints Geom::Point sp_star_get_xy (SPStar *star, SPStarPoint point, gint index, bool randomized) { - gdouble darg = 2.0 * M_PI / (double) star->sides; - - double arg = star->arg[point]; - arg += index * darg; - - Geom::Point xy = star->r[point] * Geom::Point(cos(arg), sin(arg)) + star->center; - - if (!randomized || star->randomized == 0) { - // return the exact point - return xy; - } else { // randomize the point - // find out the seed, unique for this point so that randomization is the same so long as the original point is stationary - guint32 seed = point_unique_int (xy); - // the full range (corresponding to star->randomized == 1.0) is equal to the star's diameter - double range = 2 * MAX (star->r[0], star->r[1]); - // find out the random displacement; x is controlled by step 1 from the seed, y by the step 2 - Geom::Point shift (star->randomized * range * rnd (seed, 1), star->randomized * range * rnd (seed, 2)); - // add the shift to the exact point - return xy + shift; - } + gdouble darg = 2.0 * M_PI / (double) star->sides; + + double arg = star->arg[point]; + arg += index * darg; + + Geom::Point xy = star->r[point] * Geom::Point(cos(arg), sin(arg)) + star->center; + + if (!randomized || star->randomized == 0) { + // return the exact point + return xy; + } else { // randomize the point + // find out the seed, unique for this point so that randomization is the same so long as the original point is stationary + guint32 seed = point_unique_int (xy); + // the full range (corresponding to star->randomized == 1.0) is equal to the star's diameter + double range = 2 * MAX (star->r[0], star->r[1]); + // find out the random displacement; x is controlled by step 1 from the seed, y by the step 2 + Geom::Point shift (star->randomized * range * rnd (seed, 1), star->randomized * range * rnd (seed, 2)); + // add the shift to the exact point + return xy + shift; + } } /* diff --git a/src/sp-stop-fns.h b/src/sp-stop-fns.h deleted file mode 100644 index 9903359e9..000000000 --- a/src/sp-stop-fns.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SEEN_SP_STOP_FNS_H -#define SEEN_SP_STOP_FNS_H - -#include <glib-object.h> -struct SPStop; -struct SPStopClass; - -#define SP_TYPE_STOP (sp_stop_get_type()) -#define SP_STOP(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_STOP, SPStop)) -#define SP_STOP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SP_TYPE_STOP, SPStopClass)) -#define SP_IS_STOP(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_STOP)) -#define SP_IS_STOP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), SP_TYPE_STOP)) - -GType sp_stop_get_type(); - - -#endif /* !SEEN_SP_STOP_FNS_H */ diff --git a/src/sp-stop.cpp b/src/sp-stop.cpp new file mode 100644 index 000000000..740cfef78 --- /dev/null +++ b/src/sp-stop.cpp @@ -0,0 +1,66 @@ +/** @file + * @gradient stop class. + */ +/* Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak + * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 1999,2005 authors + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include "sp-stop.h" + + +// A stop might have some non-stop siblings +SPStop* SPStop::getNextStop() +{ + SPStop *result = 0; + + for (SPObject* obj = getNext(); obj && !result; obj = obj->getNext()) { + if (SP_IS_STOP(obj)) { + result = SP_STOP(obj); + } + } + + return result; +} + +SPStop* SPStop::getPrevStop() +{ + SPStop *result = 0; + + for (SPObject* obj = getPrev(); obj; obj = obj->getPrev()) { + // The closest previous SPObject that is an SPStop *should* be ourself. + if (SP_IS_STOP(obj)) { + SPStop* stop = SP_STOP(obj); + // Sanity check to ensure we have a proper sibling structure. + if (stop->getNextStop() == this) { + result = stop; + } else { + g_warning("SPStop previous/next relationship broken"); + } + break; + } + } + + return result; +} + + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/sp-stop.h b/src/sp-stop.h index 3203d8f74..bf6893db1 100644 --- a/src/sp-stop.h +++ b/src/sp-stop.h @@ -9,14 +9,23 @@ */ #include <glib/gtypes.h> -//#include <glib-object.h> #include "sp-object.h" #include "color.h" -#include "sp-stop-fns.h" class SPObjectClass; class SPColor; +struct SPStop; +struct SPStopClass; + +#define SP_TYPE_STOP (sp_stop_get_type()) +#define SP_STOP(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_STOP, SPStop)) +#define SP_STOP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SP_TYPE_STOP, SPStopClass)) +#define SP_IS_STOP(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_STOP)) +#define SP_IS_STOP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), SP_TYPE_STOP)) + +GType sp_stop_get_type(); + /** Gradient stop. */ struct SPStop : public SPObject { /// \todo fixme: Should be SPSVGPercentage @@ -32,6 +41,10 @@ struct SPStop : public SPObject { /// \todo fixme: Implement SPSVGNumber or something similar. gfloat opacity; + + + SPStop* getNextStop(); + SPStop* getPrevStop(); }; /// The SPStop vtable. diff --git a/src/sp-text.cpp b/src/sp-text.cpp index b45f8cbb6..11665b890 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -74,7 +74,7 @@ static void sp_text_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &t static NRArenaItem *sp_text_show (SPItem *item, NRArena *arena, unsigned key, unsigned flags); static void sp_text_hide (SPItem *item, unsigned key); static char *sp_text_description (SPItem *item); -static void sp_text_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_text_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static Geom::Matrix sp_text_set_transform(SPItem *item, Geom::Matrix const &xform); static void sp_text_print (SPItem *item, SPPrintContext *gpc); @@ -421,7 +421,7 @@ sp_text_description(SPItem *item) GString *xs = SP_PX_TO_METRIC_STRING(style->font_size.computed, sp_desktop_namedview(SP_ACTIVE_DESKTOP)->getDefaultMetric()); - char *trunc = ""; + char const *trunc = ""; Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) item); if (layout && layout->inputTruncated()) { trunc = _(" [truncated]"); @@ -434,7 +434,7 @@ sp_text_description(SPItem *item) return ret; } -static void sp_text_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_text_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) { // Choose a point on the baseline for snapping from or to, with the horizontal position // of this point depending on the text alignment (left vs. right) @@ -442,8 +442,7 @@ static void sp_text_snappoints(SPItem const *item, bool const target, SnapPoints if (layout != NULL && layout->outputExists()) { boost::optional<Geom::Point> pt = layout->baselineAnchorPoint(); if (pt) { - int type = target ? int(Inkscape::SNAPTARGET_TEXT_BASELINE) : int(Inkscape::SNAPSOURCE_TEXT_BASELINE); - p.push_back(std::make_pair((*pt) * sp_item_i2d_affine(item), type)); + p.push_back(Inkscape::SnapCandidatePoint((*pt) * sp_item_i2d_affine(item), Inkscape::SNAPSOURCE_TEXT_BASELINE, Inkscape::SNAPTARGET_TEXT_BASELINE)); } } } diff --git a/src/sp-use.cpp b/src/sp-use.cpp index 76930086c..7962390c2 100644 --- a/src/sp-use.cpp +++ b/src/sp-use.cpp @@ -52,7 +52,7 @@ static void sp_use_update(SPObject *object, SPCtx *ctx, guint flags); static void sp_use_modified(SPObject *object, guint flags); static void sp_use_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags); -static void sp_use_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs); +static void sp_use_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs); static void sp_use_print(SPItem *item, SPPrintContext *ctx); static gchar *sp_use_description(SPItem *item); static NRArenaItem *sp_use_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags); @@ -742,7 +742,7 @@ sp_use_get_original(SPUse *use) } static void -sp_use_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs) +sp_use_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) { g_assert (item != NULL); g_assert (SP_IS_ITEM(item)); @@ -755,7 +755,7 @@ sp_use_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, SPItemClass const &item_class = *(SPItemClass const *) G_OBJECT_GET_CLASS(root); if (item_class.snappoints) { - item_class.snappoints(root, target, p, snapprefs); + item_class.snappoints(root, p, snapprefs); } } diff --git a/src/spiral-context.cpp b/src/spiral-context.cpp index 3825f74c7..7ce9d4710 100644 --- a/src/spiral-context.cpp +++ b/src/spiral-context.cpp @@ -241,9 +241,7 @@ sp_spiral_context_root_handler(SPEventContext *event_context, GdkEvent *event) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); - Geom::Point pt2g = to_2geom(sc->center); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); - sc->center = from_2geom(pt2g); + m.freeSnapReturnByRef(sc->center, Inkscape::SNAPSOURCE_NODE_HANDLE); sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), ( GDK_KEY_PRESS_MASK | @@ -273,12 +271,19 @@ sp_spiral_context_root_handler(SPEventContext *event_context, GdkEvent *event) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, sc->item); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, motion_dt, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE); sp_spiral_drag(sc, from_2geom(motion_dt), event->motion.state); gobble_motion_events(GDK_BUTTON1_MASK); ret = TRUE; + } else if (!sp_event_context_knot_mouseover(sc)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case GDK_BUTTON_RELEASE: @@ -424,7 +429,7 @@ sp_spiral_drag(SPSpiralContext *sc, Geom::Point p, guint state) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, sc->item); Geom::Point pt2g = to_2geom(p); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(pt2g, Inkscape::SNAPSOURCE_NODE_HANDLE); Geom::Point const p0 = desktop->dt2doc(sc->center); Geom::Point const p1 = desktop->dt2doc(pt2g); diff --git a/src/spray-context.cpp b/src/spray-context.cpp index b68fe0818..2bdac197f 100644 --- a/src/spray-context.cpp +++ b/src/spray-context.cpp @@ -11,6 +11,7 @@ * Benoît LAVORATA * Vincent MONTAGNE * Pierre BARBRY-BLOT + * Steren GIANNINI (steren.giannini@gmail.com) * * Copyright (C) 2009 authors * @@ -39,14 +40,6 @@ #include "desktop-style.h" #include "message-context.h" #include "pixmaps/cursor-spray.xpm" -//#include "pixmaps/cursor-spray-move.xpm" -//#include "pixmaps/cursor-thin.xpm" -//#include "pixmaps/cursor-thicken.xpm" -//#include "pixmaps/cursor-attract.xpm" -//#include "pixmaps/cursor-repel.xpm" -//#include "pixmaps/cursor-push.xpm" -//#include "pixmaps/cursor-roughen.xpm" -//#include "pixmaps/cursor-color.xpm" #include <boost/optional.hpp> #include "libnr/nr-matrix-ops.h" #include "libnr/nr-scale-translate-ops.h" @@ -63,7 +56,6 @@ #include "path-chemistry.h" #include "sp-gradient.h" #include "sp-stop.h" -#include "sp-stop-fns.h" #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" @@ -107,34 +99,15 @@ static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event); static SPEventContextClass *parent_class = 0; -/* - RAND is a macro which returns a pseudo-random numbers from a uniform - distribution on the interval [0 1] -*/ -#define RAND ((double) rand())/((double) RAND_MAX) - -/* - TWOPI = 2.0*pi -*/ -#define TWOPI 2.0*3.141592653589793238462643383279502884197169399375 - -/* - RANDN is a macro which returns a pseudo-random numbers from a normal - distribution with mean zero and standard deviation one. This macro uses Box - Muller's algorithm -*/ -#define RANDN sqrt(-2.0*log(RAND))*cos(TWOPI*RAND) - - -double NormalDistribution(double mu,double sigma) +/** + * This function returns pseudo-random numbers from a normal distribution + * @param mu : mean + * @param sigma : standard deviation ( > 0 ) + */ +inline double NormalDistribution(double mu,double sigma) { -/* - This function returns a pseudo-random numbers from a normal distribution with - mean equal at mu and standard deviation equal at sigma > 0 -*/ - - return (mu+sigma*RANDN); - + // use Box Muller's algorithm + return mu + sigma * sqrt( -2.0 * log(g_random_double_range(0, 1)) ) * cos( 2.0*M_PI*g_random_double_range(0, 1) ); } @@ -261,14 +234,14 @@ void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/) SPEventContext *event_context = SP_EVENT_CONTEXT(tc); SPDesktop *desktop = event_context->desktop; - guint num = 0; - gchar *sel_message = NULL; - if (!desktop->selection->isEmpty()) { - num = g_slist_length((GSList *) desktop->selection->itemList()); - sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num); - } else { - sel_message = g_strdup_printf(_("<b>Nothing</b> selected")); - } + guint num = 0; + gchar *sel_message = NULL; + if (!desktop->selection->isEmpty()) { + num = g_slist_length((GSList *) desktop->selection->itemList()); + sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num); + } else { + sel_message = g_strdup_printf(_("<b>Nothing</b> selected")); + } switch (tc->mode) { @@ -439,25 +412,32 @@ double get_move_standard_deviation(SPSprayContext *tc) return tc->standard_deviation; } -/** Method to handle the distribution of the items */ -void random_position( double &r, double &p, double &a, double &s, int choice) +/** + * Method to handle the distribution of the items + * @param[out] radius : radius of the position of the sprayed object + * @param[out] angle : angle of the position of the sprayed object + * @param[in] a : mean + * @param[in] s : standard deviation + * @param[in] choice : + + */ +void random_position( double &radius, double &angle, double &a, double &s, int /*choice*/) { - if (choice == 0) // 1 : uniform repartition - { - r = (1-pow(g_random_double_range(0, 1),2)); - p = g_random_double_range(0, M_PI*2); - } - if (choice == 1) // 0 : gaussian repartition + // angle is taken from an uniform distribution + angle = g_random_double_range(0, M_PI*2.0); + + // radius is taken from a Normal Distribution + double radius_temp =-1; + while(!((radius_temp>=0)&&(radius_temp<=1))) { - double r_temp =-1; - while(!((r_temp>=0)&&(r_temp<=1))) - { - r_temp = NormalDistribution(a,s/4); - } - // generates a number following a normal distribution - p = g_random_double_range(0, M_PI*2); - r=r_temp; + radius_temp = NormalDistribution( a, s ); } + // Because we are in polar coordinates, a special treatment has to be done to the radius. + // Otherwise, positions taken from an uniform repartition on radius and angle will not seam to + // be uniformily distributed on the disk (more at the center and less at the boundary). + // We counter this effect with a 0.5 exponent. This is empiric. + radius = pow( radius_temp, 0.5); + } @@ -495,7 +475,7 @@ bool sp_spray_recursive(SPDesktop *desktop, double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI ); double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 ); double dr; double dp; - random_position(dr,dp,mean,standard_deviation,_distrib); + random_position( dr, dp, mean, standard_deviation, _distrib ); dr=dr*radius; if (mode == SPRAY_MODE_COPY) { @@ -582,7 +562,7 @@ bool sp_spray_recursive(SPDesktop *desktop, selection->clear(); selection->add(item_copied); selection->add(unionResult); - sp_selected_path_union(selection->desktop()); + sp_selected_path_union_skip_undo(selection->desktop()); selection->add(father); Inkscape::GC::release(copy2); did = true; @@ -625,32 +605,6 @@ bool sp_spray_recursive(SPDesktop *desktop, return did; } - -bool sp_spray_color_recursive(guint /*mode*/, - SPItem */*item*/, - SPItem */*item_at_point*/, - guint32 /*fill_goal*/, - bool /*do_fill*/, - guint32 /*stroke_goal*/, - bool /*do_stroke*/, - float /*opacity_goal*/, - bool /*do_opacity*/, - bool /*do_blur*/, - bool /*reverse*/, - Geom::Point /*p*/, - double /*radius*/, - double /*force*/, - bool /*do_h*/, - bool /*do_s*/, - bool /*do_l*/, - bool /*do_o*/) -{ - bool did = false; - - return did; -} - - bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse) { Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop); @@ -730,7 +684,8 @@ void sp_spray_update_area(SPSprayContext *tc) void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift) { - SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); //sélectionne le bouton numéro "mode" + // select the button mode + SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode); // need to set explicitly, because the prefs may not have changed by the previous tc->mode = mode; sp_spray_update_cursor (tc, with_shift); diff --git a/src/star-context.cpp b/src/star-context.cpp index 3d6825e31..63a15545f 100644 --- a/src/star-context.cpp +++ b/src/star-context.cpp @@ -259,9 +259,7 @@ static gint sp_star_context_root_handler(SPEventContext *event_context, GdkEvent /* Snap center */ SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true); - Geom::Point pt2g = to_2geom(sc->center); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); - sc->center = from_2geom(pt2g); + m.freeSnapReturnByRef(sc->center, Inkscape::SNAPSOURCE_NODE_HANDLE); sp_canvas_item_grab(SP_CANVAS_ITEM(desktop->acetate), GDK_KEY_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | @@ -293,6 +291,13 @@ static gint sp_star_context_root_handler(SPEventContext *event_context, GdkEvent gobble_motion_events(GDK_BUTTON1_MASK); ret = TRUE; + } else if (!sp_event_context_knot_mouseover(event_context)) { + SnapManager &m = desktop->namedview->snap_manager; + m.setup(desktop); + + Geom::Point const motion_w(event->motion.x, event->motion.y); + Geom::Point motion_dt(desktop->w2d(motion_w)); + m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_NODE_HANDLE)); } break; case GDK_BUTTON_RELEASE: @@ -437,7 +442,7 @@ static void sp_star_drag(SPStarContext *sc, Geom::Point p, guint state) SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, sc->item); Geom::Point pt2g = to_2geom(p); - m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, pt2g, Inkscape::SNAPSOURCE_HANDLE); + m.freeSnapReturnByRef(pt2g, Inkscape::SNAPSOURCE_NODE_HANDLE); Geom::Point const p0 = desktop->dt2doc(sc->center); Geom::Point const p1 = desktop->dt2doc(pt2g); @@ -474,13 +479,17 @@ sp_star_finish (SPStarContext * sc) sc->_message_context->clear(); if (sc->item != NULL) { - SPStar *star = SP_STAR(sc->item); - if (star->r[1] == 0) { - sp_star_cancel(sc); // Don't allow the creating of zero sized arc, for example when the start and and point snap to the snap grid point - return; - } + SPStar *star = SP_STAR(sc->item); + if (star->r[1] == 0) { + sp_star_cancel(sc); // Don't allow the creating of zero sized arc, for example when the start and and point snap to the snap grid point + return; + } + + // Set transform center, so that odd stars rotate correctly + // LP #462157 + sc->item->setCenter(sc->center); - SPDesktop *desktop = SP_EVENT_CONTEXT(sc)->desktop; + SPDesktop *desktop = SP_EVENT_CONTEXT(sc)->desktop; SPObject *object = SP_OBJECT(sc->item); sp_shape_set_shape(SP_SHAPE(sc->item)); @@ -499,14 +508,14 @@ sp_star_finish (SPStarContext * sc) static void sp_star_cancel(SPStarContext *sc) { - SPDesktop *desktop = SP_EVENT_CONTEXT(sc)->desktop; + SPDesktop *desktop = SP_EVENT_CONTEXT(sc)->desktop; - sp_desktop_selection(desktop)->clear(); - sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); + sp_desktop_selection(desktop)->clear(); + sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), 0); if (sc->item != NULL) { - SP_OBJECT(sc->item)->deleteObject(); - sc->item = NULL; + SP_OBJECT(sc->item)->deleteObject(); + sc->item = NULL; } sc->within_tolerance = false; diff --git a/src/star-context.h b/src/star-context.h index 6db29ec42..024bf8d74 100644 --- a/src/star-context.h +++ b/src/star-context.h @@ -28,27 +28,27 @@ class SPStarContext; class SPStarContextClass; struct SPStarContext : public SPEventContext { - SPItem *item; - Geom::Point center; - - /* Number of corners */ - gint magnitude; - /* Outer/inner radius ratio */ - gdouble proportion; - /* flat sides or not? */ - bool isflatsided; - /* rounded corners ratio */ - gdouble rounded; - // randomization - gdouble randomized; + SPItem *item; + Geom::Point center; + + /* Number of corners */ + gint magnitude; + /* Outer/inner radius ratio */ + gdouble proportion; + /* flat sides or not? */ + bool isflatsided; + /* rounded corners ratio */ + gdouble rounded; + // randomization + gdouble randomized; sigc::connection sel_changed_connection; - Inkscape::MessageContext *_message_context; + Inkscape::MessageContext *_message_context; }; struct SPStarContextClass { - SPEventContextClass parent_class; + SPEventContextClass parent_class; }; GtkType sp_star_context_get_type (void); diff --git a/src/style.cpp b/src/style.cpp index 111018c2a..6d73e62ea 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -363,7 +363,7 @@ sp_style_filter_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) } if ( SP_IS_FILTER(ref)) { - style->filter_modified_connection = + style->filter_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_filter_ref_modified), style)); } @@ -416,7 +416,7 @@ sp_style_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle style->fill_ps_modified_connection.disconnect(); } if (SP_IS_PAINT_SERVER(ref)) { - style->fill_ps_modified_connection = + style->fill_ps_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } @@ -433,7 +433,7 @@ sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPSty style->stroke_ps_modified_connection.disconnect(); } if (SP_IS_PAINT_SERVER(ref)) { - style->stroke_ps_modified_connection = + style->stroke_ps_modified_connection = ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } @@ -724,7 +724,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) style->stroke_dashoffset_set = FALSE; } } - + /* -inkscape-font-specification */ if (!style->text_private || !style->text->font_specification.set) { val = repr->attribute("-inkscape-font-specification"); @@ -751,7 +751,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) if (!style->filter.set) { val = repr->attribute("filter"); if (val) { - sp_style_read_ifilter(val, style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL); + sp_style_read_ifilter(val, style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL); } } SPS_READ_PENUM_IF_UNSET(&style->enable_background, repr, @@ -805,21 +805,21 @@ sp_style_read_from_prefs(SPStyle *style, Glib::ustring const &path) { g_return_if_fail(style != NULL); g_return_if_fail(path != ""); - + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); // not optimal: we reconstruct the node based on the prefs, then pass it to // sp_style_read for actual processing. Inkscape::XML::SimpleDocument *tempdoc = new Inkscape::XML::SimpleDocument; Inkscape::XML::Node *tempnode = tempdoc->createElement("temp"); - + std::vector<Inkscape::Preferences::Entry> attrs = prefs->getAllEntries(path); for (std::vector<Inkscape::Preferences::Entry>::iterator i = attrs.begin(); i != attrs.end(); ++i) { tempnode->setAttribute(i->getEntryName().data(), i->getString().data()); } sp_style_read(style, NULL, tempnode); - + Inkscape::GC::release(tempnode); Inkscape::GC::release(tempdoc); delete tempdoc; @@ -1949,7 +1949,7 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare if (style->text && parent->text) { sp_style_merge_string_prop_from_dying_parent(style->text->font_specification, parent->text->font_specification); - + sp_style_merge_string_prop_from_dying_parent(style->text->font_family, parent->text->font_family); } @@ -2287,10 +2287,10 @@ sp_style_write_string(SPStyle const *const style, guint const flags) p += sp_style_write_ipaint(p, c + BMAX - p, "stroke", &style->stroke, NULL, flags); // stroke width affects markers, so write it if there's stroke OR any markers - if (!style->stroke.noneSet || + if (!style->stroke.noneSet || style->marker[SP_MARKER_LOC].set || style->marker[SP_MARKER_LOC_START].set || - style->marker[SP_MARKER_LOC_MID].set || + style->marker[SP_MARKER_LOC_MID].set || style->marker[SP_MARKER_LOC_END].set) { p += sp_style_write_ilength(p, c + BMAX - p, "stroke-width", &style->stroke_width, NULL, flags); } @@ -2349,7 +2349,7 @@ sp_style_write_string(SPStyle const *const style, guint const flags) p += g_snprintf(p, c + BMAX - p, "marker:none;"); marker_none = true; } - if (style->marker[SP_MARKER_LOC_START].set + if (style->marker[SP_MARKER_LOC_START].set && (!master || strcmp(master, style->marker[SP_MARKER_LOC_START].value))) { p += g_snprintf(p, c + BMAX - p, "marker-start:%s;", style->marker[SP_MARKER_LOC_START].value); } else if (flags == SP_STYLE_FLAG_ALWAYS && !marker_none) { @@ -2450,10 +2450,10 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to) p += sp_style_write_ipaint(p, c + BMAX - p, "stroke", &from->stroke, &to->stroke, SP_STYLE_FLAG_IFDIFF); // stroke width affects markers, so write it if there's stroke OR any markers - if (!from->stroke.noneSet || + if (!from->stroke.noneSet || from->marker[SP_MARKER_LOC].set || from->marker[SP_MARKER_LOC_START].set || - from->marker[SP_MARKER_LOC_MID].set || + from->marker[SP_MARKER_LOC_MID].set || from->marker[SP_MARKER_LOC_END].set) { p += sp_style_write_ilength(p, c + BMAX - p, "stroke-width", &from->stroke_width, &to->stroke_width, SP_STYLE_FLAG_IFDIFF); } @@ -3281,12 +3281,12 @@ sp_style_read_ifilter(gchar const *str, SPStyle * style, SPDocument *document) f->set = TRUE; f->inherit = TRUE; if (f->href && f->href->getObject()) - f->href->detach(); + f->href->detach(); } else if(streq(str, "none")) { f->set = TRUE; f->inherit = FALSE; if (f->href && f->href->getObject()) - f->href->detach(); + f->href->detach(); } else if (strneq(str, "url", 3)) { char *uri = extract_uri(str); if(uri == NULL || uri[0] == '\0') { @@ -3320,7 +3320,7 @@ sp_style_read_ifilter(gchar const *str, SPStyle * style, SPDocument *document) f->set = FALSE; f->inherit = FALSE; if (f->href && f->href->getObject()) - f->href->detach(); + f->href->detach(); } } @@ -3887,7 +3887,7 @@ sp_style_set_property_url (SPObject *item, gchar const *property, SPObject *link SPCSSAttr *css = sp_repr_css_attr_new(); if (linked) { - gchar *val = g_strdup_printf("url(#%s)", SP_OBJECT_ID(linked)); + gchar *val = g_strdup_printf("url(#%s)", linked->getId()); sp_repr_css_set_property(css, property, val); g_free(val); } else { diff --git a/src/style.h b/src/style.h index 9a2c72f16..f1b5ec534 100644 --- a/src/style.h +++ b/src/style.h @@ -1,12 +1,14 @@ -#ifndef __SP_STYLE_H__ -#define __SP_STYLE_H__ +#ifndef SEEN_SP_STYLE_H +#define SEEN_SP_STYLE_H /** \file * SPStyle - a style object for SPItem objects */ /* Authors: * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> * + * Copyright (C) 2010 Jon A. Cruz * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. * @@ -357,7 +359,7 @@ struct SPStyle { SPIEnum enable_background; /// style belongs to a cloned object - bool cloned; + bool cloned; sigc::connection release_connection; @@ -365,12 +367,17 @@ struct SPStyle { sigc::connection fill_ps_modified_connection; sigc::connection stroke_ps_modified_connection; - SPObject *getFilter() {if (filter.href) return filter.href->getObject(); else return NULL;} - const gchar *getFilterURI() {if (filter.href) return filter.href->getURI()->toString(); else return NULL;} - SPPaintServer *getFillPaintServer() {if (fill.value.href) return fill.value.href->getObject(); else return NULL;} - const gchar *getFillURI() {if (fill.value.href) return fill.value.href->getURI()->toString(); else return NULL;} - SPPaintServer *getStrokePaintServer() {if (stroke.value.href) return stroke.value.href->getObject(); else return NULL;} - const gchar *getStrokeURI() {if (stroke.value.href) return stroke.value.href->getURI()->toString(); else return NULL;} + SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : 0; } + SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : 0; } + gchar const *getFilterURI() const { return (filter.href) ? filter.href->getURI()->toString() : 0; } + + SPPaintServer *getFillPaintServer() { return (fill.value.href) ? fill.value.href->getObject() : 0; } + SPPaintServer const *getFillPaintServer() const { return (fill.value.href) ? fill.value.href->getObject() : 0; } + gchar const *getFillURI() const { return (fill.value.href) ? fill.value.href->getURI()->toString() : 0; } + + SPPaintServer *getStrokePaintServer() { return (stroke.value.href) ? stroke.value.href->getObject() : 0; } + SPPaintServer const *getStrokePaintServer() const { return (stroke.value.href) ? stroke.value.href->getObject() : 0; } + gchar const *getStrokeURI() const { return (stroke.value.href) ? stroke.value.href->getURI()->toString() : 0; } }; SPStyle *sp_style_new(SPDocument *document); @@ -541,7 +548,7 @@ struct SPTextStyle { /* Full font name, as font_factory::ConstructFontSpecification would give */ SPIString font_specification; - + /** \todo fixme: The 'font' property is ugly, and not working (lauris) */ SPIString font; }; @@ -559,7 +566,7 @@ void sp_style_set_property_url (SPObject *item, gchar const *property, SPObject gchar *attribute_unquote(gchar const *val); gchar *css2_escape_quote(gchar const *val); -#endif +#endif // SEEN_SP_STYLE_H /* diff --git a/src/svg/Makefile_insert b/src/svg/Makefile_insert index b1400052b..c27b2291c 100644 --- a/src/svg/Makefile_insert +++ b/src/svg/Makefile_insert @@ -14,7 +14,8 @@ ink_common_sources += \ svg/svg-affine.cpp \ svg/svg-color.cpp \ svg/svg-color.h \ - svg/svg-icc-color.h \ + svg/svg-device-color.h \ + svg/svg-icc-color.h \ svg/svg-length.cpp \ svg/svg-length.h \ svg/svg-path.cpp \ diff --git a/src/text-context.cpp b/src/text-context.cpp index fc28dc8e4..4f89bd1e1 100644 --- a/src/text-context.cpp +++ b/src/text-context.cpp @@ -1597,7 +1597,7 @@ sp_text_context_update_cursor(SPTextContext *tc, bool scroll_to_see) Inkscape::Text::Layout const *layout = te_get_layout(tc->text); int const nChars = layout->iteratorToCharIndex(layout->end()); - char *trunc = ""; + char const *trunc = ""; bool truncated = false; if (layout->inputTruncated()) { truncated = true; diff --git a/src/tools-switch.cpp b/src/tools-switch.cpp index 6c53ce61c..5f33453f0 100644 --- a/src/tools-switch.cpp +++ b/src/tools-switch.cpp @@ -26,7 +26,7 @@ #include <xml/repr.h> #include "select-context.h" -#include "node-context.h" +#include "ui/tool/node-tool.h" #include "tweak-context.h" #include "spray-context.h" #include "sp-path.h" @@ -126,10 +126,9 @@ tools_switch(SPDesktop *dt, int num) inkscape_eventcontext_set(sp_desktop_event_context(dt)); break; case TOOLS_NODES: - dt->set_event_context(SP_TYPE_NODE_CONTEXT, tool_names[num]); + dt->set_event_context(INK_TYPE_NODE_TOOL, tool_names[num]); dt->activate_guides(true); inkscape_eventcontext_set(sp_desktop_event_context(dt)); - dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("To edit a path, <b>click</b>, <b>Shift+click</b>, or <b>drag around</b> nodes to select them, then <b>drag</b> nodes and handles. <b>Click</b> on an object to select.")); break; case TOOLS_TWEAK: dt->set_event_context(SP_TYPE_TWEAK_CONTEXT, tool_names[num]); diff --git a/src/traits/CMakeLists.txt b/src/traits/CMakeLists.txt deleted file mode 100644 index 791f9cd85..000000000 --- a/src/traits/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -SET(traits_SRC -) diff --git a/src/traits/Makefile_insert b/src/traits/Makefile_insert deleted file mode 100644 index 4290ac2dd..000000000 --- a/src/traits/Makefile_insert +++ /dev/null @@ -1,5 +0,0 @@ - -traits/all: - -traits/clean: - diff --git a/src/traits/makefile.in b/src/traits/makefile.in deleted file mode 100644 index c0f07d432..000000000 --- a/src/traits/makefile.in +++ /dev/null @@ -1,17 +0,0 @@ -# Convenience stub makefile to call the real Makefile. - -@SET_MAKE@ - -OBJEXT = @OBJEXT@ - -# Explicit so that it's the default rule. -all: - cd .. && $(MAKE) traits/all - -clean %.a %.$(OBJEXT): - cd .. && $(MAKE) traits/$@ - -.PHONY: all clean - -.SUFFIXES: -.SUFFIXES: .a .$(OBJEXT) diff --git a/src/tweak-context.cpp b/src/tweak-context.cpp index 3f55d040b..13299b5a4 100644 --- a/src/tweak-context.cpp +++ b/src/tweak-context.cpp @@ -55,7 +55,6 @@ #include "path-chemistry.h" #include "sp-gradient.h" #include "sp-stop.h" -#include "sp-stop-fns.h" #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" #include "sp-radial-gradient.h" diff --git a/src/ui/Makefile_insert b/src/ui/Makefile_insert index 3eb6c6b13..eb8966d11 100644 --- a/src/ui/Makefile_insert +++ b/src/ui/Makefile_insert @@ -9,4 +9,6 @@ ink_common_sources += \ ui/previewable.h \ ui/previewfillable.h \ ui/previewholder.cpp \ - ui/previewholder.h + ui/previewholder.h \ + ui/uxmanager.cpp \ + ui/uxmanager.h diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index af0ec0129..dd1f981b5 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -3,9 +3,11 @@ */ /* Authors: * Krzysztof KosiÅ„ski <tweenk@o2.pl> + * Jon A. Cruz <jon@joncruz.org> * Incorporates some code from selection-chemistry.cpp, see that file for more credits. * * Copyright (C) 2008 authors + * Copyright (C) 2010 Jon A. Cruz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -69,7 +71,6 @@ #include "svg/svg.h" // for sp_svg_transform_write, used in _copySelection #include "svg/css-ostringstream.h" // used in _parseColor #include "file.h" // for file_import, used in _pasteImage -#include "preferences.h" // for used in _pasteImage #include "text-context.h" #include "text-editing.h" #include "tools-switch.h" @@ -105,14 +106,14 @@ namespace UI { */ class ClipboardManagerImpl : public ClipboardManager { public: - virtual void copy(); + virtual void copy(SPDesktop *desktop); virtual void copyPathParameter(Inkscape::LivePathEffect::PathParam *); - virtual bool paste(bool in_place); - virtual bool pasteStyle(); - virtual bool pasteSize(bool, bool, bool); - virtual bool pastePathEffect(); - virtual Glib::ustring getPathParameter(); - virtual Glib::ustring getShapeOrTextObjectId(); + virtual bool paste(SPDesktop *desktop, bool in_place); + virtual bool pasteStyle(SPDesktop *desktop); + virtual bool pasteSize(SPDesktop *desktop, bool separately, bool apply_x, bool apply_y); + virtual bool pastePathEffect(SPDesktop *desktop); + virtual Glib::ustring getPathParameter(SPDesktop* desktop); + virtual Glib::ustring getShapeOrTextObjectId(SPDesktop *desktop); virtual const gchar *getFirstObjectID(); ClipboardManagerImpl(); @@ -126,10 +127,10 @@ private: void _copyTextPath(SPTextPath *); Inkscape::XML::Node *_copyNode(Inkscape::XML::Node *, Inkscape::XML::Document *, Inkscape::XML::Node *); - void _pasteDocument(SPDocument *, bool in_place); - void _pasteDefs(SPDocument *); - bool _pasteImage(); - bool _pasteText(); + void _pasteDocument(SPDesktop *desktop, SPDocument *clipdoc, bool in_place); + void _pasteDefs(SPDesktop *desktop, SPDocument *clipdoc); + bool _pasteImage(SPDocument *doc); + bool _pasteText(SPDesktop *desktop); SPCSSAttr *_parseColor(const Glib::ustring &); void _applyPathEffect(SPItem *, gchar const *); SPDocument *_retrieveClipboard(Glib::ustring = ""); @@ -142,7 +143,7 @@ private: void _createInternalClipboard(); void _discardInternalClipboard(); Inkscape::XML::Node *_createClipNode(); - Geom::Scale _getScale(Geom::Point const &, Geom::Point const &, Geom::Rect const &, bool, bool); + Geom::Scale _getScale(SPDesktop *desktop, Geom::Point const &min, Geom::Point const &max, Geom::Rect const &obj_rect, bool apply_x, bool apply_y); Glib::ustring _getBestTarget(); void _setClipboardTargets(); void _setClipboardColor(guint32); @@ -193,10 +194,11 @@ ClipboardManagerImpl::~ClipboardManagerImpl() {} /** * @brief Copy selection contents to the clipboard */ -void ClipboardManagerImpl::copy() +void ClipboardManagerImpl::copy(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop == NULL ) return; + if ( desktop == NULL ) { + return; + } Inkscape::Selection *selection = sp_desktop_selection(desktop); // Special case for when the gradient dragger is active - copies gradient color @@ -220,7 +222,9 @@ void ClipboardManagerImpl::copy() g_snprintf(color_str, 16, "#%06x", col >> 8); sp_repr_css_set_property(_text_style, "fill", color_str); float opacity = SP_RGBA32_A_F(col); - if (opacity > 1.0) opacity = 1.0; // safeguard + if (opacity > 1.0) { + opacity = 1.0; // safeguard + } Inkscape::CSSOStringStream opcss; opcss << opacity; sp_repr_css_set_property(_text_style, "opacity", opcss.str().data()); @@ -273,9 +277,13 @@ void ClipboardManagerImpl::copy() */ void ClipboardManagerImpl::copyPathParameter(Inkscape::LivePathEffect::PathParam *pp) { - if ( pp == NULL ) return; + if ( pp == NULL ) { + return; + } gchar *svgd = sp_svg_write_path( pp->get_pathvector() ); - if ( svgd == NULL || *svgd == '\0' ) return; + if ( svgd == NULL || *svgd == '\0' ) { + return; + } _discardInternalClipboard(); _createInternalClipboard(); @@ -294,12 +302,15 @@ void ClipboardManagerImpl::copyPathParameter(Inkscape::LivePathEffect::PathParam * @brief Paste from the system clipboard into the active desktop * @param in_place Whether to put the contents where they were when copied */ -bool ClipboardManagerImpl::paste(bool in_place) +bool ClipboardManagerImpl::paste(SPDesktop *desktop, bool in_place) { // do any checking whether we really are able to paste before requesting the contents - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop == NULL ) return false; - if ( Inkscape::have_viable_layer(desktop, desktop->messageStack()) == false ) return false; + if ( desktop == NULL ) { + return false; + } + if ( Inkscape::have_viable_layer(desktop, desktop->messageStack()) == false ) { + return false; + } Glib::ustring target = _getBestTarget(); @@ -308,9 +319,13 @@ bool ClipboardManagerImpl::paste(bool in_place) // TODO: Handle x-special/gnome-copied-files and text/uri-list to support pasting files // if there is an image on the clipboard, paste it - if ( target == CLIPBOARD_GDK_PIXBUF_TARGET ) return _pasteImage(); + if ( target == CLIPBOARD_GDK_PIXBUF_TARGET ) { + return _pasteImage(desktop->doc()); + } // if there's only text, paste it into a selected text object or create a new one - if ( target == CLIPBOARD_TEXT_TARGET ) return _pasteText(); + if ( target == CLIPBOARD_TEXT_TARGET ) { + return _pasteText(desktop); + } // otherwise, use the import extensions SPDocument *tempdoc = _retrieveClipboard(target); @@ -319,7 +334,7 @@ bool ClipboardManagerImpl::paste(bool in_place) return false; } - _pasteDocument(tempdoc, in_place); + _pasteDocument(desktop, tempdoc, in_place); sp_document_unref(tempdoc); return true; @@ -338,8 +353,9 @@ const gchar *ClipboardManagerImpl::getFirstObjectID() Inkscape::XML::Node *root = sp_document_repr_root(tempdoc); - if (!root) + if (!root) { return NULL; + } Inkscape::XML::Node *ch = sp_repr_children(root); while (ch != NULL && @@ -349,8 +365,9 @@ const gchar *ClipboardManagerImpl::getFirstObjectID() strcmp(ch->name(), "svg:text") && strcmp(ch->name(), "svg:image") && strcmp(ch->name(), "svg:rect") - ) + ) { ch = ch->next(); + } if (ch) { return ch->attribute("id"); @@ -363,10 +380,11 @@ const gchar *ClipboardManagerImpl::getFirstObjectID() /** * @brief Implements the Paste Style action */ -bool ClipboardManagerImpl::pasteStyle() +bool ClipboardManagerImpl::pasteStyle(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop == NULL) return false; + if (desktop == NULL) { + return false; + } // check whether something is selected Inkscape::Selection *selection = sp_desktop_selection(desktop); @@ -394,7 +412,7 @@ bool ClipboardManagerImpl::pasteStyle() bool pasted = false; if (clipnode) { - _pasteDefs(tempdoc); + _pasteDefs(desktop, tempdoc); SPCSSAttr *style = sp_repr_css_attr(clipnode, "style"); sp_desktop_set_style(desktop, style); pasted = true; @@ -414,12 +432,15 @@ bool ClipboardManagerImpl::pasteStyle() * @param apply_x Whether to scale the width of objects / selection * @param apply_y Whether to scale the height of objects / selection */ -bool ClipboardManagerImpl::pasteSize(bool separately, bool apply_x, bool apply_y) +bool ClipboardManagerImpl::pasteSize(SPDesktop *desktop, bool separately, bool apply_x, bool apply_y) { - if(!apply_x && !apply_y) return false; // pointless parameters + if (!apply_x && !apply_y) { + return false; // pointless parameters + } - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop == NULL ) return false; + if ( desktop == NULL ) { + return false; + } Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection->isEmpty()) { _userWarn(desktop, _("Select <b>object(s)</b> to paste size to.")); @@ -447,8 +468,10 @@ bool ClipboardManagerImpl::pasteSize(bool separately, bool apply_x, bool apply_y for (GSList *i = const_cast<GSList*>(selection->itemList()) ; i ; i = i->next) { SPItem *item = SP_ITEM(i->data); Geom::OptRect obj_size = sp_item_bbox_desktop(item); - if ( !obj_size ) continue; - sp_item_scale_rel(item, _getScale(min, max, *obj_size, apply_x, apply_y)); + if ( !obj_size ) { + continue; + } + sp_item_scale_rel(item, _getScale(desktop, min, max, *obj_size, apply_x, apply_y)); } } // resize the selection as a whole @@ -456,7 +479,7 @@ bool ClipboardManagerImpl::pasteSize(bool separately, bool apply_x, bool apply_y Geom::OptRect sel_size = selection->bounds(); if ( sel_size ) { sp_selection_scale_relative(selection, sel_size->midpoint(), - _getScale(min, max, *sel_size, apply_x, apply_y)); + _getScale(desktop, min, max, *sel_size, apply_x, apply_y)); } } pasted = true; @@ -469,14 +492,14 @@ bool ClipboardManagerImpl::pasteSize(bool separately, bool apply_x, bool apply_y /** * @brief Applies a path effect from the clipboard to the selected path */ -bool ClipboardManagerImpl::pastePathEffect() +bool ClipboardManagerImpl::pastePathEffect(SPDesktop *desktop) { /** @todo FIXME: pastePathEffect crashes when moving the path with the applied effect, segfaulting in fork_private_if_necessary(). */ - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop == NULL ) + if ( desktop == NULL ) { return false; + } Inkscape::Selection *selection = sp_desktop_selection(desktop); if (selection && selection->isEmpty()) { @@ -489,13 +512,14 @@ bool ClipboardManagerImpl::pastePathEffect() Inkscape::XML::Node *root = sp_document_repr_root(tempdoc); Inkscape::XML::Node *clipnode = sp_repr_lookup_name(root, "inkscape:clipboard", 1); if ( clipnode ) { - gchar const *effect = clipnode->attribute("inkscape:path-effect"); - if ( effect ) { - _pasteDefs(tempdoc); + gchar const *effectstack = clipnode->attribute("inkscape:path-effect"); + if ( effectstack ) { + _pasteDefs(desktop, tempdoc); // make sure all selected items are converted to paths first (i.e. rectangles) - sp_selected_path_to_curves(desktop, false); - for (GSList *item = const_cast<GSList *>(selection->itemList()) ; item ; item = item->next) { - _applyPathEffect(reinterpret_cast<SPItem*>(item->data), effect); + sp_selected_to_lpeitems(desktop); + for (GSList *itemptr = const_cast<GSList *>(selection->itemList()) ; itemptr ; itemptr = itemptr->next) { + SPItem *item = reinterpret_cast<SPItem*>(itemptr->data); + _applyPathEffect(item, effectstack); } return true; @@ -513,18 +537,18 @@ bool ClipboardManagerImpl::pastePathEffect() * @brief Get LPE path data from the clipboard * @return The retrieved path data (contents of the d attribute), or "" if no path was found */ -Glib::ustring ClipboardManagerImpl::getPathParameter() +Glib::ustring ClipboardManagerImpl::getPathParameter(SPDesktop* desktop) { SPDocument *tempdoc = _retrieveClipboard(); // any target will do here if ( tempdoc == NULL ) { - _userWarn(SP_ACTIVE_DESKTOP, _("Nothing on the clipboard.")); + _userWarn(desktop, _("Nothing on the clipboard.")); return ""; } Inkscape::XML::Node *root = sp_document_repr_root(tempdoc), *path = sp_repr_lookup_name(root, "svg:path", -1); // unlimited search depth if ( path == NULL ) { - _userWarn(SP_ACTIVE_DESKTOP, _("Clipboard does not contain a path.")); + _userWarn(desktop, _("Clipboard does not contain a path.")); sp_document_unref(tempdoc); return ""; } @@ -537,21 +561,22 @@ Glib::ustring ClipboardManagerImpl::getPathParameter() * @brief Get object id of a shape or text item from the clipboard * @return The retrieved id string (contents of the id attribute), or "" if no shape or text item was found */ -Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId() +Glib::ustring ClipboardManagerImpl::getShapeOrTextObjectId(SPDesktop *desktop) { SPDocument *tempdoc = _retrieveClipboard(); // any target will do here if ( tempdoc == NULL ) { - _userWarn(SP_ACTIVE_DESKTOP, _("Nothing on the clipboard.")); + _userWarn(desktop, _("Nothing on the clipboard.")); return ""; } Inkscape::XML::Node *root = sp_document_repr_root(tempdoc); Inkscape::XML::Node *repr = sp_repr_lookup_name(root, "svg:path", -1); // unlimited search depth - if ( repr == NULL ) + if ( repr == NULL ) { repr = sp_repr_lookup_name(root, "svg:text", -1); + } if ( repr == NULL ) { - _userWarn(SP_ACTIVE_DESKTOP, _("Clipboard does not contain a path.")); + _userWarn(desktop, _("Clipboard does not contain a path.")); sp_document_unref(tempdoc); return ""; } @@ -576,7 +601,9 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) sorted_items = g_slist_sort(sorted_items, (GCompareFunc) sp_object_compare_position); for (GSList *i = sorted_items ; i ; i = i->next) { - if (!SP_IS_ITEM(i->data)) continue; + if (!SP_IS_ITEM(i->data)) { + continue; + } Inkscape::XML::Node *obj = SP_OBJECT_REPR(i->data); Inkscape::XML::Node *obj_copy = _copyNode(obj, _doc, _root); @@ -595,7 +622,7 @@ void ClipboardManagerImpl::_copySelection(Inkscape::Selection *selection) // copy style for Paste Style action if (sorted_items) { - if(SP_IS_ITEM(sorted_items->data)) { + if (SP_IS_ITEM(sorted_items->data)) { SPCSSAttr *style = take_style_from_item((SPItem *) sorted_items->data); sp_repr_css_set(_clipnode, style, "style"); sp_repr_css_attr_unref(style); @@ -630,17 +657,21 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) if (style && (style->fill.isPaintserver())) { SPObject *server = SP_OBJECT_STYLE_FILL_SERVER(item); - if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) + if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) { _copyGradient(SP_GRADIENT(server)); - if (SP_IS_PATTERN(server)) + } + if (SP_IS_PATTERN(server)) { _copyPattern(SP_PATTERN(server)); + } } if (style && (style->stroke.isPaintserver())) { SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER(item); - if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) + if (SP_IS_LINEARGRADIENT(server) || SP_IS_RADIALGRADIENT(server)) { _copyGradient(SP_GRADIENT(server)); - if (SP_IS_PATTERN(server)) + } + if (SP_IS_PATTERN(server)) { _copyPattern(SP_PATTERN(server)); + } } // For shapes, copy all of the shape's markers @@ -659,8 +690,9 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) { LivePathEffectObject *lpeobj = (*it)->lpeobject; - if (lpeobj) + if (lpeobj) { _copyNode(SP_OBJECT_REPR(SP_OBJECT(lpeobj)), _doc, _defs); + } } } } @@ -682,8 +714,9 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) _copyNode(SP_OBJECT_REPR(mask), _doc, _defs); // recurse into the mask for its gradients etc. for (SPObject *o = SP_OBJECT(mask)->children ; o != NULL ; o = o->next) { - if (SP_IS_ITEM(o)) + if (SP_IS_ITEM(o)) { _copyUsedDefs(SP_ITEM(o)); + } } } // Copy filters @@ -696,8 +729,9 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) // recurse for (SPObject *o = SP_OBJECT(item)->children ; o != NULL ; o = o->next) { - if (SP_IS_ITEM(o)) + if (SP_IS_ITEM(o)) { _copyUsedDefs(SP_ITEM(o)); + } } } @@ -726,7 +760,9 @@ void ClipboardManagerImpl::_copyPattern(SPPattern *pattern) // items in the pattern may also use gradients and other patterns, so recurse for (SPObject *child = sp_object_first_child(SP_OBJECT(pattern)) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { - if (!SP_IS_ITEM (child)) continue; + if (!SP_IS_ITEM (child)) { + continue; + } _copyUsedDefs(SP_ITEM(child)); } pattern = pattern->ref->getObject(); @@ -740,11 +776,15 @@ void ClipboardManagerImpl::_copyPattern(SPPattern *pattern) void ClipboardManagerImpl::_copyTextPath(SPTextPath *tp) { SPItem *path = sp_textpath_get_path_item(tp); - if(!path) return; + if (!path) { + return; + } Inkscape::XML::Node *path_node = SP_OBJECT_REPR(path); // Do not copy the text path to defs if it's already copied - if(sp_repr_lookup_child(_root, "id", path_node->attribute("id"))) return; + if (sp_repr_lookup_child(_root, "id", path_node->attribute("id"))) { + return; + } _copyNode(path_node, _doc, _defs); } @@ -771,9 +811,8 @@ Inkscape::XML::Node *ClipboardManagerImpl::_copyNode(Inkscape::XML::Node *node, * @param in_place Whether to paste the selection where it was when copied * @pre @c clipdoc is not empty and items can be added to the current layer */ -void ClipboardManagerImpl::_pasteDocument(SPDocument *clipdoc, bool in_place) +void ClipboardManagerImpl::_pasteDocument(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; SPDocument *target_document = sp_desktop_document(desktop); Inkscape::XML::Node *root = sp_document_repr_root(clipdoc), @@ -781,16 +820,24 @@ void ClipboardManagerImpl::_pasteDocument(SPDocument *clipdoc, bool in_place) Inkscape::XML::Document *target_xmldoc = sp_document_repr_doc(target_document); // copy definitions - _pasteDefs(clipdoc); + _pasteDefs(desktop, clipdoc); // copy objects GSList *pasted_objects = NULL; for (Inkscape::XML::Node *obj = root->firstChild() ; obj ; obj = obj->next()) { // Don't copy metadata, defs, named views and internal clipboard contents to the document - if (!strcmp(obj->name(), "svg:defs")) continue; - if (!strcmp(obj->name(), "svg:metadata")) continue; - if (!strcmp(obj->name(), "sodipodi:namedview")) continue; - if (!strcmp(obj->name(), "inkscape:clipboard")) continue; + if (!strcmp(obj->name(), "svg:defs")) { + continue; + } + if (!strcmp(obj->name(), "svg:metadata")) { + continue; + } + if (!strcmp(obj->name(), "sodipodi:namedview")) { + continue; + } + if (!strcmp(obj->name(), "inkscape:clipboard")) { + continue; + } Inkscape::XML::Node *obj_copy = _copyNode(obj, target_xmldoc, target_parent); pasted_objects = g_slist_prepend(pasted_objects, (gpointer) obj_copy); } @@ -822,11 +869,12 @@ void ClipboardManagerImpl::_pasteDocument(SPDocument *clipdoc, bool in_place) if (!in_place) { SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop, false); // Don't display the snapindicator + m.setup(desktop); + sp_event_context_discard_delayed_snap_event(desktop->event_context); // get offset from mouse pointer to bbox center, snap to grid if enabled Geom::Point mouse_offset = desktop->point() - sel_bbox->midpoint(); - offset = m.multipleOfGridPitch(mouse_offset - offset) + offset; + offset = m.multipleOfGridPitch(mouse_offset - offset, sel_bbox->midpoint() + offset) + offset; } sp_selection_move_relative(selection, offset); @@ -841,10 +889,9 @@ void ClipboardManagerImpl::_pasteDocument(SPDocument *clipdoc, bool in_place) * @param clipdoc The document to paste * @pre @c clipdoc != NULL and pasting into the active document is possible */ -void ClipboardManagerImpl::_pasteDefs(SPDocument *clipdoc) +void ClipboardManagerImpl::_pasteDefs(SPDesktop *desktop, SPDocument *clipdoc) { // boilerplate vars copied from _pasteDocument - SPDesktop *desktop = SP_ACTIVE_DESKTOP; SPDocument *target_document = sp_desktop_document(desktop); Inkscape::XML::Node *root = sp_document_repr_root(clipdoc), @@ -863,35 +910,38 @@ void ClipboardManagerImpl::_pasteDefs(SPDocument *clipdoc) /** * @brief Retrieve a bitmap image from the clipboard and paste it into the active document */ -bool ClipboardManagerImpl::_pasteImage() +bool ClipboardManagerImpl::_pasteImage(SPDocument *doc) { - SPDocument *doc = SP_ACTIVE_DOCUMENT; - if ( doc == NULL ) return false; + if ( doc == NULL ) { + return false; + } // retrieve image data Glib::RefPtr<Gdk::Pixbuf> img = _clipboard->wait_for_image(); - if (!img) return false; - - // Very stupid hack: Write into a file, then import the file into the document. - // To avoid using tmpfile and POSIX file handles, make the filename based on current time. - // This wasn't my idea, I just copied this from selection-chemistry.cpp - // and just can't think of something saner at the moment. Pasting more than - // one image per second will overwrite the image. - // However, I don't think anyone is able to copy a _different_ image into inkscape - // in 1 second. - time_t rawtime; - char image_filename[128]; - - time(&rawtime); - strftime(image_filename, 128, "inkscape_pasted_image_%Y%m%d_%H%M%S.png", localtime( &rawtime )); - /// @todo Check whether the encoding is correct here - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - std::string save_folder = Glib::filename_from_utf8(prefs->getString("/dialogs/save_as/path")); - - gchar *image_path = g_build_filename(save_folder.data(), image_filename, NULL); - img->save(image_path, "png"); - file_import(doc, image_path, NULL); - g_free(image_path); + if (!img) { + return false; + } + + // TODO unify with interface.cpp's sp_ui_drag_data_received() + // AARGH stupid + Inkscape::Extension::DB::InputList o; + Inkscape::Extension::db.get_input_list(o); + Inkscape::Extension::DB::InputList::const_iterator i = o.begin(); + while (i != o.end() && strcmp( (*i)->get_mimetype(), "image/png" ) != 0) { + ++i; + } + Inkscape::Extension::Extension *png = *i; + bool save = (strcmp(png->get_param_optiongroup("link"), "embed") == 0); + png->set_param_optiongroup("link", "embed"); + png->set_gui(false); + + gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-clipboard-import", NULL ); + img->save(filename, "png"); + file_import(doc, filename, png); + g_free(filename); + + png->set_param_optiongroup("link", save ? "embed" : "link"); + png->set_gui(true); return true; } @@ -899,14 +949,16 @@ bool ClipboardManagerImpl::_pasteImage() /** * @brief Paste text into the selected text object or create a new one to hold it */ -bool ClipboardManagerImpl::_pasteText() +bool ClipboardManagerImpl::_pasteText(SPDesktop *desktop) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop == NULL ) return false; + if ( desktop == NULL ) { + return false; + } // if the text editing tool is active, paste the text into the active text object - if (tools_isactive(desktop, TOOLS_TEXT)) + if (tools_isactive(desktop, TOOLS_TEXT)) { return sp_text_paste_inline(desktop->event_context); + } // try to parse the text as a color and, if successful, apply it as the current style SPCSSAttr *css = _parseColor(_clipboard->wait_for_text()); @@ -930,29 +982,43 @@ SPCSSAttr *ClipboardManagerImpl::_parseColor(const Glib::ustring &text) Glib::ustring::size_type len = text.bytes(); char *str = const_cast<char *>(text.data()); bool attempt_alpha = false; - if ( !str || ( *str == '\0' ) ) return NULL; // this is OK due to boolean short-circuit + if ( !str || ( *str == '\0' ) ) { + return NULL; // this is OK due to boolean short-circuit + } // those conditionals guard against parsing e.g. the string "fab" as "fab000" // (incomplete color) and "45fab71" as "45fab710" (incomplete alpha) if ( *str == '#' ) { - if ( len < 7 ) return NULL; - if ( len >= 9 ) attempt_alpha = true; + if ( len < 7 ) { + return NULL; + } + if ( len >= 9 ) { + attempt_alpha = true; + } } else { - if ( len < 6 ) return NULL; - if ( len >= 8 ) attempt_alpha = true; + if ( len < 6 ) { + return NULL; + } + if ( len >= 8 ) { + attempt_alpha = true; + } } unsigned int color = 0, alpha = 0xff; // skip a leading #, if present - if ( *str == '#' ) ++str; + if ( *str == '#' ) { + ++str; + } // try to parse first 6 digits int res = sscanf(str, "%6x", &color); if ( res && ( res != EOF ) ) { if (attempt_alpha) {// try to parse alpha if there's enough characters sscanf(str + 6, "%2x", &alpha); - if ( !res || res == EOF ) alpha = 0xff; + if ( !res || res == EOF ) { + alpha = 0xff; + } } SPCSSAttr *color_css = sp_repr_css_attr_new(); @@ -963,7 +1029,9 @@ SPCSSAttr *ClipboardManagerImpl::_parseColor(const Glib::ustring &text) sp_repr_css_set_property(color_css, "fill", color_str); float opacity = static_cast<float>(alpha)/static_cast<float>(0xff); - if (opacity > 1.0) opacity = 1.0; // safeguard + if (opacity > 1.0) { + opacity = 1.0; // safeguard + } Inkscape::CSSOStringStream opcss; opcss << opacity; sp_repr_css_set_property(color_css, "fill-opacity", opcss.str().data()); @@ -976,19 +1044,31 @@ SPCSSAttr *ClipboardManagerImpl::_parseColor(const Glib::ustring &text) /** * @brief Applies a pasted path effect to a given item */ -void ClipboardManagerImpl::_applyPathEffect(SPItem *item, gchar const *effect) +void ClipboardManagerImpl::_applyPathEffect(SPItem *item, gchar const *effectstack) { - if ( item == NULL ) return; - if ( SP_IS_RECT(item) ) return; + if ( item == NULL ) { + return; + } + if ( SP_IS_RECT(item) ) { + return; + } if (SP_IS_LPE_ITEM(item)) { SPLPEItem *lpeitem = SP_LPE_ITEM(item); - SPObject *obj = sp_uri_reference_resolve(_clipboardSPDoc, effect); - if (!obj) return; - // if the effect is not used by anyone, we might as well take it - LivePathEffectObject *lpeobj = LIVEPATHEFFECT(obj)->fork_private_if_necessary(1); - sp_lpe_item_add_path_effect(lpeitem, lpeobj); + // for each effect in the stack, check if we need to fork it before adding it to the item + std::istringstream iss(effectstack); + std::string href; + while (std::getline(iss, href, ';')) + { + SPObject *obj = sp_uri_reference_resolve(_clipboardSPDoc, href.c_str()); + if (!obj) { + return; + } + // if the effectstack is not used by anyone, we might as well take it + LivePathEffectObject *lpeobj = LIVEPATHEFFECT(obj)->fork_private_if_necessary(1); + sp_lpe_item_add_path_effect(lpeitem, lpeobj); + } } } @@ -1000,10 +1080,11 @@ void ClipboardManagerImpl::_applyPathEffect(SPItem *item, gchar const *effect) SPDocument *ClipboardManagerImpl::_retrieveClipboard(Glib::ustring required_target) { Glib::ustring best_target; - if ( required_target == "" ) + if ( required_target == "" ) { best_target = _getBestTarget(); - else + } else { best_target = required_target; + } if ( best_target == "" ) { return NULL; @@ -1053,15 +1134,18 @@ SPDocument *ClipboardManagerImpl::_retrieveClipboard(Glib::ustring required_targ // there is no specific plain SVG input extension, so if we can paste the Inkscape SVG format, // we use the image/svg+xml mimetype to look up the input extension - if(target == "image/x-inkscape-svg") + if (target == "image/x-inkscape-svg") { target = "image/svg+xml"; + } Inkscape::Extension::DB::InputList inlist; Inkscape::Extension::db.get_input_list(inlist); Inkscape::Extension::DB::InputList::const_iterator in = inlist.begin(); - for (; in != inlist.end() && target != (*in)->get_mimetype() ; ++in){}; - if ( in == inlist.end() ) + for (; in != inlist.end() && target != (*in)->get_mimetype() ; ++in) { + }; + if ( in == inlist.end() ) { return NULL; // this shouldn't happen unless _getBestTarget returns something bogus + } SPDocument *tempdoc = NULL; try { @@ -1086,7 +1170,9 @@ void ClipboardManagerImpl::_onGet(Gtk::SelectionData &sel, guint /*info*/) g_assert( _clipboardSPDoc != NULL ); Glib::ustring target = sel.get_target(); - if(target == "") return; // this shouldn't happen + if (target == "") { + return; // this shouldn't happen + } if (target == CLIPBOARD_TEXT_TARGET) { target = "image/x-inkscape-svg"; @@ -1095,8 +1181,11 @@ void ClipboardManagerImpl::_onGet(Gtk::SelectionData &sel, guint /*info*/) Inkscape::Extension::DB::OutputList outlist; Inkscape::Extension::db.get_output_list(outlist); Inkscape::Extension::DB::OutputList::const_iterator out = outlist.begin(); - for ( ; out != outlist.end() && target != (*out)->get_mimetype() ; ++out){}; - if ( out == outlist.end() && target != "image/png") return; // this also shouldn't happen + for ( ; out != outlist.end() && target != (*out)->get_mimetype() ; ++out) { + }; + if ( out == outlist.end() && target != "image/png") { + return; // this also shouldn't happen + } // FIXME: Temporary hack until we add support for memory output. // Save to a temporary file, read it back and then set the clipboard contents @@ -1117,10 +1206,12 @@ void ClipboardManagerImpl::_onGet(Gtk::SelectionData &sel, guint /*info*/) // read from namedview Inkscape::XML::Node *nv = sp_repr_lookup_name (_clipboardSPDoc->rroot, "sodipodi:namedview"); - if (nv && nv->attribute("pagecolor")) + if (nv && nv->attribute("pagecolor")) { bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00); - if (nv && nv->attribute("inkscape:pageopacity")) + } + if (nv && nv->attribute("inkscape:pageopacity")) { bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0)); + } sp_export_png_file(_clipboardSPDoc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, NULL); } @@ -1200,9 +1291,8 @@ void ClipboardManagerImpl::_discardInternalClipboard() /** * @brief Get the scale to resize an item, based on the command and desktop state */ -Geom::Scale ClipboardManagerImpl::_getScale(Geom::Point const &min, Geom::Point const &max, Geom::Rect const &obj_rect, bool apply_x, bool apply_y) +Geom::Scale ClipboardManagerImpl::_getScale(SPDesktop *desktop, Geom::Point const &min, Geom::Point const &max, Geom::Rect const &obj_rect, bool apply_x, bool apply_y) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; double scale_x = 1.0; double scale_y = 1.0; @@ -1215,8 +1305,12 @@ Geom::Scale ClipboardManagerImpl::_getScale(Geom::Point const &min, Geom::Point // If the "lock aspect ratio" button is pressed and we paste only a single coordinate, // resize the second one by the same ratio too if (desktop->isToolboxButtonActive("lock")) { - if (apply_x && !apply_y) scale_y = scale_x; - if (apply_y && !apply_x) scale_x = scale_y; + if (apply_x && !apply_y) { + scale_y = scale_x; + } + if (apply_y && !apply_x) { + scale_x = scale_y; + } } return Geom::Scale(scale_x, scale_y); @@ -1240,36 +1334,43 @@ Glib::ustring ClipboardManagerImpl::_getBestTarget() g_debug("End clipboard targets\n"); //*/ - for(std::list<Glib::ustring>::iterator i = _preferred_targets.begin() ; + for (std::list<Glib::ustring>::iterator i = _preferred_targets.begin() ; i != _preferred_targets.end() ; ++i) { - if ( std::find(targets.begin(), targets.end(), *i) != targets.end() ) + if ( std::find(targets.begin(), targets.end(), *i) != targets.end() ) { return *i; + } } #ifdef WIN32 if (OpenClipboard(NULL)) { // If both bitmap and metafile are present, pick the one that was exported first. UINT format = EnumClipboardFormats(0); while (format) { - if (format == CF_ENHMETAFILE || format == CF_DIB || format == CF_BITMAP) + if (format == CF_ENHMETAFILE || format == CF_DIB || format == CF_BITMAP) { break; + } format = EnumClipboardFormats(format); } CloseClipboard(); - if (format == CF_ENHMETAFILE) + if (format == CF_ENHMETAFILE) { return CLIPBOARD_WIN32_EMF_TARGET; - if (format == CF_DIB || format == CF_BITMAP) + } + if (format == CF_DIB || format == CF_BITMAP) { return CLIPBOARD_GDK_PIXBUF_TARGET; + } } - if (IsClipboardFormatAvailable(CF_ENHMETAFILE)) + if (IsClipboardFormatAvailable(CF_ENHMETAFILE)) { return CLIPBOARD_WIN32_EMF_TARGET; + } #endif - if (_clipboard->wait_is_image_available()) + if (_clipboard->wait_is_image_available()) { return CLIPBOARD_GDK_PIXBUF_TARGET; - if (_clipboard->wait_is_text_available()) + } + if (_clipboard->wait_is_text_available()) { return CLIPBOARD_TEXT_TARGET; + } return ""; } @@ -1324,7 +1425,8 @@ void ClipboardManagerImpl::_setClipboardTargets() Inkscape::Extension::DB::OutputList outlist; Inkscape::Extension::db.get_output_list(outlist); Inkscape::Extension::DB::OutputList::const_iterator out = outlist.begin(); - for ( ; out != outlist.end() && target != (*out)->get_mimetype() ; ++out); + for ( ; out != outlist.end() && target != (*out)->get_mimetype() ; ++out) { + } if ( out != outlist.end() ) { // FIXME: Temporary hack until we add support for memory output. // Save to a temporary file, read it back and then set the clipboard contents @@ -1382,18 +1484,20 @@ void ClipboardManagerImpl::_inkscape_wait_for_targets(std::list<Glib::ustring> & GdkAtom* targets = 0; gint n_targets = 0; gboolean test = gtk_clipboard_wait_for_targets( gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), &targets, &n_targets ); - if(!test) + if (!test) { n_targets = 0; //otherwise it will be -1. + } //Add the targets to the C++ container: - for(int i = 0; i < n_targets; i++) + for (int i = 0; i < n_targets; i++) { //Convert the atom to a string: gchar* const atom_name = gdk_atom_name(targets[i]); Glib::ustring target; - if(atom_name) + if (atom_name) { target = Glib::ScopedPtr<char>(atom_name).get(); //This frees the gchar*. + } listTargets.push_back(target); } @@ -1409,8 +1513,10 @@ ClipboardManager::ClipboardManager() {} ClipboardManager::~ClipboardManager() {} ClipboardManager *ClipboardManager::get() { - if ( _instance == NULL ) + if ( _instance == NULL ) { _instance = new ClipboardManagerImpl; + } + return _instance; } diff --git a/src/ui/clipboard.h b/src/ui/clipboard.h index 54c05ac0d..6020ecdd8 100644 --- a/src/ui/clipboard.h +++ b/src/ui/clipboard.h @@ -6,8 +6,10 @@ */ /* Authors: * Krzysztof KosiÅ„ski <tweenk@o2.pl> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2008 authors + * Copyright (C) 2010 Jon A. Cruz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -39,16 +41,16 @@ namespace UI { class ClipboardManager { public: - virtual void copy() = 0; + virtual void copy(SPDesktop *desktop) = 0; virtual void copyPathParameter(Inkscape::LivePathEffect::PathParam *) = 0; - virtual bool paste(bool in_place = false) = 0; - virtual bool pasteStyle() = 0; - virtual bool pasteSize(bool separately, bool apply_x, bool apply_y) = 0; - virtual bool pastePathEffect() = 0; - virtual Glib::ustring getPathParameter() = 0; - virtual Glib::ustring getShapeOrTextObjectId() = 0; + virtual bool paste(SPDesktop *desktop, bool in_place = false) = 0; + virtual bool pasteStyle(SPDesktop *desktop) = 0; + virtual bool pasteSize(SPDesktop *desktop, bool separately, bool apply_x, bool apply_y) = 0; + virtual bool pastePathEffect(SPDesktop *desktop) = 0; + virtual Glib::ustring getPathParameter(SPDesktop* desktop) = 0; + virtual Glib::ustring getShapeOrTextObjectId(SPDesktop *desktop) = 0; virtual const gchar *getFirstObjectID() = 0; - + static ClipboardManager *get(); protected: ClipboardManager(); // singleton @@ -56,7 +58,7 @@ protected: private: ClipboardManager(const ClipboardManager &); ///< no copy ClipboardManager &operator=(const ClipboardManager &); ///< no assign - + static ClipboardManager *_instance; }; diff --git a/src/ui/dialog/CMakeLists.txt b/src/ui/dialog/CMakeLists.txt index ea63d6023..98c4a47bb 100644 --- a/src/ui/dialog/CMakeLists.txt +++ b/src/ui/dialog/CMakeLists.txt @@ -9,6 +9,7 @@ ENDIF(WIN32) SET(ui_dialog_SRC aboutbox.cpp align-and-distribute.cpp +color-item.cpp debug.cpp dialog.cpp dialog-manager.cpp diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index fac5bad80..033bec875 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -18,6 +18,8 @@ ink_common_sources += \ ui/dialog/behavior.h \ ui/dialog/calligraphic-profile-rename.h \ ui/dialog/calligraphic-profile-rename.cpp \ + ui/dialog/color-item.cpp \ + ui/dialog/color-item.h \ ui/dialog/debug.cpp \ ui/dialog/debug.h \ ui/dialog/dialog.cpp \ @@ -75,8 +77,6 @@ ink_common_sources += \ ui/dialog/print-colors-preview-dialog.h \ ui/dialog/scriptdialog.cpp \ ui/dialog/scriptdialog.h \ - ui/dialog/spray-option.cpp \ - ui/dialog/spray-option.h \ ui/dialog/svg-fonts-dialog.cpp \ ui/dialog/svg-fonts-dialog.h \ ui/dialog/swatches.cpp \ diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp index 025bec37a..bbd02fa5d 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -103,8 +103,8 @@ AboutBox::AboutBox() : Gtk::Dialog(_("About Inkscape")) { Gtk::Label *label=new Gtk::Label(); gchar *label_text = - g_strdup_printf("<small><i>Inkscape %s, built %s</i></small>", - Inkscape::version_string, __DATE__); + g_strdup_printf("<small><i>Inkscape %s</i></small>", + Inkscape::version_string); label->set_markup(label_text); label->set_alignment(Gtk::ALIGN_RIGHT, Gtk::ALIGN_CENTER); g_free(label_text); diff --git a/src/ui/dialog/align-and-distribute.cpp b/src/ui/dialog/align-and-distribute.cpp index 2bba0a0f8..a75a8d68d 100644 --- a/src/ui/dialog/align-and-distribute.cpp +++ b/src/ui/dialog/align-and-distribute.cpp @@ -24,20 +24,20 @@ #include "unclump.h" #include "document.h" #include "enums.h" -#include "graphlayout/graphlayout.h" +#include "graphlayout.h" #include "inkscape.h" #include "macros.h" -#include "node-context.h" //For access to ShapeEditor #include "preferences.h" -#include "removeoverlap/removeoverlap.h" +#include "removeoverlap.h" #include "selection.h" -#include "shape-editor.h" //For node align/distribute methods #include "sp-flowtext.h" #include "sp-item-transform.h" #include "sp-text.h" #include "text-editing.h" #include "tools-switch.h" #include "ui/icon-names.h" +#include "ui/tool/node-tool.h" +#include "ui/tool/multi-path-manipulator.h" #include "util/glib-list-iterators.h" #include "verbs.h" #include "widgets/icon.h" @@ -429,12 +429,13 @@ private : if (!_dialog.getDesktop()) return; SPEventContext *event_context = sp_desktop_event_context(_dialog.getDesktop()); - if (!SP_IS_NODE_CONTEXT (event_context)) return ; + if (!INK_IS_NODE_TOOL (event_context)) return; + InkNodeTool *nt = INK_NODE_TOOL(event_context); if (_distribute) - event_context->shape_editor->distribute((Geom::Dim2)_orientation); + nt->_multipath->distributeNodes(_orientation); else - event_context->shape_editor->align((Geom::Dim2)_orientation); + nt->_multipath->alignNodes(_orientation); } }; diff --git a/src/ui/dialog/color-item.cpp b/src/ui/dialog/color-item.cpp new file mode 100644 index 000000000..cb6cfbbbe --- /dev/null +++ b/src/ui/dialog/color-item.cpp @@ -0,0 +1,837 @@ +/** @file + * @brief Inkscape color swatch UI item. + */ +/* Authors: + * Jon A. Cruz + * + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <errno.h> +#include <glibmm/i18n.h> +#include <gtkmm/label.h> +#include <gtk/gtkdnd.h> + +#include "color-item.h" + +#include "desktop.h" +#include "desktop-handles.h" +#include "desktop-style.h" +#include "display/nr-plain-stuff.h" +#include "document.h" +#include "inkscape.h" // for SP_ACTIVE_DESKTOP +#include "io/resource.h" +#include "io/sys.h" +#include "message-context.h" +#include "sp-gradient.h" +#include "sp-item.h" +#include "svg/svg-color.h" +#include "xml/node.h" +#include "xml/repr.h" + +#include "color.h" // for SP_RGBA32_U_COMPOSE + + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +static std::vector<std::string> mimeStrings; +static std::map<std::string, guint> mimeToInt; + + +#if ENABLE_MAGIC_COLORS +// TODO remove this soon: +extern std::vector<SwatchPage*> possible; +#endif // ENABLE_MAGIC_COLORS + + +#if ENABLE_MAGIC_COLORS +static bool bruteForce( SPDocument* document, Inkscape::XML::Node* node, Glib::ustring const& match, int r, int g, int b ) +{ + bool changed = false; + + if ( node ) { + gchar const * val = node->attribute("inkscape:x-fill-tag"); + if ( val && (match == val) ) { + SPObject *obj = document->getObjectByRepr( node ); + + gchar c[64] = {0}; + sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( r, g, b, 0xff ) ); + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property( css, "fill", c ); + + sp_desktop_apply_css_recursive( obj, css, true ); + static_cast<SPItem*>(obj)->updateRepr(); + + changed = true; + } + + val = node->attribute("inkscape:x-stroke-tag"); + if ( val && (match == val) ) { + SPObject *obj = document->getObjectByRepr( node ); + + gchar c[64] = {0}; + sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( r, g, b, 0xff ) ); + SPCSSAttr *css = sp_repr_css_attr_new(); + sp_repr_css_set_property( css, "stroke", c ); + + sp_desktop_apply_css_recursive( (SPItem*)obj, css, true ); + ((SPItem*)obj)->updateRepr(); + + changed = true; + } + + Inkscape::XML::Node* first = node->firstChild(); + changed |= bruteForce( document, first, match, r, g, b ); + + changed |= bruteForce( document, node->next(), match, r, g, b ); + } + + return changed; +} +#endif // ENABLE_MAGIC_COLORS + +static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) { + ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); + if ( item ) { + item->buttonClicked(false); + } +} + +static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer callback_data ) { + ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); + if ( item ) { + item->buttonClicked(true); + } +} + +static gboolean handleEnterNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) { + ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); + if ( item ) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if ( desktop ) { + gchar* msg = g_strdup_printf(_("Color: <b>%s</b>; <b>Click</b> to set fill, <b>Shift+click</b> to set stroke"), + item->def.descr.c_str()); + desktop->tipsMessageContext()->set(Inkscape::INFORMATION_MESSAGE, msg); + g_free(msg); + } + } + return FALSE; +} + +static gboolean handleLeaveNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) { + ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); + if ( item ) { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if ( desktop ) { + desktop->tipsMessageContext()->clear(); + } + } + return FALSE; +} + +static void dieDieDie( GtkObject *obj, gpointer user_data ) +{ + g_message("die die die %p %p", obj, user_data ); +} + +static bool getBlock( std::string& dst, guchar ch, std::string const str ) +{ + bool good = false; + std::string::size_type pos = str.find(ch); + if ( pos != std::string::npos ) + { + std::string::size_type pos2 = str.find( '(', pos ); + if ( pos2 != std::string::npos ) { + std::string::size_type endPos = str.find( ')', pos2 ); + if ( endPos != std::string::npos ) { + dst = str.substr( pos2 + 1, (endPos - pos2 - 1) ); + good = true; + } + } + } + return good; +} + +static bool popVal( guint64& numVal, std::string& str ) +{ + bool good = false; + std::string::size_type endPos = str.find(','); + if ( endPos == std::string::npos ) { + endPos = str.length(); + } + + if ( endPos != std::string::npos && endPos > 0 ) { + std::string xxx = str.substr( 0, endPos ); + const gchar* ptr = xxx.c_str(); + gchar* endPtr = 0; + numVal = g_ascii_strtoull( ptr, &endPtr, 10 ); + if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) { + // overflow + } else if ( (numVal == 0) && (endPtr == ptr) ) { + // failed conversion + } else { + good = true; + str.erase( 0, endPos + 1 ); + } + } + + return good; +} + +// TODO resolve this more cleanly: +extern gboolean colorItemHandleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, gpointer user_data); + +static void colorItemDragBegin( GtkWidget */*widget*/, GdkDragContext* dc, gpointer data ) +{ + ColorItem* item = reinterpret_cast<ColorItem*>(data); + if ( item ) + { + using Inkscape::IO::Resource::get_path; + using Inkscape::IO::Resource::ICONS; + using Inkscape::IO::Resource::SYSTEM; + int width = 32; + int height = 24; + + if (item->def.getType() != ege::PaintDef::RGB){ + GError *error = NULL; + gsize bytesRead = 0; + gsize bytesWritten = 0; + gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"), + -1, + &bytesRead, + &bytesWritten, + &error); + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_scale(localFilename, width, height, FALSE, &error); + g_free(localFilename); + gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 ); + } else { + GdkPixbuf* pixbuf = 0; + if ( item->getGradient() ){ + guchar* px = g_new( guchar, 3 * height * width ); + nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 ); + + sp_gradient_render_vector_block_rgb( item->getGradient(), + px, width, height, 3 * width, + 0, width, TRUE ); + + pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8, + width, height, width * 3, + 0, // add delete function + 0 ); + } else { + Glib::RefPtr<Gdk::Pixbuf> thumb = Gdk::Pixbuf::create( Gdk::COLORSPACE_RGB, false, 8, width, height ); + guint32 fillWith = (0xff000000 & (item->def.getR() << 24)) + | (0x00ff0000 & (item->def.getG() << 16)) + | (0x0000ff00 & (item->def.getB() << 8)); + thumb->fill( fillWith ); + pixbuf = thumb->gobj(); + g_object_ref(G_OBJECT(pixbuf)); + } + gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 ); + } + } + +} + +//"drag-drop" +// gboolean dragDropColorData( GtkWidget *widget, +// GdkDragContext *drag_context, +// gint x, +// gint y, +// guint time, +// gpointer user_data) +// { +// // TODO finish + +// return TRUE; +// } + + +SwatchPage::SwatchPage() : + _name(), + _prefWidth(0), + _colors() +{ +} + +SwatchPage::~SwatchPage() +{ +} + + +ColorItem::ColorItem(ege::PaintDef::ColorType type) : + Previewable(), + def(type), + tips(), + _previews(), + _isFill(false), + _isStroke(false), + _isLive(false), + _linkIsTone(false), + _linkPercent(0), + _linkGray(0), + _linkSrc(0), + _grad(0), + _pixData(0), + _pixWidth(0), + _pixHeight(0), + _listeners() +{ +} + +ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) : + Previewable(), + def( r, g, b, name ), + tips(), + _previews(), + _isFill(false), + _isStroke(false), + _isLive(false), + _linkIsTone(false), + _linkPercent(0), + _linkGray(0), + _linkSrc(0), + _grad(0), + _pixData(0), + _pixWidth(0), + _pixHeight(0), + _listeners() +{ +} + +ColorItem::~ColorItem() +{ +} + +ColorItem::ColorItem(ColorItem const &other) : + Inkscape::UI::Previewable() +{ + if ( this != &other ) { + *this = other; + } +} + +ColorItem &ColorItem::operator=(ColorItem const &other) +{ + if ( this != &other ) { + def = other.def; + + // TODO - correct linkage + _linkSrc = other._linkSrc; + g_message("Erk!"); + } + return *this; +} + +void ColorItem::setState( bool fill, bool stroke ) +{ + if ( (_isFill != fill) || (_isStroke != stroke) ) { + _isFill = fill; + _isStroke = stroke; + + for ( std::vector<Gtk::Widget*>::iterator it = _previews.begin(); it != _previews.end(); ++it ) { + Gtk::Widget* widget = *it; + if ( IS_EEK_PREVIEW(widget->gobj()) ) { + EekPreview * preview = EEK_PREVIEW(widget->gobj()); + + int val = eek_preview_get_linked( preview ); + val &= ~(PREVIEW_FILL | PREVIEW_STROKE); + if ( _isFill ) { + val |= PREVIEW_FILL; + } + if ( _isStroke ) { + val |= PREVIEW_STROKE; + } + eek_preview_set_linked( preview, static_cast<LinkType>(val) ); + } + } + } +} + +void ColorItem::setGradient(SPGradient *grad) +{ + if (_grad != grad) { + _grad = grad; + // TODO regen and push to listeners + } +} + +void ColorItem::setPixData(guchar* px, int width, int height) +{ + if (px != _pixData) { + if (_pixData) { + g_free(_pixData); + } + _pixData = px; + _pixWidth = width; + _pixHeight = height; + + _updatePreviews(); + } +} + +void ColorItem::_dragGetColorData( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + GtkSelectionData *data, + guint info, + guint /*time*/, + gpointer user_data) +{ + ColorItem* item = reinterpret_cast<ColorItem*>(user_data); + std::string key; + if ( info < mimeStrings.size() ) { + key = mimeStrings[info]; + } else { + g_warning("ERROR: unknown value (%d)", info); + } + + if ( !key.empty() ) { + char* tmp = 0; + int len = 0; + int format = 0; + item->def.getMIMEData(key, tmp, len, format); + if ( tmp ) { + GdkAtom dataAtom = gdk_atom_intern( key.c_str(), FALSE ); + gtk_selection_data_set( data, dataAtom, format, (guchar*)tmp, len ); + delete[] tmp; + } + } +} + +void ColorItem::_dropDataIn( GtkWidget */*widget*/, + GdkDragContext */*drag_context*/, + gint /*x*/, gint /*y*/, + GtkSelectionData */*data*/, + guint /*info*/, + guint /*event_time*/, + gpointer /*user_data*/) +{ +} + +void ColorItem::_colorDefChanged(void* data) +{ + ColorItem* item = reinterpret_cast<ColorItem*>(data); + if ( item ) { + item->_updatePreviews(); + } +} + +void ColorItem::_updatePreviews() +{ + for ( std::vector<Gtk::Widget*>::iterator it = _previews.begin(); it != _previews.end(); ++it ) { + Gtk::Widget* widget = *it; + if ( IS_EEK_PREVIEW(widget->gobj()) ) { + EekPreview * preview = EEK_PREVIEW(widget->gobj()); + + _regenPreview(preview); + + widget->queue_draw(); + } + } + + for ( std::vector<ColorItem*>::iterator it = _listeners.begin(); it != _listeners.end(); ++it ) { + guint r = def.getR(); + guint g = def.getG(); + guint b = def.getB(); + + if ( (*it)->_linkIsTone ) { + r = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * r) ) / 100; + g = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * g) ) / 100; + b = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * b) ) / 100; + } else { + r = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * r) ) / 100; + g = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * g) ) / 100; + b = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * b) ) / 100; + } + + (*it)->def.setRGB( r, g, b ); + } + + +#if ENABLE_MAGIC_COLORS + // Look for objects using this color + { + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if ( desktop ) { + SPDocument* document = sp_desktop_document( desktop ); + Inkscape::XML::Node *rroot = sp_document_repr_root( document ); + if ( rroot ) { + + // Find where this thing came from + Glib::ustring paletteName; + bool found = false; + int index = 0; + for ( std::vector<SwatchPage*>::iterator it2 = possible.begin(); it2 != possible.end() && !found; ++it2 ) { + SwatchPage* curr = *it2; + index = 0; + for ( std::vector<ColorItem*>::iterator zz = curr->_colors.begin(); zz != curr->_colors.end(); ++zz ) { + if ( this == *zz ) { + found = true; + paletteName = curr->_name; + break; + } else { + index++; + } + } + } + + if ( !paletteName.empty() ) { + gchar* str = g_strdup_printf("%d|", index); + paletteName.insert( 0, str ); + g_free(str); + str = 0; + + if ( bruteForce( document, rroot, paletteName, def.getR(), def.getG(), def.getB() ) ) { + sp_document_done( document , SP_VERB_DIALOG_SWATCHES, + _("Change color definition")); + } + } + } + } + } +#endif // ENABLE_MAGIC_COLORS + +} + +void ColorItem::_regenPreview(EekPreview * preview) +{ + if ( def.getType() != ege::PaintDef::RGB ) { + using Inkscape::IO::Resource::get_path; + using Inkscape::IO::Resource::ICONS; + using Inkscape::IO::Resource::SYSTEM; + GError *error = NULL; + gsize bytesRead = 0; + gsize bytesWritten = 0; + gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"), + -1, + &bytesRead, + &bytesWritten, + &error); + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(localFilename, &error); + if (!pixbuf) { + g_warning("Null pixbuf for %p [%s]", localFilename, localFilename ); + } + g_free(localFilename); + + eek_preview_set_pixbuf( preview, pixbuf ); + } + else if ( !_pixData ){ + eek_preview_set_color( preview, + (def.getR() << 8) | def.getR(), + (def.getG() << 8) | def.getG(), + (def.getB() << 8) | def.getB() ); + } else { + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( _pixData, GDK_COLORSPACE_RGB, FALSE, 8, + _pixWidth, _pixHeight, _pixWidth * 3, + 0, // add delete function + 0 ); + eek_preview_set_pixbuf( preview, pixbuf ); + } + + eek_preview_set_linked( preview, (LinkType)((_linkSrc ? PREVIEW_LINK_IN:0) + | (_listeners.empty() ? 0:PREVIEW_LINK_OUT) + | (_isLive ? PREVIEW_LINK_OTHER:0)) ); +} + +Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, ::PreviewSize size, guint ratio) +{ + Gtk::Widget* widget = 0; + if ( style == PREVIEW_STYLE_BLURB) { + Gtk::Label *lbl = new Gtk::Label(def.descr); + lbl->set_alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER); + widget = lbl; + } else { +// Glib::ustring blank(" "); +// if ( size == Inkscape::ICON_SIZE_MENU || size == Inkscape::ICON_SIZE_DECORATION ) { +// blank = " "; +// } + + GtkWidget* eekWidget = eek_preview_new(); + EekPreview * preview = EEK_PREVIEW(eekWidget); + Gtk::Widget* newBlot = Glib::wrap(eekWidget); + + _regenPreview(preview); + + eek_preview_set_details( preview, (::PreviewStyle)style, (::ViewType)view, (::PreviewSize)size, ratio ); + + def.addCallback( _colorDefChanged, this ); + + GValue val = {0, {{0}, {0}}}; + g_value_init( &val, G_TYPE_BOOLEAN ); + g_value_set_boolean( &val, FALSE ); + g_object_set_property( G_OBJECT(preview), "focus-on-click", &val ); + +/* + Gtk::Button *btn = new Gtk::Button(blank); + Gdk::Color color; + color.set_rgb((_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b); + btn->modify_bg(Gtk::STATE_NORMAL, color); + btn->modify_bg(Gtk::STATE_ACTIVE, color); + btn->modify_bg(Gtk::STATE_PRELIGHT, color); + btn->modify_bg(Gtk::STATE_SELECTED, color); + + Gtk::Widget* newBlot = btn; +*/ + + tips.set_tip((*newBlot), def.descr); + +/* + newBlot->signal_clicked().connect( sigc::mem_fun(*this, &ColorItem::buttonClicked) ); + + sigc::signal<void> type_signal_something; +*/ + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "clicked", + G_CALLBACK(handleClick), + this); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "alt-clicked", + G_CALLBACK(handleSecondaryClick), + this); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "button-press-event", + G_CALLBACK(colorItemHandleButtonPress), + this); + + { + std::vector<std::string> listing = def.getMIMETypes(); + int entryCount = listing.size(); + GtkTargetEntry* entries = new GtkTargetEntry[entryCount]; + GtkTargetEntry* curr = entries; + for ( std::vector<std::string>::iterator it = listing.begin(); it != listing.end(); ++it ) { + curr->target = g_strdup(it->c_str()); + curr->flags = 0; + if ( mimeToInt.find(*it) == mimeToInt.end() ){ + // these next lines are order-dependent: + mimeToInt[*it] = mimeStrings.size(); + mimeStrings.push_back(*it); + } + curr->info = mimeToInt[curr->target]; + curr++; + } + gtk_drag_source_set( GTK_WIDGET(newBlot->gobj()), + GDK_BUTTON1_MASK, + entries, entryCount, + GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY) ); + for ( int i = 0; i < entryCount; i++ ) { + g_free(entries[i].target); + } + delete[] entries; + } + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "drag-data-get", + G_CALLBACK(ColorItem::_dragGetColorData), + this); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "drag-begin", + G_CALLBACK(colorItemDragBegin), + this ); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "enter-notify-event", + G_CALLBACK(handleEnterNotify), + this); + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "leave-notify-event", + G_CALLBACK(handleLeaveNotify), + this); + +// g_signal_connect( G_OBJECT(newBlot->gobj()), +// "drag-drop", +// G_CALLBACK(dragDropColorData), +// this); + + if ( def.isEditable() ) + { +// gtk_drag_dest_set( GTK_WIDGET(newBlot->gobj()), +// GTK_DEST_DEFAULT_ALL, +// destColorTargets, +// G_N_ELEMENTS(destColorTargets), +// GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) ); + + +// g_signal_connect( G_OBJECT(newBlot->gobj()), +// "drag-data-received", +// G_CALLBACK(_dropDataIn), +// this ); + } + + g_signal_connect( G_OBJECT(newBlot->gobj()), + "destroy", + G_CALLBACK(dieDieDie), + this); + + + widget = newBlot; + } + + _previews.push_back( widget ); + + return widget; +} + +void ColorItem::buttonClicked(bool secondary) +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) { + char const * attrName = secondary ? "stroke" : "fill"; + + SPCSSAttr *css = sp_repr_css_attr_new(); + Glib::ustring descr; + switch (def.getType()) { + case ege::PaintDef::CLEAR: { + // TODO actually make this clear + sp_repr_css_set_property( css, attrName, "none" ); + descr = secondary? _("Remove stroke color") : _("Remove fill color"); + break; + } + case ege::PaintDef::NONE: { + sp_repr_css_set_property( css, attrName, "none" ); + descr = secondary? _("Set stroke color to none") : _("Set fill color to none"); + break; + } + case ege::PaintDef::RGB: { + Glib::ustring colorspec; + if ( _grad ){ + colorspec = "url(#"; + colorspec += _grad->getId(); + colorspec += ")"; + } else { + gchar c[64]; + guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff; + sp_svg_write_color(c, sizeof(c), rgba); + colorspec = c; + } + sp_repr_css_set_property( css, attrName, colorspec.c_str() ); + descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch"); + break; + } + } + sp_desktop_set_style(desktop, css); + sp_repr_css_attr_unref(css); + + sp_document_done( sp_desktop_document(desktop), SP_VERB_DIALOG_SWATCHES, descr.c_str() ); + } +} + +void ColorItem::_wireMagicColors( SwatchPage *colorSet ) +{ + if ( colorSet ) + { + for ( std::vector<ColorItem*>::iterator it = colorSet->_colors.begin(); it != colorSet->_colors.end(); ++it ) + { + std::string::size_type pos = (*it)->def.descr.find("*{"); + if ( pos != std::string::npos ) + { + std::string subby = (*it)->def.descr.substr( pos + 2 ); + std::string::size_type endPos = subby.find("}*"); + if ( endPos != std::string::npos ) + { + subby.erase( endPos ); + //g_message("FOUND MAGIC at '%s'", (*it)->def.descr.c_str()); + //g_message(" '%s'", subby.c_str()); + + if ( subby.find('E') != std::string::npos ) + { + (*it)->def.setEditable( true ); + } + + if ( subby.find('L') != std::string::npos ) + { + (*it)->_isLive = true; + } + + std::string part; + // Tint. index + 1 more val. + if ( getBlock( part, 'T', subby ) ) { + guint64 colorIndex = 0; + if ( popVal( colorIndex, part ) ) { + guint64 percent = 0; + if ( popVal( percent, part ) ) { + (*it)->_linkTint( *(colorSet->_colors[colorIndex]), percent ); + } + } + } + + // Shade/tone. index + 1 or 2 more val. + if ( getBlock( part, 'S', subby ) ) { + guint64 colorIndex = 0; + if ( popVal( colorIndex, part ) ) { + guint64 percent = 0; + if ( popVal( percent, part ) ) { + guint64 grayLevel = 0; + if ( !popVal( grayLevel, part ) ) { + grayLevel = 0; + } + (*it)->_linkTone( *(colorSet->_colors[colorIndex]), percent, grayLevel ); + } + } + } + + } + } + } + } +} + + +void ColorItem::_linkTint( ColorItem& other, int percent ) +{ + if ( !_linkSrc ) + { + other._listeners.push_back(this); + _linkIsTone = false; + _linkPercent = percent; + if ( _linkPercent > 100 ) + _linkPercent = 100; + if ( _linkPercent < 0 ) + _linkPercent = 0; + _linkGray = 0; + _linkSrc = &other; + + ColorItem::_colorDefChanged(&other); + } +} + +void ColorItem::_linkTone( ColorItem& other, int percent, int grayLevel ) +{ + if ( !_linkSrc ) + { + other._listeners.push_back(this); + _linkIsTone = true; + _linkPercent = percent; + if ( _linkPercent > 100 ) + _linkPercent = 100; + if ( _linkPercent < 0 ) + _linkPercent = 0; + _linkGray = grayLevel; + _linkSrc = &other; + + ColorItem::_colorDefChanged(&other); + } +} + +} // namespace Dialogs +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/color-item.h b/src/ui/dialog/color-item.h new file mode 100644 index 000000000..4aac86a30 --- /dev/null +++ b/src/ui/dialog/color-item.h @@ -0,0 +1,128 @@ +/** @file + * @brief Inkscape color swatch UI item. + */ +/* Authors: + * Jon A. Cruz + * + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_DIALOGS_COLOR_ITEM_H +#define SEEN_DIALOGS_COLOR_ITEM_H + +#include <gtkmm/tooltips.h> + +#include "widgets/ege-paint-def.h" +#include "ui/previewable.h" + +class SPGradient; + +namespace Inkscape { +namespace UI { +namespace Dialogs { + +class ColorItem; + +class SwatchPage +{ +public: + SwatchPage(); + ~SwatchPage(); + + Glib::ustring _name; + int _prefWidth; + std::vector<ColorItem*> _colors; +}; + + +/** + * The color swatch you see on screen as a clickable box. + */ +class ColorItem : public Inkscape::UI::Previewable +{ + friend void _loadPaletteFile( gchar const *filename ); +public: + ColorItem( ege::PaintDef::ColorType type ); + ColorItem( unsigned int r, unsigned int g, unsigned int b, + Glib::ustring& name ); + virtual ~ColorItem(); + ColorItem(ColorItem const &other); + virtual ColorItem &operator=(ColorItem const &other); + virtual Gtk::Widget* getPreview(PreviewStyle style, + ViewType view, + ::PreviewSize size, + guint ratio); + void buttonClicked(bool secondary = false); + + void setGradient(SPGradient *grad); + SPGradient * getGradient() const { return _grad; } + + void setPixData(guchar* px, int width, int height); + + void setState( bool fill, bool stroke ); + bool isFill() { return _isFill; } + bool isStroke() { return _isStroke; } + + ege::PaintDef def; + +private: + + static void _dropDataIn( GtkWidget *widget, + GdkDragContext *drag_context, + gint x, gint y, + GtkSelectionData *data, + guint info, + guint event_time, + gpointer user_data); + + static void _dragGetColorData( GtkWidget *widget, + GdkDragContext *drag_context, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data); + + static void _wireMagicColors( SwatchPage *colorSet ); + static void _colorDefChanged(void* data); + + void _updatePreviews(); + void _regenPreview(EekPreview * preview); + + void _linkTint( ColorItem& other, int percent ); + void _linkTone( ColorItem& other, int percent, int grayLevel ); + + Gtk::Tooltips tips; + std::vector<Gtk::Widget*> _previews; + + bool _isFill; + bool _isStroke; + bool _isLive; + bool _linkIsTone; + int _linkPercent; + int _linkGray; + ColorItem* _linkSrc; + SPGradient* _grad; + guchar *_pixData; + int _pixWidth; + int _pixHeight; + std::vector<ColorItem*> _listeners; +}; + +} // namespace Dialogs +} // namespace UI +} // namespace Inkscape + +#endif // SEEN_DIALOGS_COLOR_ITEM_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index 2116d46c3..30cbed649 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -40,7 +40,6 @@ #include "ui/dialog/icon-preview.h" #include "ui/dialog/floating-behavior.h" #include "ui/dialog/dock-behavior.h" -#include "ui/dialog/spray-option.h" #include "ui/dialog/print-colors-preview-dialog.h" #include "preferences.h" @@ -114,7 +113,6 @@ DialogManager::DialogManager() { registerFactory("Transformation", &create<Transformation, FloatingBehavior>); registerFactory("UndoHistory", &create<UndoHistory, FloatingBehavior>); registerFactory("InputDevices", &create<InputDialog, FloatingBehavior>); - registerFactory("SprayOptionClass", &create<SprayOptionClass, FloatingBehavior>); } else { @@ -142,7 +140,6 @@ DialogManager::DialogManager() { registerFactory("Transformation", &create<Transformation, DockBehavior>); registerFactory("UndoHistory", &create<UndoHistory, DockBehavior>); registerFactory("InputDevices", &create<InputDialog, DockBehavior>); - registerFactory("SprayOptionClass", &create<SprayOptionClass, DockBehavior>); } } diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index a7241ea40..86baa85cd 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -222,7 +222,7 @@ DocumentProperties::build_page() Gtk::Label* label_bor = manage (new Gtk::Label); label_bor->set_markup (_("<b>Border</b>")); Gtk::Label *label_for = manage (new Gtk::Label); - label_for->set_markup (_("<b>Format</b>")); + label_for->set_markup (_("<b>Page Size</b>")); _page_sizer.init(); Gtk::Widget *const widget_array[] = diff --git a/src/ui/dialog/filedialogimpl-gtkmm.cpp b/src/ui/dialog/filedialogimpl-gtkmm.cpp index e650cf842..916e3ec97 100644 --- a/src/ui/dialog/filedialogimpl-gtkmm.cpp +++ b/src/ui/dialog/filedialogimpl-gtkmm.cpp @@ -521,7 +521,7 @@ bool SVGPreview::set(Glib::ustring &fileName, int dialogType) return FALSE; } long fileLen = info.st_size; - if (fileLen > 0x150000L) + if (fileLen > 0xA00000L) { showingNoPreview = false; showTooLarge(fileLen); diff --git a/src/ui/dialog/filedialogimpl-win32.cpp b/src/ui/dialog/filedialogimpl-win32.cpp index d22a368f2..0f3672f25 100644 --- a/src/ui/dialog/filedialogimpl-win32.cpp +++ b/src/ui/dialog/filedialogimpl-win32.cpp @@ -61,7 +61,7 @@ namespace Dialog const int PreviewWidening = 150; const char PreviewWindowClassName[] = "PreviewWnd"; -const unsigned long MaxPreviewFileSize = 1344; // kB +const unsigned long MaxPreviewFileSize = 10240; // kB #define IDC_SHOW_PREVIEW 1000 @@ -454,15 +454,15 @@ UINT_PTR CALLBACK FileOpenDialogImplWin32::GetOpenFileName_hookproc( pImpl = (FileOpenDialogImplWin32*)ofn->lCustData; // Subclass the parent - pImpl->_base_window_proc = (WNDPROC)GetWindowLongPtr(hParentWnd, GWL_WNDPROC); - SetWindowLongPtr(hParentWnd, GWL_WNDPROC, (LONG_PTR)file_dialog_subclass_proc); + pImpl->_base_window_proc = (WNDPROC)GetWindowLongPtr(hParentWnd, GWLP_WNDPROC); + SetWindowLongPtr(hParentWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(file_dialog_subclass_proc)); // Add a button to the toolbar pImpl->_toolbar_wnd = FindWindowEx(hParentWnd, NULL, "ToolbarWindow32", NULL); pImpl->_show_preview_button_bitmap = LoadBitmap( hInstance, MAKEINTRESOURCE(IDC_SHOW_PREVIEW)); - TBADDBITMAP tbAddBitmap = {NULL, (UINT)pImpl->_show_preview_button_bitmap}; + TBADDBITMAP tbAddBitmap = {NULL, reinterpret_cast<UINT_PTR>(pImpl->_show_preview_button_bitmap)}; const int iBitmapIndex = SendMessage(pImpl->_toolbar_wnd, TB_ADDBITMAP, 1, (LPARAM)&tbAddBitmap); @@ -1373,10 +1373,10 @@ void FileOpenDialogImplWin32::render_preview() if(_preview_bitmap_image) // Is the image a pixbuf? { // Set the transformation - const Matrix matrix = { + const Cairo::Matrix matrix( scaleFactor, 0, 0, scaleFactor, - svgX, svgY }; + svgX, svgY); context->set_matrix (matrix); // Render the image diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 1345ffe55..132e5fd4e 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -1265,7 +1265,7 @@ void FilterEffectsDialog::FilterModifier::update_filters() SPFilter* f = (SPFilter*)l->data; row[_columns.filter] = f; const gchar* lbl = f->label(); - const gchar* id = SP_OBJECT_ID(f); + const gchar* id = f->getId(); row[_columns.label] = lbl ? lbl : (id ? id : "filter"); } @@ -1485,7 +1485,7 @@ void FilterEffectsDialog::PrimitiveList::update() row[_columns.primitive] = prim; row[_columns.type_id] = FPConverter.get_id_from_key(prim->repr->name()); row[_columns.type] = _(FPConverter.get_label(row[_columns.type_id]).c_str()); - row[_columns.id] = SP_OBJECT_ID(prim); + row[_columns.id] = prim->getId(); if(prim == active_prim) { get_selection()->select(row); diff --git a/src/ui/dialog/icon-preview.cpp b/src/ui/dialog/icon-preview.cpp index 336afc3c5..088f63031 100644 --- a/src/ui/dialog/icon-preview.cpp +++ b/src/ui/dialog/icon-preview.cpp @@ -90,7 +90,7 @@ IconPreviewPanel::IconPreviewPanel() : std::vector<Glib::ustring> pref_sizes = prefs->getAllDirs("/iconpreview/sizes/default"); std::vector<int> rawSizes; - + for (std::vector<Glib::ustring>::iterator i = pref_sizes.begin(); i != pref_sizes.end(); ++i) { if (prefs->getBool(*i + "/show", true)) { int sizeVal = prefs->getInt(*i + "/value", -1); @@ -215,7 +215,7 @@ void IconPreviewPanel::refreshPreview() while ( items && !target ) { SPItem* item = SP_ITEM( items->data ); SPObject * obj = SP_OBJECT(item); - gchar const *id = SP_OBJECT_ID( obj ); + gchar const *id = obj->getId(); if ( id ) { target = obj; } @@ -248,7 +248,7 @@ void IconPreviewPanel::modeToggled() void IconPreviewPanel::renderPreview( SPObject* obj ) { SPDocument * doc = SP_OBJECT_DOCUMENT(obj); - gchar * id = SP_OBJECT_ID(obj); + gchar const * id = obj->getId(); // g_message(" setting up to render '%s' as the icon", id ); diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 90516063c..40efc8282 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -433,15 +433,28 @@ void InkscapePreferences::initPageTools() this->AddPage(_page_node, _("Node"), iter_tools, PREFS_PAGE_TOOLS_NODE); AddSelcueCheckbox(_page_node, "/tools/nodes", true); AddGradientCheckbox(_page_node, "/tools/nodes", true); - _page_node.add_group_header( _("Path outline:")); + _page_node.add_group_header( _("Path outline")); _t_node_pathoutline_color.init(_("Path outline color"), "/tools/nodes/highlight_color", 0xff0000ff); - _page_node.add_line( false, _("Path outline color"), _t_node_pathoutline_color, "", _("Selects the color used for showing the path outline."), false); - _t_node_pathflash_enabled.init ( _("Path outline flash on mouse-over"), "/tools/nodes/pathflash_enabled", false); + _page_node.add_line( false, "", _t_node_pathoutline_color, "", _("Selects the color used for showing the path outline."), false); + _t_node_show_outline.init(_("Always show outline"), "/tools/nodes/show_outline", false); + _page_node.add_line( true, "", _t_node_show_outline, "", _("Show outlines for all paths, not only invisible paths")); + _t_node_live_outline.init(_("Update outline when dragging nodes"), "/tools/nodes/live_outline", false); + _page_node.add_line( true, "", _t_node_live_outline, "", _("Update the outline when dragging or transforming nodes. If this is off, the outline will only update when completing a drag.")); + _t_node_live_objects.init(_("Update paths when dragging nodes"), "/tools/nodes/live_objects", false); + _page_node.add_line( true, "", _t_node_live_objects, "", _("Update paths when dragging or transforming nodes. If this is off, paths will only be updated when completing a drag.")); + _t_node_show_path_direction.init(_("Show path direction on outlines"), "/tools/nodes/show_path_direction", false); + _page_node.add_line( true, "", _t_node_show_path_direction, "", _("Visualize the direction of selected paths by drawing small arrows in the middle of each outline segment")); + _t_node_pathflash_enabled.init ( _("Show temporary path outline"), "/tools/nodes/pathflash_enabled", false); _page_node.add_line( true, "", _t_node_pathflash_enabled, "", _("When hovering over a path, briefly flash its outline.")); - _t_node_pathflash_unselected.init ( _("Suppress path outline flash when one path selected"), "/tools/nodes/pathflash_unselected", false); - _page_node.add_line( true, "", _t_node_pathflash_unselected, "", _("If a path is selected, do not continue flashing path outlines.")); + _t_node_pathflash_selected.init ( _("Show temporary outline for selected paths"), "/tools/nodes/pathflash_selected", false); + _page_node.add_line( true, "", _t_node_pathflash_selected, "", _("Show temporary outline even when a path is selected for editing")); _t_node_pathflash_timeout.init("/tools/nodes/pathflash_timeout", 0, 10000.0, 100.0, 100.0, 1000.0, true, false); _page_node.add_line( false, _("Flash time"), _t_node_pathflash_timeout, "ms", _("Specifies how long the path outline will be visible after a mouse-over (in milliseconds). Specify 0 to have the outline shown until mouse leaves the path."), false); + _page_node.add_group_header(_("Editing preferences")); + _t_node_single_node_transform_handles.init(_("Show transform handles for single nodes"), "/tools/nodes/single_node_transform_handles", false); + _page_node.add_line( true, "", _t_node_single_node_transform_handles, "", _("Show transform handles even when only a single node is selected.")); + _t_node_delete_preserves_shape.init(_("Deleting nodes preserves shape"), "/tools/nodes/delete_preserves_shape", true); + _page_node.add_line( true, "", _t_node_delete_preserves_shape, "", _("Move handles next to deleted nodes to resemble original shape. Hold Ctrl to get the other behavior.")); //Tweak this->AddPage(_page_tweak, _("Tweak"), iter_tools, PREFS_PAGE_TOOLS_TWEAK); @@ -657,6 +670,28 @@ void InkscapePreferences::initPageMasks() _mask_mask_remove.init ( _("Remove clippath/mask object after applying"), "/options/maskobject/remove", true); _page_mask.add_line(true, "", _mask_mask_remove, "", _("After applying, remove the object used as the clipping path or mask from the drawing")); + + _page_mask.add_group_header( _("Before applying clippath/mask:")); + + _mask_grouping_none.init( _("Do not group clipped/masked objects"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_NONE, true, 0); + _mask_grouping_separate.init( _("Enclose every clipped/masked object in its own group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_SEPARATE, false, &_mask_grouping_none); + _mask_grouping_all.init( _("Put all clipped/masked objects into one group"), "/options/maskobject/grouping", PREFS_MASKOBJECT_GROUPING_ALL, false, &_mask_grouping_none); + + _page_mask.add_line(true, "", _mask_grouping_none, "", + _("Apply clippath/mask to every object")); + + _page_mask.add_line(true, "", _mask_grouping_separate, "", + _("Apply clippath/mask to groups containing single object")); + + _page_mask.add_line(true, "", _mask_grouping_all, "", + _("Apply clippath/mask to group containing all objects")); + + _page_mask.add_group_header( _("After releasing clippath/mask:")); + + _mask_ungrouping.init ( _("Ungroup automatically created groups"), "/options/maskobject/ungrouping", true); + _page_mask.add_line(true, "", _mask_ungrouping, "", + _("Ungroup groups created when setting clip/mask")); + this->AddPage(_page_mask, _("Clippaths and masks"), PREFS_PAGE_MASKS); } @@ -987,7 +1022,7 @@ void InkscapePreferences::initPageGrids() _page_grids.add_line( false, "", _grids_notebook, "", "", false); _grids_notebook.append_page(_grids_xy, CanvasGrid::getName( GRID_RECTANGULAR )); _grids_notebook.append_page(_grids_axonom, CanvasGrid::getName( GRID_AXONOMETRIC )); - _grids_xy_units.init("/options/grids/units"); + _grids_xy_units.init("/options/grids/xy/units"); _grids_xy.add_line( false, _("Grid units:"), _grids_xy_units, "", "", false); _grids_xy_origin_x.init("/options/grids/xy/origin_x", -10000.0, 10000.0, 0.1, 1.0, 0.0, false, false); _grids_xy_origin_y.init("/options/grids/xy/origin_y", -10000.0, 10000.0, 0.1, 1.0, 0.0, false, false); @@ -1069,7 +1104,7 @@ void InkscapePreferences::initPageUI() _("Chinese/Taiwan (zh_TW)"), _("Croatian (hr)"), _("Czech (cs)"), _("Danish (da)"), _("Dutch (nl)"), _("Dzongkha (dz)"), _("German (de)"), _("Greek (el)"), _("English (en)"), _("English/Australia (en_AU)"), _("English/Canada (en_CA)"), _("English/Great Britain (en_GB)"), _("Pig Latin (en_US@piglatin)"), - _("Esperanto (eo)"), _("Estonian (et)"), _("Finnish (fi)"), + _("Esperanto (eo)"), _("Estonian (et)"), _("Farsi (fa)"), _("Finnish (fi)"), _("French (fr)"), _("Irish (ga)"), _("Galician (gl)"), _("Hebrew (he)"), _("Hungarian (hu)"), _("Indonesian (id)"), _("Italian (it)"), _("Japanese (ja)"), _("Khmer (km)"), _("Kinyarwanda (rw)"), _("Korean (ko)"), _("Lithuanian (lt)"), _("Macedonian (mk)"), _("Mongolian (mn)"), _("Nepali (ne)"), _("Norwegian BokmÃ¥l (nb)"), _("Norwegian Nynorsk (nn)"), _("Panjabi (pa)"), @@ -1077,7 +1112,7 @@ void InkscapePreferences::initPageUI() _("Serbian (sr)"), _("Serbian in Latin script (sr@latin)"), _("Slovak (sk)"), _("Slovenian (sl)"), _("Spanish (es)"), _("Spanish/Mexico (es_MX)"), _("Swedish (sv)"), _("Thai (th)"), _("Turkish (tr)"), _("Ukrainian (uk)"), _("Vietnamese (vi)")}; Glib::ustring langValues[] = {"", "sq", "am", "ar", "hy", "az", "eu", "be", "bg", "bn", "br", "ca", "ca@valencia", "zh_CN", "zh_TW", "hr", "cs", "da", "nl", - "dz", "de", "el", "en", "en_AU", "en_CA", "en_GB", "en_US@piglatin", "eo", "et", "fi", "fr", "ga", + "dz", "de", "el", "en", "en_AU", "en_CA", "en_GB", "en_US@piglatin", "eo", "et", "fa", "fi", "fr", "ga", "gl", "he", "hu", "id", "it", "ja", "km", "rw", "ko", "lt", "mk", "mn", "ne", "nb", "nn", "pa", "pl", "pt", "pt_BR", "ro", "ru", "sr", "sr@latin", "sk", "sl", "es", "es_MX", "sv", "th", "tr", "uk", "vi" }; @@ -1121,6 +1156,12 @@ void InkscapePreferences::initPageUI() _page_ui.add_line( false, _("Zoom correction factor (in %):"), _ui_zoom_correction, "", _("Adjust the slider until the length of the ruler on your screen matches its real length. This information is used when zooming to 1:1, 1:2, etc., to display objects in their true sizes"), true); + + _ui_partialdynamic.init( _("Enable dynamic relayout for incomplete sections."), "/options/workarounds/dynamicnotdone", false); + _page_ui.add_line( false, "", _ui_partialdynamic, "", + _("When on, will allow dynamic layout of components that are not completely finished being refactored."), true); + + this->AddPage(_page_ui, _("Interface"), PREFS_PAGE_UI); } diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 16e62df59..0ba8c965d 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -141,9 +141,15 @@ protected: PrefRadioButton _t_sel_trans_obj, _t_sel_trans_outl, _t_sel_cue_none, _t_sel_cue_mark, _t_sel_cue_box, _t_bbox_visual, _t_bbox_geometric; PrefCheckButton _t_cvg_keep_objects, _t_cvg_convert_whole_groups; + PrefCheckButton _t_node_show_outline; + PrefCheckButton _t_node_live_outline; + PrefCheckButton _t_node_live_objects; PrefCheckButton _t_node_pathflash_enabled; - PrefCheckButton _t_node_pathflash_unselected; + PrefCheckButton _t_node_pathflash_selected; PrefSpinButton _t_node_pathflash_timeout; + PrefCheckButton _t_node_show_path_direction; + PrefCheckButton _t_node_single_node_transform_handles; + PrefCheckButton _t_node_delete_preserves_shape; PrefColorPicker _t_node_pathoutline_color; PrefRadioButton _win_dockable, _win_floating; @@ -167,6 +173,8 @@ protected: PrefCheckButton _mask_mask_on_top; PrefCheckButton _mask_mask_remove; + PrefRadioButton _mask_grouping_none, _mask_grouping_separate, _mask_grouping_all; + PrefCheckButton _mask_ungrouping; PrefRadioButton _blur_quality_best, _blur_quality_better, _blur_quality_normal, _blur_quality_worse, _blur_quality_worst; PrefRadioButton _filter_quality_best, _filter_quality_better, _filter_quality_normal, _filter_quality_worse, _filter_quality_worst; @@ -197,6 +205,7 @@ protected: PrefCombo _misc_small_tools; PrefCheckButton _ui_colorsliders_top; PrefSpinButton _misc_recent; + PrefCheckButton _ui_partialdynamic; ZoomCorrRulerSlider _ui_zoom_correction; //Spellcheck diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp index a06b6c9b6..98bf236fc 100644 --- a/src/ui/dialog/layers.cpp +++ b/src/ui/dialog/layers.cpp @@ -304,7 +304,7 @@ bool LayersPanel::_checkForUpdated(const Gtk::TreePath &/*path*/, const Gtk::Tre Glib::ustring tmp = row[_model->_colLabel]; if ( layer == row[_model->_colObject] ) { - row[_model->_colLabel] = layer->label() ? layer->label() : SP_OBJECT_ID(layer); + row[_model->_colLabel] = layer->label() ? layer->label() : layer->getId(); row[_model->_colVisible] = SP_IS_ITEM(layer) ? !SP_ITEM(layer)->isHidden() : false; row[_model->_colLocked] = SP_IS_ITEM(layer) ? SP_ITEM(layer)->isLocked() : false; @@ -381,7 +381,7 @@ void LayersPanel::_addLayer( SPDocument* doc, SPObject* layer, Gtk::TreeModel::R Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend(); Gtk::TreeModel::Row row = *iter; row[_model->_colObject] = child; - row[_model->_colLabel] = child->label() ? child->label() : SP_OBJECT_ID(child); + row[_model->_colLabel] = child->label() ? child->label() : child->getId(); row[_model->_colVisible] = SP_IS_ITEM(child) ? !SP_ITEM(child)->isHidden() : false; row[_model->_colLocked] = SP_IS_ITEM(child) ? SP_ITEM(child)->isLocked() : false; diff --git a/src/ui/dialog/spray-option.cpp b/src/ui/dialog/spray-option.cpp deleted file mode 100644 index a9e037381..000000000 --- a/src/ui/dialog/spray-option.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/*Julien LERAY (julien.leray@ecl2010.ec-lyon.fr), interface for the spray tool*/ - - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <gtkmm/spinbutton.h> - -#include "desktop-handles.h" -#include "unclump.h" -#include "document.h" -#include "enums.h" -#include "graphlayout/graphlayout.h" -#include "inkscape.h" -#include "macros.h" -#include "node-context.h" -#include "preferences.h" -#include "removeoverlap/removeoverlap.h" -#include "selection.h" -#include "shape-editor.h" -#include "sp-flowtext.h" -#include "sp-item-transform.h" -#include "sp-text.h" -#include "text-editing.h" -#include "tools-switch.h" -#include "ui/icon-names.h" -#include "util/glib-list-iterators.h" -#include "verbs.h" -#include "widgets/icon.h" - -#include "spray-option.h" - -namespace Inkscape { -namespace UI { -namespace Dialog { - - -//Classes qui permettent de créer les environnements Gaussienne, Witdh... - - - -class Action { -public: - Action(const Glib::ustring &id, - const Glib::ustring &/*tiptext*/, - guint /*row*/, - guint /*column*/, - Gtk::Table &parent, - Gtk::Tooltips &/*tooltips*/, - SprayOptionClass &dialog): - _dialog(dialog), - _id(id), - _parent(parent) {} - - virtual ~Action(){} - virtual void on_button_click(){} - SprayOptionClass &_dialog; - -private : - - Glib::ustring _id; - Gtk::Table &_parent; -}; - -class ActionE : public Action { -private: - Gtk::Label _Label; - Gtk::SpinButton _Gap; - guint _min, _max; - Glib::ustring _pref_path; - -public: - ActionE(const Glib::ustring &id, - const Glib::ustring &tiptext, - guint row, guint column, - SprayOptionClass &dialog, - guint min, guint max, - Glib::ustring const &pref_path ): - Action(id, tiptext, row, column, - dialog._Table(), dialog.tooltips(), dialog), - _min(min), - _max(max), - _pref_path(pref_path) - { - dialog._Table().set_col_spacings(3); - - double increm = ((double)_max - (double)_min)/10; - double val_ini = ((double)_max + (double)_min)/2; - _Gap.set_digits(1); - _Gap.set_size_request(60, -1); - _Gap.set_increments(increm , 0); - _Gap.set_range(_min, _max); - _Gap.set_value(val_ini); - dialog.tooltips().set_tip(_Gap, - tiptext); - _Gap.signal_changed().connect(sigc::mem_fun(*this, &ActionE::on_button_click)); //rajout douteux - _Label.set_label(id); - - dialog._Table().attach(_Label, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL); - dialog._Table().attach(_Gap, column+1, column+2, row, row+1, Gtk::EXPAND, Gtk::EXPAND); - } - - virtual void on_button_click(){ - if (!_dialog.getDesktop()) return; - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - prefs->setDouble(_pref_path, SP_VERB_CONTEXT_SPRAY); - - double const Gap = _Gap.get_value(); - - - prefs->setDouble(_pref_path, Gap); - - sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_CONTEXT_SPRAY, - _("Remove overlaps")); - } - - -}; - -class ActionF : public Action { -private: - Gtk::Label _Label; - Gtk::Label _Label1; - Gtk::Label _Label2; - Gtk::SpinButton _Gap1; - Gtk::SpinButton _Gap2; - Glib::ustring _pref1_path; - Glib::ustring _pref2_path; - -public: - ActionF(const Glib::ustring &id, - const Glib::ustring &tiptext, - guint row, guint column, - SprayOptionClass &dialog, - Glib::ustring const &pref1_path, - Glib::ustring const &pref2_path ): - Action(id, tiptext, row, column, - dialog._Table(), dialog.tooltips(), dialog), - _pref1_path(pref1_path), - _pref2_path(pref2_path) - { - dialog.F_Table().set_col_spacings(3); - - _Label.set_label(id); - - _Gap1.set_digits(1); - _Gap1.set_size_request(60, -1); - _Gap1.set_increments(0.1, 0); - _Gap1.set_range(0, 10); - _Gap1.set_value(1); - dialog.tooltips().set_tip(_Gap1, - _("Minimum")); - - _Label1.set_label(Q_("Min")); - - _Gap2.set_digits(1); - _Gap2.set_size_request(60, -1); - _Gap2.set_increments(0.1, 0); - _Gap2.set_range(0, 10); - _Gap2.set_value(1); - dialog.tooltips().set_tip(_Gap2, - _("Maximum")); - - _Label2.set_label(_("Max:")); - - _Gap1.signal_changed().connect(sigc::mem_fun(*this, &ActionF::on_button_click)); - _Gap2.signal_changed().connect(sigc::mem_fun(*this, &ActionF::on_button_click)); - - dialog.F_Table().attach(_Label, column, column+1, row, row+1, Gtk::FILL, Gtk::FILL); - dialog.F_Table().attach(_Label1, column+1, column+2, row, row+1, Gtk::FILL, Gtk::FILL); - dialog.F_Table().attach(_Gap1, column+2, column+3, row, row+1, Gtk::EXPAND, Gtk::EXPAND); - dialog.F_Table().attach(_Label2, column+3, column+4, row, row+1, Gtk::FILL, Gtk::FILL); - dialog.F_Table().attach(_Gap2, column+4, column+5, row, row+1, Gtk::EXPAND, Gtk::EXPAND); - - } - - virtual void on_button_click(){ - if (!_dialog.getDesktop()) return; - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - prefs->setDouble(_pref1_path, SP_VERB_CONTEXT_SPRAY); - prefs->setDouble(_pref2_path, SP_VERB_CONTEXT_SPRAY); - - double const Gap1 = _Gap1.get_value(); - double const Gap2 = _Gap2.get_value(); - - prefs->setDouble(_pref1_path, Gap1); - prefs->setDouble(_pref2_path, Gap2); - - sp_document_done(sp_desktop_document(_dialog.getDesktop()), SP_VERB_CONTEXT_SPRAY, - _("Remove overlaps")); - } - - -}; - - - -void SprayOptionClass::combo_action() { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - cout<<"combo.get_active_row_number = "<<_combo.get_active_row_number()<<endl; - - int const distrib = _combo.get_active_row_number(); - - prefs->setInt("/tools/spray/distribution", distrib); - - - sp_document_done(sp_desktop_document(this->getDesktop()), SP_VERB_CONTEXT_SPRAY, - _("Remove overlaps")); - -} - - - - -void SprayOptionClass::action() { - for (list<Action *>::iterator it = _actionList.begin(); it != _actionList.end(); ++it) { - (*it)->on_button_click(); - } - combo_action(); -} - - - - - - -void on_selection_changed(Inkscape::Application */*inkscape*/, Inkscape::Selection */*selection*/, SprayOptionClass *daad) -{ - daad->randomize_bbox = Geom::OptRect(); -} - -///////////////////////////////////////////////////////// -//Construction de l'interface -///////////////////////////////////////////////////////// - - -SprayOptionClass::SprayOptionClass() - : UI::Widget::Panel ("", "/dialogs/spray", SP_VERB_DIALOG_SPRAY_OPTION), - _actionList(), - _distributionFrame(Q_("sprayOptions|Distribution")), - _Frame(Q_("sprayOptions|Cursor Options")), - _FFrame(Q_("sprayOptions|Random Options")), - _distributionTable(), - _gaussianTable(1, 5, false), - _ETable(3,2,false), - _FTable(2,5,false), - _anchorBox(), - _unifBox(), - _gaussianBox(), - _HBox(), - _FHBox(), - _BoutonBox(), - _distributionBox(), - _VBox(), - _FVBox(), - _ActionBox(), - _anchorLabel(Q_("sprayOptions|Distribution:")), - _unifLabel(Q_("sprayOptions|Uniform")), - _gaussLabel(Q_("sprayOptions|Gaussian")), - _Label(), - _FLabel(), - _unif(), - _gauss(), - _combo(), - _tooltips() -{ - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - //ComboBoxText - - _combo.append_text(Q_("sprayOptions|Uniform")); - _combo.append_text(Q_("sprayOptions|Gaussian")); - - _combo.set_active(prefs->getInt("/tools/spray/distribution", 1)); - _combo.signal_changed().connect(sigc::mem_fun(*this, &SprayOptionClass::combo_action)); - - _anchorBox.pack_start(_anchorLabel); - _anchorBox.pack_start(_combo); - - _gaussianBox.pack_start(_anchorBox); - - - _distributionBox.pack_start(_gaussianBox); - _distributionFrame.add(_distributionBox); - - - //Hbox Random - addFButton(Q_("sprayOptions|Scale:") ,_("Apply a scale factor"), 0, 0, "/tools/spray/scale_min","/tools/spray/scale_max"); - addFButton(Q_("sprayOptions|Rotation:") ,_("Apply rotation"), 1, 0, "/tools/spray/rot_min","/tools/spray/rot_max"); - _FHBox.pack_start(_FLabel); - _FHBox.pack_start(_FTable); - - //Implementation dans la Vbox Cursor - _FVBox.pack_start(_FHBox); - _FFrame.add(_FVBox); - - //Hbox Cursor - addEButton(Q_("sprayOptions|Ratio:") ,_("Eccentricity of the ellipse"), 0, 0, 0, 1,"/tools/spray/ratio"); - addEButton(Q_("sprayOptions|Angle:") ,_("Angle of the ellipse"), 1, 0, 0, 5,"/tools/spray/tilt"); - addEButton(Q_("sprayOptions|Width:") ,_("Size of the ellipse"), 2, 0, 0, 1,"/tools/spray/width"); - _HBox.pack_start(_Label); - _HBox.pack_start(_ETable); - - //Implementation dans la Vbox Cursor - _VBox.pack_start(_HBox); - _Frame.add(_VBox); - - Gtk::Box *contents = _getContents(); - contents->set_spacing(4); - - - - - - - // Crée dans l'ordre suivant les différentes Frames (cadres de réglages) - - contents->pack_start(_distributionFrame, true, true); - contents->pack_start(_FFrame, true, true); - contents->pack_start(_Frame, true, true); - - - - // Connect to the global selection change, to invalidate cached randomize_bbox - g_signal_connect (G_OBJECT (INKSCAPE), "change_selection", G_CALLBACK (on_selection_changed), this); - randomize_bbox = Geom::OptRect(); - - show_all_children(); - - - -} - -SprayOptionClass::~SprayOptionClass() -{ - sp_signal_disconnect_by_data (G_OBJECT (INKSCAPE), this); - - for (std::list<Action *>::iterator it = _actionList.begin(); - it != _actionList.end(); - it ++) - delete *it; -} - - - - - - - -//Fonctions qui lient la demande d'ajout d'une interface graphique à l'action correspondante - -void SprayOptionClass::addEButton(const Glib::ustring &id, - const Glib::ustring &tiptext, - guint row, guint column, - guint min, guint max, - Glib::ustring const &pref_path) -{ - _actionList.push_back( new ActionE(id, tiptext,row, column,*this,min ,max, pref_path )); -} - -void SprayOptionClass::addFButton(const Glib::ustring &id, - const Glib::ustring &tiptext, - guint row, guint column, - Glib::ustring const &pref1_path, - Glib::ustring const &pref2_path) -{ - _actionList.push_back( new ActionF(id, tiptext,row, column,*this,pref1_path, pref2_path )); -} - - - - - -SprayOptionClass &SprayOptionClass::get_SprayOptionClass() -{ - return *this; -} - -} // namespace Dialog -} // namespace UI -} // namespace Inkscape - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/spray-option.h b/src/ui/dialog/spray-option.h deleted file mode 100644 index 42090a120..000000000 --- a/src/ui/dialog/spray-option.h +++ /dev/null @@ -1,145 +0,0 @@ - -/*Julien LERAY (julien.leray@ecl2010.ec-lyon.fr), interface for the spray tool*/ - -#ifndef INKSCAPE_UI_DIALOG_SPRAY_OPTION_H -#define INKSCAPE_UI_DIALOG_SPRAY_OPTION_H - -#include <gtkmm/notebook.h> -#include <glibmm/i18n.h> - -#include <list> -#include <gtkmm/frame.h> -#include <gtkmm/tooltips.h> -#include <gtkmm/comboboxtext.h> -#include <gtkmm/table.h> -#include <gtkmm/buttonbox.h> -#include <gtkmm/label.h> -#include "libnr/nr-dim2.h" -#include "libnr/nr-rect.h" - - -#include "ui/widget/panel.h" -#include "ui/widget/notebook-page.h" - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <gtkmm/spinbutton.h> -#include "desktop-handles.h" -#include "unclump.h" -#include "document.h" -#include "enums.h" -#include "graphlayout/graphlayout.h" -#include "inkscape.h" -#include "macros.h" -#include "node-context.h" -#include "preferences.h" -#include "removeoverlap/removeoverlap.h" -#include "selection.h" -#include "shape-editor.h" -#include "sp-flowtext.h" -#include "sp-item-transform.h" -#include "sp-text.h" -#include "text-editing.h" -#include "tools-switch.h" -#include "ui/icon-names.h" -#include "util/glib-list-iterators.h" -#include "verbs.h" -#include "widgets/icon.h" - -#include "spray-context.h" -#include "verbs.h" - -#include <iostream> -using namespace std; - -using namespace Inkscape::UI::Widget; - -class SPItem; - - -namespace Inkscape { -namespace UI { -namespace Dialog { - -class Action; - -class SprayOptionClass : public Widget::Panel { - -private: - - SprayOptionClass(SprayOptionClass const &d); - SprayOptionClass& operator=(SprayOptionClass const &d); - -public: - SprayOptionClass(); - virtual ~SprayOptionClass(); - void test() { cout<<"appel de test !!"<<endl; } - static SprayOptionClass &getInstance() { return *new SprayOptionClass(); } - - - Gtk::Table &_Table(){return _ETable;} - Gtk::Table &F_Table(){return _FTable;} - Gtk::Tooltips &tooltips(){return _tooltips;} - void action(); - void combo_action(); - Geom::OptRect randomize_bbox; - - SprayOptionClass &get_SprayOptionClass(); - -protected: - - void addGaussianButton(guint row, guint col); - void addEButton(const Glib::ustring &id, const Glib::ustring &tiptext, guint row, guint column, - guint min, guint max, const Glib::ustring &pref_path); - void addFButton(const Glib::ustring &id, const Glib::ustring &tiptext, guint row, guint column, - const Glib::ustring &pref1_path, const Glib::ustring &pref2_path); - - std::list<Action *> _actionList; - Gtk::Frame _distributionFrame; - Gtk::Frame _Frame; - Gtk::Frame _FFrame; - Gtk::Table _distributionTable; - Gtk::Table _gaussianTable; - Gtk::Table _ETable; - Gtk::Table _FTable; - Gtk::HBox _anchorBox; - Gtk::HBox _unifBox; - Gtk::HBox _gaussianBox; - Gtk::HBox _HBox; - Gtk::HBox _FHBox; - Gtk::HBox _BoutonBox; - Gtk::VBox _distributionBox; - Gtk::VBox _VBox; - Gtk::VBox _FVBox; - Gtk::VBox _ActionBox; - Gtk::Label _anchorLabel; - Gtk::Label _unifLabel; - Gtk::Label _gaussLabel; - Gtk::Label _Label; - Gtk::Label _FLabel; - Gtk::CheckButton _unif; - Gtk::CheckButton _gauss; - Gtk::ComboBoxText _combo; - Gtk::Tooltips _tooltips; -}; - - -} // namespace Dialog -} // namespace UI -} // namespace Inkscape - -#endif // INKSCAPE_UI_DIALOG_ALIGN_AND_DISTRIBUTE_H - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : - diff --git a/src/ui/dialog/svg-fonts-dialog.cpp b/src/ui/dialog/svg-fonts-dialog.cpp index cb22e029b..998f4e1e1 100644 --- a/src/ui/dialog/svg-fonts-dialog.cpp +++ b/src/ui/dialog/svg-fonts-dialog.cpp @@ -252,7 +252,7 @@ void SvgFontsDialog::update_fonts() row[_columns.spfont] = f; row[_columns.svgfont] = new SvgFont(f); const gchar* lbl = f->label(); - const gchar* id = SP_OBJECT_ID(f); + const gchar* id = f->getId(); row[_columns.label] = lbl ? lbl : (id ? id : "font"); } @@ -653,7 +653,7 @@ Gtk::VBox* SvgFontsDialog::glyphs_tab(){ missing_glyph_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::missing_glyph_description_from_selected_path)); missing_glyph_reset_button.set_label(_("Reset")); missing_glyph_reset_button.signal_clicked().connect(sigc::mem_fun(*this, &SvgFontsDialog::reset_missing_glyph_description)); - + glyphs_vbox.pack_start(*missing_glyph_hbox, false,false); glyphs_vbox.add(_GlyphsListScroller); diff --git a/src/ui/dialog/swatches.cpp b/src/ui/dialog/swatches.cpp index 450d4202d..6f013f4f3 100644 --- a/src/ui/dialog/swatches.cpp +++ b/src/ui/dialog/swatches.cpp @@ -1,3 +1,4 @@ + /** @file * @brief Color swatches dialog */ @@ -22,6 +23,7 @@ #include <glibmm/i18n.h> #include <gdkmm/pixbuf.h> +#include "color-item.h" #include "desktop.h" #include "desktop-handles.h" #include "desktop-style.h" @@ -36,278 +38,36 @@ #include "path-prefix.h" #include "preferences.h" #include "sp-item.h" -#include "svg/svg-color.h" #include "sp-gradient-fns.h" #include "sp-gradient.h" #include "sp-gradient-vector.h" #include "swatches.h" #include "style.h" +#include "ui/previewholder.h" #include "widgets/gradient-vector.h" #include "widgets/eek-preview.h" #include "display/nr-plain-stuff.h" #include "sp-gradient-reference.h" -#define USE_DOCUMENT_PALETTE 1 namespace Inkscape { namespace UI { namespace Dialogs { #define VBLOCK 16 +#define PREVIEW_PIXBUF_WIDTH 128 void _loadPaletteFile( gchar const *filename ); -/** - * The color swatch you see on screen as a clickable box. - */ -class ColorItem : public Inkscape::UI::Previewable -{ - friend void _loadPaletteFile( gchar const *filename ); -public: - ColorItem( ege::PaintDef::ColorType type ); - ColorItem( unsigned int r, unsigned int g, unsigned int b, - Glib::ustring& name ); - virtual ~ColorItem(); - ColorItem(ColorItem const &other); - virtual ColorItem &operator=(ColorItem const &other); - virtual Gtk::Widget* getPreview(PreviewStyle style, - ViewType view, - ::PreviewSize size, - guint ratio); - void buttonClicked(bool secondary = false); - - void setState( bool fill, bool stroke ); - bool isFill() { return _isFill; } - bool isStroke() { return _isStroke; } - - ege::PaintDef def; - void* ptr; - -private: - static void _dropDataIn( GtkWidget *widget, - GdkDragContext *drag_context, - gint x, gint y, - GtkSelectionData *data, - guint info, - guint event_time, - gpointer user_data); - - static void _dragGetColorData( GtkWidget *widget, - GdkDragContext *drag_context, - GtkSelectionData *data, - guint info, - guint time, - gpointer user_data); - - static void _wireMagicColors( void* p ); - static void _colorDefChanged(void* data); - - void _linkTint( ColorItem& other, int percent ); - void _linkTone( ColorItem& other, int percent, int grayLevel ); - - Gtk::Tooltips tips; - std::vector<Gtk::Widget*> _previews; - - bool _isFill; - bool _isStroke; - bool _isLive; - bool _linkIsTone; - int _linkPercent; - int _linkGray; - ColorItem* _linkSrc; - std::vector<ColorItem*> _listeners; -}; - - - -ColorItem::ColorItem(ege::PaintDef::ColorType type) : - def(type), - ptr(0), - _isFill(false), - _isStroke(false), - _isLive(false), - _linkIsTone(false), - _linkPercent(0), - _linkGray(0), - _linkSrc(0) -{ -} - -ColorItem::ColorItem( unsigned int r, unsigned int g, unsigned int b, Glib::ustring& name ) : - def( r, g, b, name ), - ptr(0), - _isFill(false), - _isStroke(false), - _isLive(false), - _linkIsTone(false), - _linkPercent(0), - _linkGray(0), - _linkSrc(0) -{ -} - -ColorItem::~ColorItem() -{ -} - -ColorItem::ColorItem(ColorItem const &other) : - Inkscape::UI::Previewable() -{ - if ( this != &other ) { - *this = other; - } -} - -ColorItem &ColorItem::operator=(ColorItem const &other) -{ - if ( this != &other ) { - def = other.def; - - // TODO - correct linkage - _linkSrc = other._linkSrc; - g_message("Erk!"); - } - return *this; -} - -void ColorItem::setState( bool fill, bool stroke ) -{ - if ( (_isFill != fill) || (_isStroke != stroke) ) { - _isFill = fill; - _isStroke = stroke; - - for ( std::vector<Gtk::Widget*>::iterator it = _previews.begin(); it != _previews.end(); ++it ) { - Gtk::Widget* widget = *it; - if ( IS_EEK_PREVIEW(widget->gobj()) ) { - EekPreview * preview = EEK_PREVIEW(widget->gobj()); - - int val = eek_preview_get_linked( preview ); - val &= ~(PREVIEW_FILL | PREVIEW_STROKE); - if ( _isFill ) { - val |= PREVIEW_FILL; - } - if ( _isStroke ) { - val |= PREVIEW_STROKE; - } - eek_preview_set_linked( preview, static_cast<LinkType>(val) ); - } - } - } -} - - -class JustForNow -{ -public: - JustForNow() : _prefWidth(0) {} - - Glib::ustring _name; - int _prefWidth; - std::vector<ColorItem*> _colors; -}; - -static std::vector<JustForNow*> possible; +class DocTrack; -static std::vector<std::string> mimeStrings; -static std::map<std::string, guint> mimeToInt; +std::vector<SwatchPage*> possible; +static std::map<SPDocument*, SwatchPage*> docPalettes; +static std::vector<DocTrack*> docTrackings; +static std::map<SwatchesPanel*, SPDocument*> docPerPanel; -static std::map<ColorItem*, guchar*> previewMap; -static std::map<ColorItem*, SPGradient*> gradMap; // very temporary workaround. -void ColorItem::_dragGetColorData( GtkWidget */*widget*/, - GdkDragContext */*drag_context*/, - GtkSelectionData *data, - guint info, - guint /*time*/, - gpointer user_data) -{ - ColorItem* item = reinterpret_cast<ColorItem*>(user_data); - std::string key; - if ( info < mimeStrings.size() ) { - key = mimeStrings[info]; - } else { - g_warning("ERROR: unknown value (%d)", info); - } - - if ( !key.empty() ) { - char* tmp = 0; - int len = 0; - int format = 0; - item->def.getMIMEData(key, tmp, len, format); - if ( tmp ) { - GdkAtom dataAtom = gdk_atom_intern( key.c_str(), FALSE ); - gtk_selection_data_set( data, dataAtom, format, (guchar*)tmp, len ); - delete[] tmp; - } - } -} - -static void dragBegin( GtkWidget */*widget*/, GdkDragContext* dc, gpointer data ) -{ - ColorItem* item = reinterpret_cast<ColorItem*>(data); - if ( item ) - { - using Inkscape::IO::Resource::get_path; - using Inkscape::IO::Resource::ICONS; - using Inkscape::IO::Resource::SYSTEM; - int width = 32; - int height = 24; - - if (item->def.getType() != ege::PaintDef::RGB){ - GError *error = NULL; - gsize bytesRead = 0; - gsize bytesWritten = 0; - gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"), - -1, - &bytesRead, - &bytesWritten, - &error); - GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_scale(localFilename, width, height, FALSE, &error); - g_free(localFilename); - gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 ); - } else { - GdkPixbuf* pixbuf = 0; - if ( gradMap.find(item) == gradMap.end() ){ - Glib::RefPtr<Gdk::Pixbuf> thumb = Gdk::Pixbuf::create( Gdk::COLORSPACE_RGB, false, 8, width, height ); - guint32 fillWith = (0xff000000 & (item->def.getR() << 24)) - | (0x00ff0000 & (item->def.getG() << 16)) - | (0x0000ff00 & (item->def.getB() << 8)); - thumb->fill( fillWith ); - pixbuf = thumb->gobj(); - } else { - SPGradient* grad = gradMap[item]; - - guchar* px = g_new( guchar, 3 * height * width ); - nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 ); - - sp_gradient_render_vector_block_rgb( grad, - px, width, height, 3 * width, - 0, width, TRUE ); - - pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8, - width, height, width * 3, - 0, // add delete function - 0 ); - } - gtk_drag_set_icon_pixbuf( dc, pixbuf, 0, 0 ); - } - } - -} - -//"drag-drop" -// gboolean dragDropColorData( GtkWidget *widget, -// GdkDragContext *drag_context, -// gint x, -// gint y, -// guint time, -// gpointer user_data) -// { -// // TODO finish - -// return TRUE; -// } static void handleClick( GtkWidget* /*widget*/, gpointer callback_data ) { ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); @@ -323,34 +83,10 @@ static void handleSecondaryClick( GtkWidget* /*widget*/, gint /*arg1*/, gpointer } } -static gboolean handleEnterNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) { - ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); - if ( item ) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop ) { - gchar* msg = g_strdup_printf(_("Color: <b>%s</b>; <b>Click</b> to set fill, <b>Shift+click</b> to set stroke"), - item->def.descr.c_str()); - desktop->tipsMessageContext()->set(Inkscape::INFORMATION_MESSAGE, msg); - g_free(msg); - } - } - return FALSE; -} - -static gboolean handleLeaveNotify( GtkWidget* /*widget*/, GdkEventCrossing* /*event*/, gpointer callback_data ) { - ColorItem* item = reinterpret_cast<ColorItem*>(callback_data); - if ( item ) { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop ) { - desktop->tipsMessageContext()->clear(); - } - } - return FALSE; -} - static GtkWidget* popupMenu = 0; static std::vector<GtkWidget*> popupExtras; static ColorItem* bounceTarget = 0; +static SwatchesPanel* bouncePanel = 0; static void redirClick( GtkMenuItem *menuitem, gpointer /*user_data*/ ) { @@ -366,7 +102,6 @@ static void redirSecondaryClick( GtkMenuItem *menuitem, gpointer /*user_data*/ ) } } -#if USE_DOCUMENT_PALETTE static void editGradientImpl( SPGradient* gr ) { if ( gr ) { @@ -378,7 +113,7 @@ static void editGradientImpl( SPGradient* gr ) static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) { if ( bounceTarget ) { - SwatchesPanel* swp = bounceTarget->ptr ? reinterpret_cast<SwatchesPanel*>(bounceTarget->ptr) : 0; + SwatchesPanel* swp = bouncePanel; SPDesktop* desktop = swp ? swp->getDesktop() : 0; SPDocument *doc = desktop ? desktop->doc() : 0; if (doc) { @@ -386,7 +121,7 @@ static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); - if ( targetName == grad->id ) { + if ( targetName == grad->getId() ) { editGradientImpl( grad ); break; } @@ -398,7 +133,7 @@ static void editGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) { if ( bounceTarget ) { - SwatchesPanel* swp = bounceTarget->ptr ? reinterpret_cast<SwatchesPanel*>(bounceTarget->ptr) : 0; + SwatchesPanel* swp = bouncePanel; SPDesktop* desktop = swp ? swp->getDesktop() : 0; SPDocument *doc = desktop ? desktop->doc() : 0; if (doc) { @@ -420,20 +155,35 @@ static void addNewGradient( GtkMenuItem */*menuitem*/, gpointer /*user_data*/ ) editGradientImpl( gr ); - // Work-around for timing of gradient addition change. Must follow edit. - if ( swp ) { - swp->handleGradientsChange(); - } } } } -#endif // USE_DOCUMENT_PALETTE -static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, gpointer user_data) +static SwatchesPanel* findContainingPanel( GtkWidget *widget ) +{ + SwatchesPanel *swp = 0; + + std::map<GtkWidget*, SwatchesPanel*> rawObjects; + for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) { + rawObjects[GTK_WIDGET(it->first->gobj())] = it->first; + } + + for (GtkWidget* curr = widget; curr && !swp; curr = gtk_widget_get_parent(curr)) { + if (rawObjects.find(curr) != rawObjects.end()) { + swp = rawObjects[curr]; + } + } + + return swp; +} + +gboolean colorItemHandleButtonPress( GtkWidget* widget, GdkEventButton* event, gpointer user_data) { gboolean handled = FALSE; if ( (event->button == 3) && (event->type == GDK_BUTTON_PRESS) ) { + SwatchesPanel* swp = findContainingPanel( widget ); + if ( !popupMenu ) { popupMenu = gtk_menu_new(); GtkWidget* child = 0; @@ -455,7 +205,6 @@ static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, user_data); gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); -#if USE_DOCUMENT_PALETTE child = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); popupExtras.push_back(child); @@ -489,20 +238,19 @@ static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, gtk_menu_shell_append(GTK_MENU_SHELL(popupMenu), child); //popupExtras.push_back(child); gtk_widget_set_sensitive( child, FALSE ); -#endif // USE_DOCUMENT_PALETTE gtk_widget_show_all(popupMenu); } ColorItem* item = reinterpret_cast<ColorItem*>(user_data); if ( item ) { - SwatchesPanel* swp = item->ptr ? reinterpret_cast<SwatchesPanel*>(item->ptr) : 0; bool show = swp && (swp->getSelectedIndex() == 0); for ( std::vector<GtkWidget*>::iterator it = popupExtras.begin(); it != popupExtras.end(); ++ it) { gtk_widget_set_sensitive(*it, show); } bounceTarget = item; + bouncePanel = swp; if ( popupMenu ) { gtk_menu_popup(GTK_MENU(popupMenu), NULL, NULL, NULL, NULL, event->button, event->time); handled = TRUE; @@ -513,374 +261,6 @@ static gboolean handleButtonPress( GtkWidget* /*widget*/, GdkEventButton* event, return handled; } -static void dieDieDie( GtkObject *obj, gpointer user_data ) -{ - g_message("die die die %p %p", obj, user_data ); -} - -#include "color.h" // for SP_RGBA32_U_COMPOSE - -void ColorItem::_dropDataIn( GtkWidget */*widget*/, - GdkDragContext */*drag_context*/, - gint /*x*/, gint /*y*/, - GtkSelectionData */*data*/, - guint /*info*/, - guint /*event_time*/, - gpointer /*user_data*/) -{ -} - -static bool bruteForce( SPDocument* document, Inkscape::XML::Node* node, Glib::ustring const& match, int r, int g, int b ) -{ - bool changed = false; - - if ( node ) { - gchar const * val = node->attribute("inkscape:x-fill-tag"); - if ( val && (match == val) ) { - SPObject *obj = document->getObjectByRepr( node ); - - gchar c[64] = {0}; - sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( r, g, b, 0xff ) ); - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_set_property( css, "fill", c ); - - sp_desktop_apply_css_recursive( (SPItem*)obj, css, true ); - ((SPItem*)obj)->updateRepr(); - - changed = true; - } - - val = node->attribute("inkscape:x-stroke-tag"); - if ( val && (match == val) ) { - SPObject *obj = document->getObjectByRepr( node ); - - gchar c[64] = {0}; - sp_svg_write_color( c, sizeof(c), SP_RGBA32_U_COMPOSE( r, g, b, 0xff ) ); - SPCSSAttr *css = sp_repr_css_attr_new(); - sp_repr_css_set_property( css, "stroke", c ); - - sp_desktop_apply_css_recursive( (SPItem*)obj, css, true ); - ((SPItem*)obj)->updateRepr(); - - changed = true; - } - - Inkscape::XML::Node* first = node->firstChild(); - changed |= bruteForce( document, first, match, r, g, b ); - - changed |= bruteForce( document, node->next(), match, r, g, b ); - } - - return changed; -} - -void ColorItem::_colorDefChanged(void* data) -{ - ColorItem* item = reinterpret_cast<ColorItem*>(data); - if ( item ) { - for ( std::vector<Gtk::Widget*>::iterator it = item->_previews.begin(); it != item->_previews.end(); ++it ) { - Gtk::Widget* widget = *it; - if ( IS_EEK_PREVIEW(widget->gobj()) ) { - EekPreview * preview = EEK_PREVIEW(widget->gobj()); - eek_preview_set_color( preview, - (item->def.getR() << 8) | item->def.getR(), - (item->def.getG() << 8) | item->def.getG(), - (item->def.getB() << 8) | item->def.getB() ); - - eek_preview_set_linked( preview, (LinkType)((item->_linkSrc ? PREVIEW_LINK_IN:0) - | (item->_listeners.empty() ? 0:PREVIEW_LINK_OUT) - | (item->_isLive ? PREVIEW_LINK_OTHER:0)) ); - - widget->queue_draw(); - } - } - - for ( std::vector<ColorItem*>::iterator it = item->_listeners.begin(); it != item->_listeners.end(); ++it ) { - guint r = item->def.getR(); - guint g = item->def.getG(); - guint b = item->def.getB(); - - if ( (*it)->_linkIsTone ) { - r = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * r) ) / 100; - g = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * g) ) / 100; - b = ( ((*it)->_linkPercent * (*it)->_linkGray) + ((100 - (*it)->_linkPercent) * b) ) / 100; - } else { - r = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * r) ) / 100; - g = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * g) ) / 100; - b = ( ((*it)->_linkPercent * 255) + ((100 - (*it)->_linkPercent) * b) ) / 100; - } - - (*it)->def.setRGB( r, g, b ); - } - - - // Look for objects using this color - { - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if ( desktop ) { - SPDocument* document = sp_desktop_document( desktop ); - Inkscape::XML::Node *rroot = sp_document_repr_root( document ); - if ( rroot ) { - - // Find where this thing came from - Glib::ustring paletteName; - bool found = false; - int index = 0; - for ( std::vector<JustForNow*>::iterator it2 = possible.begin(); it2 != possible.end() && !found; ++it2 ) { - JustForNow* curr = *it2; - index = 0; - for ( std::vector<ColorItem*>::iterator zz = curr->_colors.begin(); zz != curr->_colors.end(); ++zz ) { - if ( item == *zz ) { - found = true; - paletteName = curr->_name; - break; - } else { - index++; - } - } - } - - if ( !paletteName.empty() ) { - gchar* str = g_strdup_printf("%d|", index); - paletteName.insert( 0, str ); - g_free(str); - str = 0; - - if ( bruteForce( document, rroot, paletteName, item->def.getR(), item->def.getG(), item->def.getB() ) ) { - sp_document_done( document , SP_VERB_DIALOG_SWATCHES, - _("Change color definition")); - } - } - } - } - } - } -} - - -Gtk::Widget* ColorItem::getPreview(PreviewStyle style, ViewType view, ::PreviewSize size, guint ratio) -{ - Gtk::Widget* widget = 0; - if ( style == PREVIEW_STYLE_BLURB) { - Gtk::Label *lbl = new Gtk::Label(def.descr); - lbl->set_alignment(Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER); - widget = lbl; - } else { -// Glib::ustring blank(" "); -// if ( size == Inkscape::ICON_SIZE_MENU || size == Inkscape::ICON_SIZE_DECORATION ) { -// blank = " "; -// } - - GtkWidget* eekWidget = eek_preview_new(); - EekPreview * preview = EEK_PREVIEW(eekWidget); - Gtk::Widget* newBlot = Glib::wrap(eekWidget); - - if ( previewMap.find(this) == previewMap.end() ){ - eek_preview_set_color( preview, (def.getR() << 8) | def.getR(), - (def.getG() << 8) | def.getG(), - (def.getB() << 8) | def.getB()); - } else { - guchar* px = previewMap[this]; - int width = 128; - int height = 16; - GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( px, GDK_COLORSPACE_RGB, FALSE, 8, - width, height, width * 3, - 0, // add delete function - 0 ); - eek_preview_set_pixbuf( preview, pixbuf ); - } - if ( def.getType() != ege::PaintDef::RGB ) { - using Inkscape::IO::Resource::get_path; - using Inkscape::IO::Resource::ICONS; - using Inkscape::IO::Resource::SYSTEM; - GError *error = NULL; - gsize bytesRead = 0; - gsize bytesWritten = 0; - gchar *localFilename = g_filename_from_utf8( get_path(SYSTEM, ICONS, "remove-color.png"), - -1, - &bytesRead, - &bytesWritten, - &error); - GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(localFilename, &error); - if (!pixbuf) { - g_warning("Null pixbuf for %p [%s]", localFilename, localFilename ); - } - g_free(localFilename); - - eek_preview_set_pixbuf( preview, pixbuf ); - } - - eek_preview_set_details( preview, (::PreviewStyle)style, (::ViewType)view, (::PreviewSize)size, ratio ); - eek_preview_set_linked( preview, (LinkType)((_linkSrc ? PREVIEW_LINK_IN:0) - | (_listeners.empty() ? 0:PREVIEW_LINK_OUT) - | (_isLive ? PREVIEW_LINK_OTHER:0)) ); - - def.addCallback( _colorDefChanged, this ); - - GValue val = {0, {{0}, {0}}}; - g_value_init( &val, G_TYPE_BOOLEAN ); - g_value_set_boolean( &val, FALSE ); - g_object_set_property( G_OBJECT(preview), "focus-on-click", &val ); - -/* - Gtk::Button *btn = new Gtk::Button(blank); - Gdk::Color color; - color.set_rgb((_r << 8)|_r, (_g << 8)|_g, (_b << 8)|_b); - btn->modify_bg(Gtk::STATE_NORMAL, color); - btn->modify_bg(Gtk::STATE_ACTIVE, color); - btn->modify_bg(Gtk::STATE_PRELIGHT, color); - btn->modify_bg(Gtk::STATE_SELECTED, color); - - Gtk::Widget* newBlot = btn; -*/ - - tips.set_tip((*newBlot), def.descr); - -/* - newBlot->signal_clicked().connect( sigc::mem_fun(*this, &ColorItem::buttonClicked) ); - - sigc::signal<void> type_signal_something; -*/ - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "clicked", - G_CALLBACK(handleClick), - this); - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "alt-clicked", - G_CALLBACK(handleSecondaryClick), - this); - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "button-press-event", - G_CALLBACK(handleButtonPress), - this); - - { - std::vector<std::string> listing = def.getMIMETypes(); - int entryCount = listing.size(); - GtkTargetEntry* entries = new GtkTargetEntry[entryCount]; - GtkTargetEntry* curr = entries; - for ( std::vector<std::string>::iterator it = listing.begin(); it != listing.end(); ++it ) { - curr->target = g_strdup(it->c_str()); - curr->flags = 0; - if ( mimeToInt.find(*it) == mimeToInt.end() ){ - // these next lines are order-dependent: - mimeToInt[*it] = mimeStrings.size(); - mimeStrings.push_back(*it); - } - curr->info = mimeToInt[curr->target]; - curr++; - } - gtk_drag_source_set( GTK_WIDGET(newBlot->gobj()), - GDK_BUTTON1_MASK, - entries, entryCount, - GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY) ); - for ( int i = 0; i < entryCount; i++ ) { - g_free(entries[i].target); - } - delete[] entries; - } - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "drag-data-get", - G_CALLBACK(ColorItem::_dragGetColorData), - this); - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "drag-begin", - G_CALLBACK(dragBegin), - this ); - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "enter-notify-event", - G_CALLBACK(handleEnterNotify), - this); - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "leave-notify-event", - G_CALLBACK(handleLeaveNotify), - this); - -// g_signal_connect( G_OBJECT(newBlot->gobj()), -// "drag-drop", -// G_CALLBACK(dragDropColorData), -// this); - - if ( def.isEditable() ) - { -// gtk_drag_dest_set( GTK_WIDGET(newBlot->gobj()), -// GTK_DEST_DEFAULT_ALL, -// destColorTargets, -// G_N_ELEMENTS(destColorTargets), -// GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE) ); - - -// g_signal_connect( G_OBJECT(newBlot->gobj()), -// "drag-data-received", -// G_CALLBACK(_dropDataIn), -// this ); - } - - g_signal_connect( G_OBJECT(newBlot->gobj()), - "destroy", - G_CALLBACK(dieDieDie), - this); - - - widget = newBlot; - } - - _previews.push_back( widget ); - - return widget; -} - -void ColorItem::buttonClicked(bool secondary) -{ - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop) { - char const * attrName = secondary ? "stroke" : "fill"; - - SPCSSAttr *css = sp_repr_css_attr_new(); - Glib::ustring descr; - switch (def.getType()) { - case ege::PaintDef::CLEAR: { - // TODO actually make this clear - sp_repr_css_set_property( css, attrName, "none" ); - descr = secondary? _("Remove stroke color") : _("Remove fill color"); - break; - } - case ege::PaintDef::NONE: { - sp_repr_css_set_property( css, attrName, "none" ); - descr = secondary? _("Set stroke color to none") : _("Set fill color to none"); - break; - } - case ege::PaintDef::RGB: { - Glib::ustring colorspec; - if ( gradMap.find(this) == gradMap.end() ){ - gchar c[64]; - guint32 rgba = (def.getR() << 24) | (def.getG() << 16) | (def.getB() << 8) | 0xff; - sp_svg_write_color(c, sizeof(c), rgba); - colorspec = c; - } else { - SPGradient* grad = gradMap[this]; - colorspec = "url(#"; - colorspec += grad->id; - colorspec += ")"; - } - sp_repr_css_set_property( css, attrName, colorspec.c_str() ); - descr = secondary? _("Set stroke color from swatch") : _("Set fill color from swatch"); - break; - } - } - sp_desktop_set_style(desktop, css); - sp_repr_css_attr_unref(css); - - sp_document_done( sp_desktop_document(desktop), SP_VERB_DIALOG_SWATCHES, descr.c_str() ); - } -} static char* trim( char* str ) { char* ret = str; @@ -915,149 +295,6 @@ bool parseNum( char*& str, int& val ) { } -static bool getBlock( std::string& dst, guchar ch, std::string const str ) -{ - bool good = false; - std::string::size_type pos = str.find(ch); - if ( pos != std::string::npos ) - { - std::string::size_type pos2 = str.find( '(', pos ); - if ( pos2 != std::string::npos ) { - std::string::size_type endPos = str.find( ')', pos2 ); - if ( endPos != std::string::npos ) { - dst = str.substr( pos2 + 1, (endPos - pos2 - 1) ); - good = true; - } - } - } - return good; -} - -static bool popVal( guint64& numVal, std::string& str ) -{ - bool good = false; - std::string::size_type endPos = str.find(','); - if ( endPos == std::string::npos ) { - endPos = str.length(); - } - - if ( endPos != std::string::npos && endPos > 0 ) { - std::string xxx = str.substr( 0, endPos ); - const gchar* ptr = xxx.c_str(); - gchar* endPtr = 0; - numVal = g_ascii_strtoull( ptr, &endPtr, 10 ); - if ( (numVal == G_MAXUINT64) && (ERANGE == errno) ) { - // overflow - } else if ( (numVal == 0) && (endPtr == ptr) ) { - // failed conversion - } else { - good = true; - str.erase( 0, endPos + 1 ); - } - } - - return good; -} - -void ColorItem::_wireMagicColors( void* p ) -{ - JustForNow* onceMore = reinterpret_cast<JustForNow*>(p); - if ( onceMore ) - { - for ( std::vector<ColorItem*>::iterator it = onceMore->_colors.begin(); it != onceMore->_colors.end(); ++it ) - { - std::string::size_type pos = (*it)->def.descr.find("*{"); - if ( pos != std::string::npos ) - { - std::string subby = (*it)->def.descr.substr( pos + 2 ); - std::string::size_type endPos = subby.find("}*"); - if ( endPos != std::string::npos ) - { - subby.erase( endPos ); - //g_message("FOUND MAGIC at '%s'", (*it)->def.descr.c_str()); - //g_message(" '%s'", subby.c_str()); - - if ( subby.find('E') != std::string::npos ) - { - (*it)->def.setEditable( true ); - } - - if ( subby.find('L') != std::string::npos ) - { - (*it)->_isLive = true; - } - - std::string part; - // Tint. index + 1 more val. - if ( getBlock( part, 'T', subby ) ) { - guint64 colorIndex = 0; - if ( popVal( colorIndex, part ) ) { - guint64 percent = 0; - if ( popVal( percent, part ) ) { - (*it)->_linkTint( *(onceMore->_colors[colorIndex]), percent ); - } - } - } - - // Shade/tone. index + 1 or 2 more val. - if ( getBlock( part, 'S', subby ) ) { - guint64 colorIndex = 0; - if ( popVal( colorIndex, part ) ) { - guint64 percent = 0; - if ( popVal( percent, part ) ) { - guint64 grayLevel = 0; - if ( !popVal( grayLevel, part ) ) { - grayLevel = 0; - } - (*it)->_linkTone( *(onceMore->_colors[colorIndex]), percent, grayLevel ); - } - } - } - - } - } - } - } -} - - -void ColorItem::_linkTint( ColorItem& other, int percent ) -{ - if ( !_linkSrc ) - { - other._listeners.push_back(this); - _linkIsTone = false; - _linkPercent = percent; - if ( _linkPercent > 100 ) - _linkPercent = 100; - if ( _linkPercent < 0 ) - _linkPercent = 0; - _linkGray = 0; - _linkSrc = &other; - - ColorItem::_colorDefChanged(&other); - } -} - -void ColorItem::_linkTone( ColorItem& other, int percent, int grayLevel ) -{ - if ( !_linkSrc ) - { - other._listeners.push_back(this); - _linkIsTone = true; - _linkPercent = percent; - if ( _linkPercent > 100 ) - _linkPercent = 100; - if ( _linkPercent < 0 ) - _linkPercent = 0; - _linkGray = grayLevel; - _linkSrc = &other; - - ColorItem::_colorDefChanged(&other); - } -} - - void _loadPaletteFile( gchar const *filename ) { char block[1024]; @@ -1069,7 +306,7 @@ void _loadPaletteFile( gchar const *filename ) bool inHeader = true; bool hasErr = false; - JustForNow *onceMore = new JustForNow(); + SwatchPage *onceMore = new SwatchPage(); do { result = fgets( block, sizeof(block), f ); @@ -1245,46 +482,45 @@ SwatchesPanel::SwatchesPanel(gchar const* prefsPath) : _remove(0), _currentIndex(0), _currentDesktop(0), - _currentDocument(0), - _ptr(0) + _currentDocument(0) { Gtk::RadioMenuItem* hotItem = 0; _holder = new PreviewHolder(); _clear = new ColorItem( ege::PaintDef::CLEAR ); - _clear->ptr = this; _remove = new ColorItem( ege::PaintDef::NONE ); - _remove->ptr = this; -#if USE_DOCUMENT_PALETTE - { - JustForNow *docPalette = new JustForNow(); + if (docPalettes.empty()) { + SwatchPage *docPalette = new SwatchPage(); docPalette->_name = "Auto"; - possible.push_back(docPalette); - - _ptr = docPalette; + docPalettes[0] = docPalette; } -#endif // USE_DOCUMENT_PALETTE + loadEmUp(); if ( !possible.empty() ) { - JustForNow* first = 0; + SwatchPage* first = 0; int index = 0; Glib::ustring targetName; if ( !_prefs_path.empty() ) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); targetName = prefs->getString(_prefs_path + "/palette"); if (!targetName.empty()) { - for ( std::vector<JustForNow*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) { - if ( (*iter)->_name == targetName ) { - first = *iter; - break; - } + if (targetName == "Auto") { + first = docPalettes[0]; + } else { index++; + for ( std::vector<SwatchPage*>::iterator iter = possible.begin(); iter != possible.end(); ++iter ) { + if ( (*iter)->_name == targetName ) { + first = *iter; + break; + } + index++; + } } } } if ( !first ) { - first = possible.front(); + first = docPalettes[0]; _currentIndex = 0; } else { _currentIndex = index; @@ -1295,8 +531,9 @@ SwatchesPanel::SwatchesPanel(gchar const* prefsPath) : Gtk::RadioMenuItem::Group groupOne; int i = 0; - for ( std::vector<JustForNow*>::iterator it = possible.begin(); it != possible.end(); it++ ) { - JustForNow* curr = *it; + std::vector<SwatchPage*> swatchSets = _getSwatchSets(); + for ( std::vector<SwatchPage*>::iterator it = swatchSets.begin(); it != swatchSets.end(); it++ ) { + SwatchPage* curr = *it; Gtk::RadioMenuItem* single = manage(new Gtk::RadioMenuItem(groupOne, curr->_name)); if ( curr == first ) { hotItem = single; @@ -1320,11 +557,10 @@ SwatchesPanel::SwatchesPanel(gchar const* prefsPath) : SwatchesPanel::~SwatchesPanel() { + _trackDocument( this, 0 ); + _documentConnection.disconnect(); - _resourceConnection.disconnect(); _selChanged.disconnect(); - _setModified.disconnect(); - _subselChanged.disconnect(); if ( _clear ) { delete _clear; @@ -1354,8 +590,6 @@ void SwatchesPanel::setDesktop( SPDesktop* desktop ) if ( _currentDesktop ) { _documentConnection.disconnect(); _selChanged.disconnect(); - _setModified.disconnect(); - _subselChanged.disconnect(); } _currentDesktop = desktop; @@ -1382,104 +616,235 @@ void SwatchesPanel::setDesktop( SPDesktop* desktop ) } } -void SwatchesPanel::_setDocument( SPDocument *document ) + +class DocTrack { - if ( document != _currentDocument ) { - if ( _currentDocument ) { - _resourceConnection.disconnect(); +public: + DocTrack(SPDocument *doc, sigc::connection &gradientRsrcChanged, sigc::connection &defsChanged, sigc::connection &defsModified) : + doc(doc), + gradientRsrcChanged(gradientRsrcChanged), + defsChanged(defsChanged), + defsModified(defsModified) + { + } + + ~DocTrack() + { + if (doc) { + gradientRsrcChanged.disconnect(); + defsChanged.disconnect(); + defsModified.disconnect(); } - _currentDocument = document; - if ( _currentDocument ) { - _resourceConnection = sp_document_resources_changed_connect(document, - "gradient", - sigc::mem_fun(*this, &SwatchesPanel::handleGradientsChange)); + } + + SPDocument *doc; + sigc::connection gradientRsrcChanged; + sigc::connection defsChanged; + sigc::connection defsModified; + +private: + DocTrack(DocTrack const &); // no copy + DocTrack &operator=(DocTrack const &); // no assign +}; + +void SwatchesPanel::_trackDocument( SwatchesPanel *panel, SPDocument *document ) +{ + SPDocument *oldDoc = 0; + if (docPerPanel.find(panel) != docPerPanel.end()) { + oldDoc = docPerPanel[panel]; + if (!oldDoc) { + docPerPanel.erase(panel); // Should not be needed, but clean up just in case. + } + } + if (oldDoc != document) { + if (oldDoc) { + docPerPanel[panel] = 0; + bool found = false; + for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) { + found = (it->second == document); + } + if (!found) { + for (std::vector<DocTrack*>::iterator it = docTrackings.begin(); it != docTrackings.end(); ++it){ + if ((*it)->doc == oldDoc) { + delete *it; + docTrackings.erase(it); + break; + } + } + } + } + + if (document) { + bool found = false; + for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); (it != docPerPanel.end()) && !found; ++it) { + found = (it->second == document); + } + docPerPanel[panel] = document; + if (!found) { + sigc::connection conn1 = sp_document_resources_changed_connect( document, "gradient", sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleGradientsChange), document) ); + sigc::connection conn2 = SP_DOCUMENT_DEFS(document)->connectRelease( sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document)) ); + sigc::connection conn3 = SP_DOCUMENT_DEFS(document)->connectModified( sigc::hide(sigc::hide(sigc::bind(sigc::ptr_fun(&SwatchesPanel::handleDefsModified), document))) ); + + DocTrack *dt = new DocTrack(document, conn1, conn2, conn3); + docTrackings.push_back(dt); + + if (docPalettes.find(document) == docPalettes.end()) { + SwatchPage *docPalette = new SwatchPage(); + docPalette->_name = "Auto"; + docPalettes[document] = docPalette; + } + } } + } - handleGradientsChange(); + std::set<SPDocument*> docs; + for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) { + docs.insert(it->second); } } -void SwatchesPanel::handleGradientsChange() +void SwatchesPanel::_setDocument( SPDocument *document ) +{ + if ( document != _currentDocument ) { + _trackDocument(this, document); + _currentDocument = document; + handleGradientsChange( document ); + } +} + +static void recalcSwatchContents(SPDocument* doc, + std::vector<ColorItem*> &tmpColors, + std::map<ColorItem*, guchar*> &previewMappings, + std::map<ColorItem*, SPGradient*> &gradMappings) { std::vector<SPGradient*> newList; - const GSList *gradients = sp_document_get_resource_list(_currentDocument, "gradient"); + const GSList *gradients = sp_document_get_resource_list(doc, "gradient"); for (const GSList *item = gradients; item; item = item->next) { SPGradient* grad = SP_GRADIENT(item->data); - if ( grad->has_stops ) { + if ( grad->isSwatch() ) { newList.push_back(SP_GRADIENT(item->data)); } } -#if USE_DOCUMENT_PALETTE - if ( _ptr ) { - JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr); - // TODO delete pointed to objects - docPalette->_colors.clear(); - if ( !newList.empty() ) { - for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it ) - { - SPGradient* grad = *it; - if ( grad->repr->attribute("osb:paint") ) { - sp_gradient_ensure_vector( grad ); - SPGradientStop first = grad->vector.stops[0]; - SPColor color = first.color; - guint32 together = color.toRGBA32(first.opacity); - - // At the moment we can't trust the count of 1 vs 2 stops. - SPGradientStop second = (*it)->vector.stops[1]; - SPColor color2 = second.color; - guint32 together2 = color2.toRGBA32(second.opacity); - - if ( (grad->vector.stops.size() <= 2) && (together == together2) ) { - // Treat as solid-color - Glib::ustring name( grad->id ); - unsigned int r = SP_RGBA32_R_U(together); - unsigned int g = SP_RGBA32_G_U(together); - unsigned int b = SP_RGBA32_B_U(together); - ColorItem* item = new ColorItem( r, g, b, name ); - item->ptr = this; - docPalette->_colors.push_back(item); - gradMap[item] = grad; - } else { - // Treat as gradient - Glib::ustring name( grad->id ); - unsigned int r = SP_RGBA32_R_U(together); - unsigned int g = SP_RGBA32_G_U(together); - unsigned int b = SP_RGBA32_B_U(together); - ColorItem* item = new ColorItem( r, g, b, name ); - item->ptr = this; - docPalette->_colors.push_back(item); - - gint width = 128; - gint height = VBLOCK; - guchar* px = g_new( guchar, 3 * height * width ); - nr_render_checkerboard_rgb( px, width, VBLOCK, 3 * width, 0, 0 ); - - sp_gradient_render_vector_block_rgb( grad, - px, width, height, 3 * width, - 0, width, TRUE ); - - previewMap[item] = px; - gradMap[item] = grad; - } + if ( !newList.empty() ) { + for ( std::vector<SPGradient*>::iterator it = newList.begin(); it != newList.end(); ++it ) + { + SPGradient* grad = *it; + sp_gradient_ensure_vector( grad ); + SPGradientStop first = grad->vector.stops[0]; + SPColor color = first.color; + guint32 together = color.toRGBA32(first.opacity); + + SPGradientStop second = (*it)->vector.stops[1]; + SPColor color2 = second.color; + + Glib::ustring name( grad->getId() ); + unsigned int r = SP_RGBA32_R_U(together); + unsigned int g = SP_RGBA32_G_U(together); + unsigned int b = SP_RGBA32_B_U(together); + ColorItem* item = new ColorItem( r, g, b, name ); + + gint width = PREVIEW_PIXBUF_WIDTH; + gint height = VBLOCK; + guchar* px = g_new( guchar, 3 * height * width ); + nr_render_checkerboard_rgb( px, width, height, 3 * width, 0, 0 ); + + sp_gradient_render_vector_block_rgb( grad, + px, width, height, 3 * width, + 0, width, TRUE ); + + previewMappings[item] = px; + + tmpColors.push_back(item); + gradMappings[item] = grad; + } + } +} + +void SwatchesPanel::handleGradientsChange(SPDocument *document) +{ + SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0; + if (docPalette) { + std::vector<ColorItem*> tmpColors; + std::map<ColorItem*, guchar*> tmpPrevs; + std::map<ColorItem*, SPGradient*> tmpGrads; + recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads); + + for (std::map<ColorItem*, guchar*>::iterator it = tmpPrevs.begin(); it != tmpPrevs.end(); ++it) { + it->first->setPixData(it->second, PREVIEW_PIXBUF_WIDTH, VBLOCK); + } + + for (std::map<ColorItem*, SPGradient*>::iterator it = tmpGrads.begin(); it != tmpGrads.end(); ++it) { + it->first->setGradient(it->second); + } + + docPalette->_colors.swap(tmpColors); + for (std::vector<ColorItem*>::iterator it = tmpColors.begin(); it != tmpColors.end(); ++it) { + delete *it; + } + + + // Figure out which SwatchesPanel instances are affected and update them. + + for (std::map<SwatchesPanel*, SPDocument*>::iterator it = docPerPanel.begin(); it != docPerPanel.end(); ++it) { + if (it->second == document) { + SwatchesPanel* swp = it->first; + std::vector<SwatchPage*> pages = swp->_getSwatchSets(); + SwatchPage* curr = pages[swp->_currentIndex]; + if (curr == docPalette) { + swp->_rebuild(); } } } - JustForNow* curr = possible[_currentIndex]; - if (curr == docPalette) { - _rebuild(); + } +} + +void SwatchesPanel::handleDefsModified(SPDocument *document) +{ + SwatchPage *docPalette = (docPalettes.find(document) != docPalettes.end()) ? docPalettes[document] : 0; + if (docPalette) { + std::vector<ColorItem*> tmpColors; + std::map<ColorItem*, guchar*> tmpPrevs; + std::map<ColorItem*, SPGradient*> tmpGrads; + recalcSwatchContents(document, tmpColors, tmpPrevs, tmpGrads); + + int cap = std::min(docPalette->_colors.size(), tmpColors.size()); + for (int i = 0; i < cap; i++) { + ColorItem* newColor = tmpColors[i]; + ColorItem* oldColor = docPalette->_colors[i]; + if ( (newColor->def.getType() != oldColor->def.getType()) || + (newColor->def.getR() != oldColor->def.getR()) || + (newColor->def.getG() != oldColor->def.getG()) || + (newColor->def.getB() != oldColor->def.getB()) ) { + oldColor->def.setRGB(newColor->def.getR(), newColor->def.getG(), newColor->def.getB()); + } + if (tmpGrads.find(newColor) != tmpGrads.end()) { + oldColor->setGradient(tmpGrads[newColor]); + } + if ( tmpPrevs.find(newColor) != tmpPrevs.end() ) { + oldColor->setPixData(tmpPrevs[newColor], PREVIEW_PIXBUF_WIDTH, VBLOCK); + } } } -#endif // USE_DOCUMENT_PALETTE } -void SwatchesPanel::_updateFromSelection() +std::vector<SwatchPage*> SwatchesPanel::_getSwatchSets() const { -#if USE_DOCUMENT_PALETTE - if ( _ptr ) { - JustForNow *docPalette = reinterpret_cast<JustForNow *>(_ptr); + std::vector<SwatchPage*> tmp; + if (docPalettes.find(_currentDocument) != docPalettes.end()) { + tmp.push_back(docPalettes[_currentDocument]); + } + tmp.insert(tmp.end(), possible.begin(), possible.end()); + + return tmp; +} + +void SwatchesPanel::_updateFromSelection() +{ + SwatchPage *docPalette = (docPalettes.find(_currentDocument) != docPalettes.end()) ? docPalettes[_currentDocument] : 0; + if ( docPalette ) { Glib::ustring fillId; Glib::ustring strokeId; @@ -1554,7 +919,6 @@ void SwatchesPanel::_updateFromSelection() item->setState( isFill, isStroke ); } } -#endif // USE_DOCUMENT_PALETTE } void SwatchesPanel::_handleAction( int setId, int itemId ) @@ -1562,12 +926,13 @@ void SwatchesPanel::_handleAction( int setId, int itemId ) switch( setId ) { case 3: { - if ( itemId >= 0 && itemId < static_cast<int>(possible.size()) ) { + std::vector<SwatchPage*> pages = _getSwatchSets(); + if ( itemId >= 0 && itemId < static_cast<int>(pages.size()) ) { _currentIndex = itemId; if ( !_prefs_path.empty() ) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setString(_prefs_path + "/palette", possible[_currentIndex]->_name); + prefs->setString(_prefs_path + "/palette", pages[_currentIndex]->_name); } _rebuild(); @@ -1579,7 +944,8 @@ void SwatchesPanel::_handleAction( int setId, int itemId ) void SwatchesPanel::_rebuild() { - JustForNow* curr = possible[_currentIndex]; + std::vector<SwatchPage*> pages = _getSwatchSets(); + SwatchPage* curr = pages[_currentIndex]; _holder->clear(); if ( curr->_prefWidth > 0 ) { diff --git a/src/ui/dialog/swatches.h b/src/ui/dialog/swatches.h index 35bcd8c78..b18fd6cad 100644 --- a/src/ui/dialog/swatches.h +++ b/src/ui/dialog/swatches.h @@ -11,17 +11,18 @@ #define SEEN_DIALOGS_SWATCHES_H #include <gtkmm/textview.h> -#include <gtkmm/tooltips.h> #include "ui/widget/panel.h" -#include "ui/previewholder.h" -#include "widgets/ege-paint-def.h" namespace Inkscape { namespace UI { + +class PreviewHolder; + namespace Dialogs { class ColorItem; +class SwatchPage; /** * A panel that displays paint swatches. @@ -40,7 +41,6 @@ public: virtual SPDesktop* getDesktop() {return _currentDesktop;} virtual int getSelectedIndex() {return _currentIndex;} // temporary - virtual void handleGradientsChange(); // temporary protected: virtual void _updateFromSelection(); @@ -48,23 +48,25 @@ protected: virtual void _setDocument( SPDocument *document ); virtual void _rebuild(); + virtual std::vector<SwatchPage*> _getSwatchSets() const; + private: SwatchesPanel(SwatchesPanel const &); // no copy SwatchesPanel &operator=(SwatchesPanel const &); // no assign + static void _trackDocument( SwatchesPanel *panel, SPDocument *document ); + static void handleGradientsChange(SPDocument *document); + static void handleDefsModified(SPDocument *document); + PreviewHolder* _holder; ColorItem* _clear; ColorItem* _remove; int _currentIndex; SPDesktop* _currentDesktop; SPDocument* _currentDocument; - void* _ptr; sigc::connection _documentConnection; - sigc::connection _resourceConnection; sigc::connection _selChanged; - sigc::connection _setModified; - sigc::connection _subselChanged; }; } //namespace Dialogs diff --git a/src/ui/icon-names.h b/src/ui/icon-names.h index 76e76ea34..d36d4a41a 100644 --- a/src/ui/icon-names.h +++ b/src/ui/icon-names.h @@ -350,6 +350,8 @@ "paint-pattern" #define INKSCAPE_ICON_PAINT_SOLID \ "paint-solid" +#define INKSCAPE_ICON_PAINT_SWATCH \ + "paint-swatch" #define INKSCAPE_ICON_PAINT_UNKNOWN \ "paint-unknown" #define INKSCAPE_ICON_PATH_BREAK_APART \ diff --git a/src/ui/tool/Makefile_insert b/src/ui/tool/Makefile_insert new file mode 100644 index 000000000..2a72dd645 --- /dev/null +++ b/src/ui/tool/Makefile_insert @@ -0,0 +1,32 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +ink_common_sources += \ + ui/tool/control-point.cpp \ + ui/tool/control-point.h \ + ui/tool/control-point-selection.cpp \ + ui/tool/control-point-selection.h \ + ui/tool/commit-events.h \ + ui/tool/curve-drag-point.cpp \ + ui/tool/curve-drag-point.h \ + ui/tool/event-utils.cpp \ + ui/tool/event-utils.h \ + ui/tool/manipulator.cpp \ + ui/tool/manipulator.h \ + ui/tool/modifier-tracker.cpp \ + ui/tool/modifier-tracker.h \ + ui/tool/multi-path-manipulator.cpp \ + ui/tool/multi-path-manipulator.h \ + ui/tool/node.cpp \ + ui/tool/node.h \ + ui/tool/node-types.h \ + ui/tool/node-tool.cpp \ + ui/tool/node-tool.h \ + ui/tool/path-manipulator.cpp \ + ui/tool/path-manipulator.h \ + ui/tool/selectable-control-point.cpp \ + ui/tool/selectable-control-point.h \ + ui/tool/selector.cpp \ + ui/tool/selector.h \ + ui/tool/shape-record.h \ + ui/tool/transform-handle-set.cpp \ + ui/tool/transform-handle-set.h diff --git a/src/ui/tool/commit-events.h b/src/ui/tool/commit-events.h new file mode 100644 index 000000000..d99872766 --- /dev/null +++ b/src/ui/tool/commit-events.h @@ -0,0 +1,51 @@ +/** @file + * Commit events. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_COMMIT_EVENTS_H +#define SEEN_UI_TOOL_COMMIT_EVENTS_H + +namespace Inkscape { +namespace UI { + +/// This is used to provide sensible messages on the undo stack. +enum CommitEvent { + COMMIT_MOUSE_MOVE, + COMMIT_KEYBOARD_MOVE_X, + COMMIT_KEYBOARD_MOVE_Y, + COMMIT_MOUSE_SCALE, + COMMIT_MOUSE_SCALE_UNIFORM, + COMMIT_KEYBOARD_SCALE_UNIFORM, + COMMIT_KEYBOARD_SCALE_X, + COMMIT_KEYBOARD_SCALE_Y, + COMMIT_MOUSE_ROTATE, + COMMIT_KEYBOARD_ROTATE, + COMMIT_MOUSE_SKEW_X, + COMMIT_MOUSE_SKEW_Y, + COMMIT_KEYBOARD_SKEW_X, + COMMIT_KEYBOARD_SKEW_Y, + COMMIT_FLIP_X, + COMMIT_FLIP_Y +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/control-point-selection.cpp b/src/ui/tool/control-point-selection.cpp new file mode 100644 index 000000000..f880d2ddf --- /dev/null +++ b/src/ui/tool/control-point-selection.cpp @@ -0,0 +1,613 @@ +/** @file + * Node selection - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <boost/none.hpp> +#include <2geom/transforms.h> +#include "desktop.h" +#include "preferences.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/selectable-control-point.h" +#include "ui/tool/transform-handle-set.h" + +namespace Inkscape { +namespace UI { + +/** + * @class ControlPointSelection + * @brief Group of selected control points. + * + * Some operations can be performed on all selected points regardless of their type, therefore + * this class is also a Manipulator. It handles the transformations of points using + * the keyboard. + * + * The exposed interface is similar to that of an STL set. Internally, a hash map is used. + * @todo Correct iterators (that don't expose the connection list) + */ + +/** @var ControlPointSelection::signal_update + * Fires when the display needs to be updated to reflect changes. + */ +/** @var ControlPointSelection::signal_point_changed + * Fires when a control point is added to or removed from the selection. + * The first param contains a pointer to the control point that changed sel. state. + * The second says whether the point is currently selected. + */ +/** @var ControlPointSelection::signal_commit + * Fires when a change that needs to be committed to XML happens. + */ + +ControlPointSelection::ControlPointSelection(SPDesktop *d, SPCanvasGroup *th_group) + : Manipulator(d) + , _handles(new TransformHandleSet(d, th_group)) + , _dragging(false) + , _handles_visible(true) + , _one_node_handles(false) +{ + signal_update.connect( sigc::bind( + sigc::mem_fun(*this, &ControlPointSelection::_updateTransformHandles), + true)); + ControlPoint::signal_mouseover_change.connect( + sigc::hide( + sigc::mem_fun(*this, &ControlPointSelection::_mouseoverChanged))); + _handles->signal_transform.connect( + sigc::mem_fun(*this, &ControlPointSelection::transform)); + _handles->signal_commit.connect( + sigc::mem_fun(*this, &ControlPointSelection::_commitHandlesTransform)); +} + +ControlPointSelection::~ControlPointSelection() +{ + clear(); + delete _handles; +} + +/** Add a control point to the selection. */ +std::pair<ControlPointSelection::iterator, bool> ControlPointSelection::insert(const value_type &x) +{ + iterator found = _points.find(x); + if (found != _points.end()) { + return std::pair<iterator, bool>(found, false); + } + + found = _points.insert(x).first; + + x->updateState(); + _pointChanged(x, true); + + return std::pair<iterator, bool>(found, true); +} + +/** Remove a point from the selection. */ +void ControlPointSelection::erase(iterator pos) +{ + SelectableControlPoint *erased = *pos; + _points.erase(pos); + erased->updateState(); + _pointChanged(erased, false); +} +ControlPointSelection::size_type ControlPointSelection::erase(const key_type &k) +{ + iterator pos = _points.find(k); + if (pos == _points.end()) return 0; + erase(pos); + return 1; +} +void ControlPointSelection::erase(iterator first, iterator last) +{ + while (first != last) erase(first++); +} + +/** Remove all points from the selection, making it empty. */ +void ControlPointSelection::clear() +{ + for (iterator i = begin(); i != end(); ) + erase(i++); +} + +/** Select all points that this selection can contain. */ +void ControlPointSelection::selectAll() +{ + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + insert(*i); + } +} +/** Select all points inside the given rectangle (in desktop coordinates). */ +void ControlPointSelection::selectArea(Geom::Rect const &r) +{ + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + if (r.contains(**i)) + insert(*i); + } +} +/** Unselect all selected points and select all unselected points. */ +void ControlPointSelection::invertSelection() +{ + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + if ((*i)->selected()) erase(*i); + else insert(*i); + } +} +void ControlPointSelection::spatialGrow(SelectableControlPoint *origin, int dir) +{ + bool grow = (dir > 0); + Geom::Point p = origin->position(); + double best_dist = grow ? HUGE_VAL : 0; + SelectableControlPoint *match = NULL; + for (set_type::iterator i = _all_points.begin(); i != _all_points.end(); ++i) { + bool selected = (*i)->selected(); + if (grow && !selected) { + double dist = Geom::distance((*i)->position(), p); + if (dist < best_dist) { + best_dist = dist; + match = *i; + } + } + if (!grow && selected) { + double dist = Geom::distance((*i)->position(), p); + // use >= to also deselect the origin node when it's the last one selected + if (dist >= best_dist) { + best_dist = dist; + match = *i; + } + } + } + if (match) { + if (grow) insert(match); + else erase(match); + } +} + +/** Transform all selected control points by the given affine transformation. */ +void ControlPointSelection::transform(Geom::Matrix const &m) +{ + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = *i; + cur->transform(m); + } + _updateBounds(); + // TODO preserving the rotation radius needs some rethinking... + if (_rot_radius) (*_rot_radius) *= m.descrim(); + if (_mouseover_rot_radius) (*_mouseover_rot_radius) *= m.descrim(); + signal_update.emit(); +} + +/** Align control points on the specified axis. */ +void ControlPointSelection::align(Geom::Dim2 axis) +{ + if (empty()) return; + Geom::Dim2 d = static_cast<Geom::Dim2>((axis + 1) % 2); + + Geom::OptInterval bound; + for (iterator i = _points.begin(); i != _points.end(); ++i) { + bound.unionWith(Geom::OptInterval((*i)->position()[d])); + } + + double new_coord = bound->middle(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + Geom::Point pos = (*i)->position(); + pos[d] = new_coord; + (*i)->move(pos); + } +} + +/** Equdistantly distribute control points by moving them in the specified dimension. */ +void ControlPointSelection::distribute(Geom::Dim2 d) +{ + if (empty()) return; + + // this needs to be a multimap, otherwise it will fail when some points have the same coord + typedef std::multimap<double, SelectableControlPoint*> SortMap; + + SortMap sm; + Geom::OptInterval bound; + // first we insert all points into a multimap keyed by the aligned coord to sort them + // simultaneously we compute the extent of selection + for (iterator i = _points.begin(); i != _points.end(); ++i) { + Geom::Point pos = (*i)->position(); + sm.insert(std::make_pair(pos[d], (*i))); + bound.unionWith(Geom::OptInterval(pos[d])); + } + + // now we iterate over the multimap and set aligned positions. + double step = size() == 1 ? 0 : bound->extent() / (size() - 1); + double start = bound->min(); + unsigned num = 0; + for (SortMap::iterator i = sm.begin(); i != sm.end(); ++i, ++num) { + Geom::Point pos = i->second->position(); + pos[d] = start + num * step; + i->second->move(pos); + } +} + +/** Get the bounds of the selection. + * @return Smallest rectangle containing the positions of all selected points, + * or nothing if the selection is empty */ +Geom::OptRect ControlPointSelection::pointwiseBounds() +{ + return _bounds; +} + +Geom::OptRect ControlPointSelection::bounds() +{ + return size() == 1 ? (*_points.begin())->bounds() : _bounds; +} + +void ControlPointSelection::showTransformHandles(bool v, bool one_node) +{ + _one_node_handles = one_node; + _handles_visible = v; + _updateTransformHandles(false); +} + +void ControlPointSelection::hideTransformHandles() +{ + _handles->setVisible(false); +} +void ControlPointSelection::restoreTransformHandles() +{ + _updateTransformHandles(true); +} + +void ControlPointSelection::toggleTransformHandlesMode() +{ + if (_handles->mode() == TransformHandleSet::MODE_SCALE) { + _handles->setMode(TransformHandleSet::MODE_ROTATE_SKEW); + if (size() == 1) _handles->rotationCenter().setVisible(false); + } else { + _handles->setMode(TransformHandleSet::MODE_SCALE); + } +} + +void ControlPointSelection::_pointGrabbed(SelectableControlPoint *point) +{ + hideTransformHandles(); + _dragging = true; + _grabbed_point = point; + _farthest_point = point; + double maxdist = 0; + for (iterator i = _points.begin(); i != _points.end(); ++i) { + _original_positions.insert(std::make_pair(*i, (*i)->position())); + double dist = Geom::distance(*_grabbed_point, **i); + if (dist > maxdist) { + maxdist = dist; + _farthest_point = *i; + } + } +} + +void ControlPointSelection::_pointDragged(Geom::Point &new_pos, GdkEventMotion *event) +{ + Geom::Point abs_delta = new_pos - _original_positions[_grabbed_point]; + double fdist = Geom::distance(_original_positions[_grabbed_point], _original_positions[_farthest_point]); + if (held_alt(*event) && fdist > 0) { + // sculpting + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = (*i); + double dist = Geom::distance(_original_positions[cur], _original_positions[_grabbed_point]); + double deltafrac = 0.5 + 0.5 * cos(M_PI * dist/fdist); + cur->move(_original_positions[cur] + abs_delta * deltafrac); + } + } else { + Geom::Point delta = new_pos - _grabbed_point->position(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = (*i); + cur->move(_original_positions[cur] + abs_delta); + } + _handles->rotationCenter().move(_handles->rotationCenter().position() + delta); + } + signal_update.emit(); +} + +void ControlPointSelection::_pointUngrabbed() +{ + _original_positions.clear(); + _dragging = false; + _grabbed_point = _farthest_point = NULL; + _updateBounds(); + restoreTransformHandles(); + signal_commit.emit(COMMIT_MOUSE_MOVE); +} + +bool ControlPointSelection::_pointClicked(SelectableControlPoint *p, GdkEventButton *event) +{ + // clicking a selected node should toggle the transform handles between rotate and scale mode, + // if they are visible + if (held_no_modifiers(*event) && _handles_visible && p->selected()) { + toggleTransformHandlesMode(); + return true; + } + return false; +} + +void ControlPointSelection::_pointChanged(SelectableControlPoint *p, bool selected) +{ + _updateBounds(); + _updateTransformHandles(false); + if (_bounds) + _handles->rotationCenter().move(_bounds->midpoint()); + + signal_point_changed.emit(p, selected); +} + +void ControlPointSelection::_mouseoverChanged() +{ + _mouseover_rot_radius = boost::none; +} + +void ControlPointSelection::_updateBounds() +{ + _rot_radius = boost::none; + _bounds = Geom::OptRect(); + for (iterator i = _points.begin(); i != _points.end(); ++i) { + SelectableControlPoint *cur = (*i); + Geom::Point p = cur->position(); + if (!_bounds) { + _bounds = Geom::Rect(p, p); + } else { + _bounds->expandTo(p); + } + } +} + +void ControlPointSelection::_updateTransformHandles(bool preserve_center) +{ + if (_dragging) return; + + if (_handles_visible && size() > 1) { + _handles->setBounds(*bounds(), preserve_center); + _handles->setVisible(true); + } else if (_one_node_handles && size() == 1) { // only one control point in selection + SelectableControlPoint *p = *begin(); + _handles->setBounds(p->bounds()); + _handles->rotationCenter().move(p->position()); + _handles->rotationCenter().setVisible(false); + _handles->setVisible(true); + } else { + _handles->setVisible(false); + } +} + +/** Moves the selected points along the supplied unit vector according to + * the modifier state of the supplied event. */ +bool ControlPointSelection::_keyboardMove(GdkEventKey const &event, Geom::Point const &dir) +{ + if (held_control(event)) return false; + unsigned num = 1 + combine_key_events(shortcut_key(event), 0); + + Geom::Point delta = dir * num; + if (held_shift(event)) delta *= 10; + if (held_alt(event)) { + delta /= _desktop->current_zoom(); + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + double nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000); + delta *= nudge; + } + + transform(Geom::Translate(delta)); + if (fabs(dir[Geom::X]) > 0) { + signal_commit.emit(COMMIT_KEYBOARD_MOVE_X); + } else { + signal_commit.emit(COMMIT_KEYBOARD_MOVE_Y); + } + return true; +} + +/** @brief Computes the distance to the farthest corner of the bounding box. + * Used to determine what it means to "rotate by one pixel". */ +double ControlPointSelection::_rotationRadius(Geom::Point const &rc) +{ + if (empty()) return 1.0; // some safe value + Geom::Rect b = *bounds(); + double maxlen = 0; + for (unsigned i = 0; i < 4; ++i) { + double len = Geom::distance(b.corner(i), rc); + if (len > maxlen) maxlen = len; + } + return maxlen; +} + +/** Rotates the selected points in the given direction according to the modifier state + * from the supplied event. + * @param event Key event to take modifier state from + * @param dir Direction of rotation (math convention: 1 = counterclockwise, -1 = clockwise) + */ +bool ControlPointSelection::_keyboardRotate(GdkEventKey const &event, int dir) +{ + if (empty()) return false; + + Geom::Point rc; + + // rotate around the mouseovered point, or the selection's rotation center + // if nothing is mouseovered + double radius; + SelectableControlPoint *scp = + dynamic_cast<SelectableControlPoint*>(ControlPoint::mouseovered_point); + if (scp) { + rc = scp->position(); + if (!_mouseover_rot_radius) { + _mouseover_rot_radius = _rotationRadius(rc); + } + radius = *_mouseover_rot_radius; + } else { + rc = _handles->rotationCenter(); + if (!_rot_radius) { + _rot_radius = _rotationRadius(rc); + } + radius = *_rot_radius; + } + + double angle; + if (held_alt(event)) { + // Rotate by "one pixel". We interpret this as rotating by an angle that causes + // the topmost point of a circle circumscribed about the selection's bounding box + // to move on an arc 1 screen pixel long. + angle = atan2(1.0 / _desktop->current_zoom(), radius) * dir; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + angle = M_PI * dir / snaps; + } + + // translate to origin, rotate, translate back to original position + Geom::Matrix m = Geom::Translate(-rc) + * Geom::Rotate(angle) * Geom::Translate(rc); + transform(m); + signal_commit.emit(COMMIT_KEYBOARD_ROTATE); + return true; +} + + +bool ControlPointSelection::_keyboardScale(GdkEventKey const &event, int dir) +{ + if (empty()) return false; + + double maxext = bounds()->maxExtent(); + if (Geom::are_near(maxext, 0)) return false; + + Geom::Point center; + SelectableControlPoint *scp = + dynamic_cast<SelectableControlPoint*>(ControlPoint::mouseovered_point); + if (scp) { + center = scp->position(); + } else { + center = _handles->rotationCenter().position(); + } + + double length_change; + if (held_alt(event)) { + // Scale by "one pixel". It means shrink/grow 1px for the larger dimension + // of the bounding box. + length_change = 1.0 / _desktop->current_zoom() * dir; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + length_change = prefs->getDoubleLimited("/options/defaultscale/value", 2, 1, 1000); + length_change *= dir; + } + double scale = (maxext + length_change) / maxext; + + Geom::Matrix m = Geom::Translate(-center) * Geom::Scale(scale) * Geom::Translate(center); + transform(m); + signal_commit.emit(COMMIT_KEYBOARD_SCALE_UNIFORM); + return true; +} + +bool ControlPointSelection::_keyboardFlip(Geom::Dim2 d) +{ + if (empty()) return false; + + Geom::Scale scale_transform(1, 1); + if (d == Geom::X) { + scale_transform = Geom::Scale(-1, 1); + } else { + scale_transform = Geom::Scale(1, -1); + } + + SelectableControlPoint *scp = + dynamic_cast<SelectableControlPoint*>(ControlPoint::mouseovered_point); + Geom::Point center = scp ? scp->position() : _handles->rotationCenter().position(); + + Geom::Matrix m = Geom::Translate(-center) * scale_transform * Geom::Translate(center); + transform(m); + signal_commit.emit(d == Geom::X ? COMMIT_FLIP_X : COMMIT_FLIP_Y); + return true; +} + +void ControlPointSelection::_commitHandlesTransform(CommitEvent ce) +{ + _updateBounds(); + _updateTransformHandles(true); + signal_commit.emit(ce); +} + +bool ControlPointSelection::event(GdkEvent *event) +{ + // implement generic event handling that should apply for all control point selections here; + // for example, keyboard moves and transformations. This way this functionality doesn't need + // to be duplicated in many places + // Later split out so that it can be reused in object selection + + switch (event->type) { + case GDK_KEY_PRESS: + // do not handle key events if the selection is empty + if (empty()) break; + + switch(shortcut_key(event->key)) { + // moves + case GDK_Up: + case GDK_KP_Up: + case GDK_KP_8: + return _keyboardMove(event->key, Geom::Point(0, 1)); + case GDK_Down: + case GDK_KP_Down: + case GDK_KP_2: + return _keyboardMove(event->key, Geom::Point(0, -1)); + case GDK_Right: + case GDK_KP_Right: + case GDK_KP_6: + return _keyboardMove(event->key, Geom::Point(1, 0)); + case GDK_Left: + case GDK_KP_Left: + case GDK_KP_4: + return _keyboardMove(event->key, Geom::Point(-1, 0)); + + // rotates + case GDK_bracketleft: + return _keyboardRotate(event->key, 1); + case GDK_bracketright: + return _keyboardRotate(event->key, -1); + + // scaling + case GDK_less: + case GDK_comma: + return _keyboardScale(event->key, -1); + case GDK_greater: + case GDK_period: + return _keyboardScale(event->key, 1); + + // TODO: skewing + + // flipping + // NOTE: H is horizontal flip, while Shift+H switches transform handle mode! + case GDK_h: + case GDK_H: + if (held_shift(event->key)) { + toggleTransformHandlesMode(); + return true; + } + // any modifiers except shift should cause no action + if (held_any_modifiers(event->key)) break; + return _keyboardFlip(Geom::X); + case GDK_v: + case GDK_V: + if (held_any_modifiers(event->key)) break; + return _keyboardFlip(Geom::Y); + default: break; + } + break; + default: break; + } + return false; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/control-point-selection.h b/src/ui/tool/control-point-selection.h new file mode 100644 index 000000000..514ecb2e3 --- /dev/null +++ b/src/ui/tool/control-point-selection.h @@ -0,0 +1,161 @@ +/** @file + * Node selection - stores a set of nodes and applies transformations + * to them + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_NODE_SELECTION_H +#define SEEN_UI_TOOL_NODE_SELECTION_H + +#include <memory> +#include <boost/optional.hpp> +#include <sigc++/sigc++.h> +#include <2geom/forward.h> +#include <2geom/point.h> +#include <2geom/rect.h> +#include "display/display-forward.h" +#include "util/accumulators.h" +#include "util/unordered-containers.h" +#include "ui/tool/commit-events.h" +#include "ui/tool/manipulator.h" + +class SPDesktop; + +namespace Inkscape { +namespace UI { +class TransformHandleSet; +class SelectableControlPoint; +} +} + +namespace Inkscape { +namespace UI { + +class ControlPointSelection : public Manipulator, public sigc::trackable { +public: + ControlPointSelection(SPDesktop *d, SPCanvasGroup *th_group); + ~ControlPointSelection(); + typedef INK_UNORDERED_SET<SelectableControlPoint *> set_type; + typedef set_type Set; // convenience alias + + typedef set_type::iterator iterator; + typedef set_type::const_iterator const_iterator; + typedef set_type::size_type size_type; + typedef SelectableControlPoint *value_type; + typedef SelectableControlPoint *key_type; + + // size + bool empty() { return _points.empty(); } + size_type size() { return _points.size(); } + + // iterators + iterator begin() { return _points.begin(); } + const_iterator begin() const { return _points.begin(); } + iterator end() { return _points.end(); } + const_iterator end() const { return _points.end(); } + + // insert + std::pair<iterator, bool> insert(const value_type& x); + template <class InputIterator> + void insert(InputIterator first, InputIterator last) { + for (; first != last; ++first) { + insert(*first); + } + } + + // erase + void clear(); + void erase(iterator pos); + size_type erase(const key_type& k); + void erase(iterator first, iterator last); + + // find + iterator find(const key_type &k) { + return _points.find(k); + } + + // Sometimes it is very useful to keep a list of all selectable points. + set_type const &allPoints() const { return _all_points; } + set_type &allPoints() { return _all_points; } + // ...for example in these methods. Another useful case is snapping. + void selectAll(); + void selectArea(Geom::Rect const &); + void invertSelection(); + void spatialGrow(SelectableControlPoint *origin, int dir); + + virtual bool event(GdkEvent *); + + void transform(Geom::Matrix const &m); + void align(Geom::Dim2 d); + void distribute(Geom::Dim2 d); + + Geom::OptRect pointwiseBounds(); + Geom::OptRect bounds(); + + bool transformHandlesEnabled() { return _handles_visible; } + void showTransformHandles(bool v, bool one_node); + // the two methods below do not modify the state; they are for use in manipulators + // that need to temporarily hide the handles, for example when moving a node + void hideTransformHandles(); + void restoreTransformHandles(); + void toggleTransformHandlesMode(); + + sigc::signal<void> signal_update; + sigc::signal<void, SelectableControlPoint *, bool> signal_point_changed; + sigc::signal<void, CommitEvent> signal_commit; +private: + // The functions below are invoked from SelectableControlPoint. + // Previously they were connected to handlers when selecting, but this + // creates problems when dragging a point that was not selected. + void _pointGrabbed(SelectableControlPoint *); + void _pointDragged(Geom::Point &, GdkEventMotion *); + void _pointUngrabbed(); + bool _pointClicked(SelectableControlPoint *, GdkEventButton *); + void _pointChanged(SelectableControlPoint *, bool); + void _mouseoverChanged(); + + void _updateTransformHandles(bool preserve_center); + void _updateBounds(); + bool _keyboardMove(GdkEventKey const &, Geom::Point const &); + bool _keyboardRotate(GdkEventKey const &, int); + bool _keyboardScale(GdkEventKey const &, int); + bool _keyboardFlip(Geom::Dim2); + void _keyboardTransform(Geom::Matrix const &); + void _commitHandlesTransform(CommitEvent ce); + double _rotationRadius(Geom::Point const &); + + set_type _points; + set_type _all_points; + INK_UNORDERED_MAP<SelectableControlPoint *, Geom::Point> _original_positions; + boost::optional<double> _rot_radius; + boost::optional<double> _mouseover_rot_radius; + Geom::OptRect _bounds; + TransformHandleSet *_handles; + SelectableControlPoint *_grabbed_point, *_farthest_point; + unsigned _dragging : 1; + unsigned _handles_visible : 1; + unsigned _one_node_handles : 1; + + friend class SelectableControlPoint; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/control-point.cpp b/src/ui/tool/control-point.cpp new file mode 100644 index 000000000..b74e3bc9c --- /dev/null +++ b/src/ui/tool/control-point.cpp @@ -0,0 +1,581 @@ +/** @file + * Desktop-bound visual control object - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <iostream> +#include <gdkmm.h> +#include <gtkmm.h> +#include <2geom/point.h> +#include "desktop.h" +#include "desktop-handles.h" +#include "display/snap-indicator.h" +#include "event-context.h" +#include "message-context.h" +#include "preferences.h" +#include "ui/tool/control-point.h" +#include "ui/tool/event-utils.h" + +namespace Inkscape { +namespace UI { + +// class and member documentation goes here... + +/** + * @class ControlPoint + * @brief Draggable point, the workhorse of on-canvas editing. + * + * Control points (formerly known as knots) are graphical representations of some significant + * point in the drawing. The drawing can be changed by dragging the point and the things that are + * attached to it with the mouse. Example things that could be edited with draggable points + * are gradient stops, the place where text is attached to a path, text kerns, nodes and handles + * in a path, and many more. + * + * @par Control point event handlers + * @par + * The control point has several virtual methods which allow you to react to things that + * happen to it. The most important ones are the grabbed, dragged, ungrabbed and moved functions. + * When a drag happens, the order of calls is as follows: + * - <tt>grabbed()</tt> + * - <tt>dragged()</tt> + * - <tt>dragged()</tt> + * - <tt>dragged()</tt> + * - ... + * - <tt>dragged()</tt> + * - <tt>ungrabbed()</tt> + * + * The control point can also respond to clicks and double clicks. On a double click, + * clicked() is called, followed by doubleclicked(). When deriving from SelectableControlPoint, + * you need to manually call the superclass version at the appropriate point in your handler. + * + * @par Which method to override? + * @par + * You might wonder which hook to use when you want to do things when the point is relocated. + * Here are some tips: + * - If the point is used to edit an object, override the move() method. + * - If the point can usually be dragged wherever you like but can optionally be constrained + * to axes or the like, add a handler for <tt>signal_dragged</tt> that modifies its new + * position argument. + * - If the point has additional canvas items tied to it (like handle lines), override + * the setPosition() method. + */ + +/** + * @enum ControlPoint::State + * Enumeration representing the possible states of the control point, used to determine + * its appearance. + * @var ControlPoint::STATE_NORMAL + * Normal state + * @var ControlPoint::STATE_MOUSEOVER + * Mouse is hovering over the control point + * @var ControlPoint::STATE_CLICKED + * First mouse button pressed over the control point + */ + +// Default colors for control points +static ControlPoint::ColorSet default_color_set = { + {0xffffff00, 0x01000000}, // normal fill, stroke + {0xff0000ff, 0x01000000}, // mouseover fill, stroke + {0x0000ffff, 0x01000000} // clicked fill, stroke +}; + +/** Holds the currently mouseovered control point. */ +ControlPoint *ControlPoint::mouseovered_point = 0; + +/** Emitted when the mouseovered point changes. The parameter is the new mouseovered point. + * When a point ceases to be mouseovered, the parameter will be NULL. */ +sigc::signal<void, ControlPoint*> ControlPoint::signal_mouseover_change; + +/** Stores the window point over which the cursor was during the last mouse button press */ +Geom::Point ControlPoint::_drag_event_origin(Geom::infinity(), Geom::infinity()); + +/** Stores the desktop point from which the last drag was initiated */ +Geom::Point ControlPoint::_drag_origin(Geom::infinity(), Geom::infinity()); + +/** Events which should be captured when a handle is being dragged. */ +int const ControlPoint::_grab_event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK); + +bool ControlPoint::_drag_initiated = false; +bool ControlPoint::_event_grab = false; + +/** A color set which you can use to create an invisible control that can still receive events. + * @relates ControlPoint */ +ControlPoint::ColorSet invisible_cset = { + {0x00000000, 0x00000000}, + {0x00000000, 0x00000000}, + {0x00000000, 0x00000000} +}; + +/** + * Create a regular control point. + * Derive to have constructors with a reasonable number of parameters. + * + * @param d Desktop for this control + * @param initial_pos Initial position of the control point in desktop coordinates + * @param anchor Where is the control point rendered relative to its desktop coordinates + * @param shape Shape of the control point: square, diamond, circle... + * @param size Pixel size of the visual representation + * @param cset Colors of the point + * @param group The canvas group the point's canvas item should be created in + */ +ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, SPCtrlShapeType shape, + unsigned int size, ColorSet *cset, SPCanvasGroup *group) + : _desktop (d) + , _canvas_item (NULL) + , _cset (cset ? cset : &default_color_set) + , _state (STATE_NORMAL) + , _position (initial_pos) +{ + _canvas_item = sp_canvas_item_new( + group ? group : sp_desktop_controls (_desktop), SP_TYPE_CTRL, + "anchor", (GtkAnchorType) anchor, "size", (gdouble) size, "shape", shape, + "filled", TRUE, "fill_color", _cset->normal.fill, + "stroked", TRUE, "stroke_color", _cset->normal.stroke, + "mode", SP_CTRL_MODE_XOR, NULL); + _commonInit(); +} + +/** + * Create a control point with a pixbuf-based visual representation. + * + * @param d Desktop for this control + * @param initial_pos Initial position of the control point in desktop coordinates + * @param anchor Where is the control point rendered relative to its desktop coordinates + * @param pixbuf Pixbuf to be used as the visual representation + * @param cset Colors of the point + * @param group The canvas group the point's canvas item should be created in + */ +ControlPoint::ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pixbuf, + ColorSet *cset, SPCanvasGroup *group) + : _desktop (d) + , _canvas_item (NULL) + , _cset(cset ? cset : &default_color_set) + , _position (initial_pos) +{ + _canvas_item = sp_canvas_item_new( + group ? group : sp_desktop_controls(_desktop), SP_TYPE_CTRL, + "anchor", (GtkAnchorType) anchor, "size", (gdouble) pixbuf->get_width(), + "shape", SP_CTRL_SHAPE_BITMAP, "pixbuf", pixbuf->gobj(), + "filled", TRUE, "fill_color", _cset->normal.fill, + "stroked", TRUE, "stroke_color", _cset->normal.stroke, + "mode", SP_CTRL_MODE_XOR, NULL); + _commonInit(); +} + +ControlPoint::~ControlPoint() +{ + // avoid storing invalid points in mouseovered_point + if (this == mouseovered_point) { + _clearMouseover(); + } + + g_signal_handler_disconnect(G_OBJECT(_canvas_item), _event_handler_connection); + //sp_canvas_item_hide(_canvas_item); + gtk_object_destroy(_canvas_item); +} + +void ControlPoint::_commonInit() +{ + SP_CTRL(_canvas_item)->moveto(_position); + _event_handler_connection = g_signal_connect(G_OBJECT(_canvas_item), "event", + G_CALLBACK(_event_handler), this); +} + +/** Relocate the control point without side effects. + * Overload this method only if there is an additional graphical representation + * that must be updated (like the lines that connect handles to nodes). If you override it, + * you must also call the superclass implementation of the method. + * @todo Investigate whether this method should be protected */ +void ControlPoint::setPosition(Geom::Point const &pos) +{ + _position = pos; + SP_CTRL(_canvas_item)->moveto(pos); +} + +/** Move the control point to new position with side effects. + * This is called after each drag. Override this method if only some positions make sense + * for a control point (like a point that must always be on a path and can't modify it), + * or when moving a control point changes the positions of other points. */ +void ControlPoint::move(Geom::Point const &pos) +{ + setPosition(pos); +} + +/** Apply an arbitrary affine transformation to a control point. This is used + * by ControlPointSelection, and is important for things like nodes with handles. + * The default implementation simply moves the point according to the transform. */ +void ControlPoint::transform(Geom::Matrix const &m) { + move(position() * m); +} + +bool ControlPoint::visible() const +{ + return sp_canvas_item_is_visible(_canvas_item); +} + +/** Set the visibility of the control point. An invisible point is not drawn on the canvas + * and cannot receive any events. If you want to have an invisible point that can respond + * to events, use <tt>invisible_cset</tt> as its color set. */ +void ControlPoint::setVisible(bool v) +{ + if (v) sp_canvas_item_show(_canvas_item); + else sp_canvas_item_hide(_canvas_item); +} + +Glib::ustring ControlPoint::format_tip(char const *format, ...) +{ + va_list args; + va_start(args, format); + char *dyntip = g_strdup_vprintf(format, args); + va_end(args); + Glib::ustring ret = dyntip; + g_free(dyntip); + return ret; +} + +unsigned int ControlPoint::_size() const +{ + double ret; + g_object_get(_canvas_item, "size", &ret, NULL); + return static_cast<unsigned int>(ret); +} + +SPCtrlShapeType ControlPoint::_shape() const +{ + SPCtrlShapeType ret; + g_object_get(_canvas_item, "shape", &ret, NULL); + return ret; +} + +GtkAnchorType ControlPoint::_anchor() const +{ + GtkAnchorType ret; + g_object_get(_canvas_item, "anchor", &ret, NULL); + return ret; +} + +Glib::RefPtr<Gdk::Pixbuf> ControlPoint::_pixbuf() +{ + GdkPixbuf *ret; + g_object_get(_canvas_item, "pixbuf", &ret, NULL); + return Glib::wrap(ret); +} + +// Same for setters. + +void ControlPoint::_setSize(unsigned int size) +{ + g_object_set(_canvas_item, "size", (gdouble) size, NULL); +} + +void ControlPoint::_setShape(SPCtrlShapeType shape) +{ + g_object_set(_canvas_item, "shape", shape, NULL); +} + +void ControlPoint::_setAnchor(GtkAnchorType anchor) +{ + g_object_set(_canvas_item, "anchor", anchor, NULL); +} + +void ControlPoint::_setPixbuf(Glib::RefPtr<Gdk::Pixbuf> p) +{ + g_object_set(_canvas_item, "pixbuf", Glib::unwrap(p), NULL); +} + +// re-routes events into the virtual function +int ControlPoint::_event_handler(SPCanvasItem */*item*/, GdkEvent *event, ControlPoint *point) +{ + return point->_eventHandler(event) ? TRUE : FALSE; +} + +// main event callback, which emits all other callbacks. +bool ControlPoint::_eventHandler(GdkEvent *event) +{ + // NOTE the static variables below are shared for all points! + // TODO handle clicks and drags from other buttons too + + // offset from the pointer hotspot to the center of the grabbed knot in desktop coords + static Geom::Point pointer_offset; + // number of last doubleclicked button + static unsigned next_release_doubleclick = 0; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int drag_tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + switch(event->type) + { + case GDK_BUTTON_PRESS: + next_release_doubleclick = 0; + if (event->button.button == 1) { + // 1st mouse button click. internally, start dragging, but do not emit signals + // or change position until drag tolerance is exceeded. + _drag_event_origin[Geom::X] = event->button.x; + _drag_event_origin[Geom::Y] = event->button.y; + pointer_offset = _position - _desktop->w2d(_drag_event_origin); + _drag_initiated = false; + // route all events to this handler + sp_canvas_item_grab(_canvas_item, _grab_event_mask, NULL, event->button.time); + _event_grab = true; + _setState(STATE_CLICKED); + return true; + } + return false; + + case GDK_2BUTTON_PRESS: + // store the button number for next release + next_release_doubleclick = event->button.button; + return true; + + case GDK_MOTION_NOTIFY: + combine_motion_events(_desktop->canvas, event->motion, 0); + if (_event_grab && !_desktop->event_context->space_panning) { + _desktop->snapindicator->remove_snaptarget(); + bool transferred = false; + if (!_drag_initiated) { + bool t = fabs(event->motion.x - _drag_event_origin[Geom::X]) <= drag_tolerance && + fabs(event->motion.y - _drag_event_origin[Geom::Y]) <= drag_tolerance; + if (t) return true; + + // if we are here, it means the tolerance was just exceeded. + _drag_origin = _position; + transferred = grabbed(&event->motion); + // _drag_initiated might change during the above virtual call + if (!_drag_initiated) { + // this guarantees smooth redraws while dragging + sp_canvas_force_full_redraw_after_interruptions(_desktop->canvas, 5); + _drag_initiated = true; + } + } + if (!transferred) { + // dragging in progress + Geom::Point new_pos = _desktop->w2d(event_point(event->motion)) + pointer_offset; + + // the new position is passed by reference and can be changed in the handlers. + dragged(new_pos, &event->motion); + move(new_pos); + _updateDragTip(&event->motion); // update dragging tip after moving to new position + + _desktop->scroll_to_point(new_pos); + _desktop->set_coordinate_status(_position); + sp_event_context_snap_delay_handler(_desktop->event_context, NULL, + (gpointer) this, &event->motion, + DelayedSnapEvent::CONTROL_POINT_HANDLER); + } + return true; + } + break; + + case GDK_BUTTON_RELEASE: + if (_event_grab && event->button.button == 1) { + // If we have any pending snap event, then invoke it now! + // (This is needed because we might not have snapped on the latest GDK_MOTION_NOTIFY event + // if the mouse speed was too high. This is inherent to the snap-delay mechanism. + // We must snap at some point in time though, and this is our last chance) + // PS: For other contexts this is handled already in sp_event_context_item_handler or + // sp_event_context_root_handler + if (_desktop->event_context->_delayed_snap_event) { + sp_event_context_snap_watchdog_callback(_desktop->event_context->_delayed_snap_event); + } + + sp_canvas_item_ungrab(_canvas_item, event->button.time); + _setMouseover(this, event->button.state); + _event_grab = false; + + if (_drag_initiated) { + sp_canvas_end_forced_full_redraws(_desktop->canvas); + } + + if (_drag_initiated) { + // it is the end of a drag + _drag_initiated = false; + ungrabbed(&event->button); + return true; + } else { + // it is the end of a click + if (next_release_doubleclick) { + return doubleclicked(&event->button); + } else { + return clicked(&event->button); + } + } + } + break; + + case GDK_ENTER_NOTIFY: + _setMouseover(this, event->crossing.state); + return true; + case GDK_LEAVE_NOTIFY: + _clearMouseover(); + return true; + + case GDK_GRAB_BROKEN: + if (_event_grab && !event->grab_broken.keyboard) { + { + ungrabbed(NULL); + if (_drag_initiated) + sp_canvas_end_forced_full_redraws(_desktop->canvas); + } + _setState(STATE_NORMAL); + _event_grab = false; + _drag_initiated = false; + return true; + } + break; + + // update tips on modifier state change + // TODO add ESC keybinding as drag cancel + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + if (mouseovered_point != this) return false; + if (_drag_initiated) { + return true; // this prevents the tool from overwriting the drag tip + } else { + unsigned state = state_after_event(event); + if (state != event->key.state) { + // we need to return true if there was a tip available, otherwise the tool's + // handler will process this event and set the tool's message, overwriting + // the point's message + return _updateTip(state); + } + } + break; + + default: break; + } + + return false; +} + +void ControlPoint::_setMouseover(ControlPoint *p, unsigned state) +{ + bool visible = p->visible(); + if (visible) { // invisible points shouldn't get mouseovered + p->_setState(STATE_MOUSEOVER); + } + p->_updateTip(state); + + if (visible && mouseovered_point != p) { + mouseovered_point = p; + signal_mouseover_change.emit(mouseovered_point); + } +} + +bool ControlPoint::_updateTip(unsigned state) +{ + Glib::ustring tip = _getTip(state); + if (!tip.empty()) { + _desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, + tip.data()); + return true; + } else { + _desktop->event_context->defaultMessageContext()->clear(); + return false; + } +} + +bool ControlPoint::_updateDragTip(GdkEventMotion *event) +{ + if (!_hasDragTips()) return false; + Glib::ustring tip = _getDragTip(event); + if (!tip.empty()) { + _desktop->event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, + tip.data()); + return true; + } else { + _desktop->event_context->defaultMessageContext()->clear(); + return false; + } +} + +void ControlPoint::_clearMouseover() +{ + if (mouseovered_point) { + mouseovered_point->_desktop->event_context->defaultMessageContext()->clear(); + mouseovered_point->_setState(STATE_NORMAL); + mouseovered_point = 0; + signal_mouseover_change.emit(mouseovered_point); + } +} + +/** Transfer the grab to another point. This method allows one to create a draggable point + * that should be dragged instead of the one that received the grabbed signal. + * This is used to implement dragging out handles in the new node tool, for example. + * + * This method will NOT emit the ungrab signal of @c prev_point, because this would complicate + * using it with selectable control points. If you use this method while dragging, you must emit + * the ungrab signal yourself. + * + * Note that this will break horribly if you try to transfer grab between points in different + * desktops, which doesn't make much sense anyway. */ +void ControlPoint::transferGrab(ControlPoint *prev_point, GdkEventMotion *event) +{ + if (!_event_grab) return; + + grabbed(event); + sp_canvas_item_ungrab(prev_point->_canvas_item, event->time); + sp_canvas_item_grab(_canvas_item, _grab_event_mask, NULL, event->time); + + if (!_drag_initiated) { + sp_canvas_force_full_redraw_after_interruptions(_desktop->canvas, 5); + _drag_initiated = true; + } + + prev_point->_setState(STATE_NORMAL); + _setMouseover(this, event->state); +} + +/** + * @brief Change the state of the knot + * Alters the appearance of the knot to match one of the states: normal, mouseover + * or clicked. + */ +void ControlPoint::_setState(State state) +{ + ColorEntry current = {0, 0}; + switch(state) { + case STATE_NORMAL: + current = _cset->normal; break; + case STATE_MOUSEOVER: + current = _cset->mouseover; break; + case STATE_CLICKED: + current = _cset->clicked; break; + }; + _setColors(current); + _state = state; +} +void ControlPoint::_setColors(ColorEntry colors) +{ + g_object_set(_canvas_item, "fill_color", colors.fill, "stroke_color", colors.stroke, NULL); +} + +// dummy implementations for handlers +// they are here to avoid unused param warnings +bool ControlPoint::grabbed(GdkEventMotion *) { return false; } +void ControlPoint::dragged(Geom::Point &, GdkEventMotion *) {} +void ControlPoint::ungrabbed(GdkEventButton *) {} +bool ControlPoint::clicked(GdkEventButton *) { return false; } +bool ControlPoint::doubleclicked(GdkEventButton *) { return false; } + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/control-point.h b/src/ui/tool/control-point.h new file mode 100644 index 000000000..48c70748b --- /dev/null +++ b/src/ui/tool/control-point.h @@ -0,0 +1,202 @@ +/** @file + * Desktop-bound visual control object + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_CONTROL_POINT_H +#define SEEN_UI_TOOL_CONTROL_POINT_H + +#include <boost/utility.hpp> +#include <sigc++/sigc++.h> +#include <gdkmm.h> +#include <gtkmm.h> +#include <2geom/point.h> + +#include "display/display-forward.h" +#include "forward.h" +#include "util/accumulators.h" +#include "display/sodipodi-ctrl.h" + +namespace Inkscape { +namespace UI { + +// most of the documentation is in the .cpp file + +class ControlPoint : boost::noncopyable, public sigc::trackable { +public: + typedef Inkscape::Util::ReverseInterruptible RInt; + typedef Inkscape::Util::Interruptible Int; + // these have to be public, because GCC doesn't allow protected types in constructors, + // even if the constructors are protected themselves. + struct ColorEntry { + guint32 fill; + guint32 stroke; + }; + struct ColorSet { + ColorEntry normal; + ColorEntry mouseover; + ColorEntry clicked; + }; + enum State { + STATE_NORMAL, + STATE_MOUSEOVER, + STATE_CLICKED + }; + + virtual ~ControlPoint(); + + /// @name Adjust the position of the control point + /// @{ + /** Current position of the control point. */ + Geom::Point const &position() const { return _position; } + operator Geom::Point const &() { return _position; } + virtual void move(Geom::Point const &pos); + virtual void setPosition(Geom::Point const &pos); + virtual void transform(Geom::Matrix const &m); + /// @} + + /// @name Toggle the point's visibility + /// @{ + bool visible() const; + virtual void setVisible(bool v); + /// @} + + /// @name Transfer grab from another event handler + /// @{ + void transferGrab(ControlPoint *from, GdkEventMotion *event); + /// @} + + /// @name Receive notifications about control point events + /// @{ + /*sigc::signal<void, Geom::Point const &, Geom::Point &, GdkEventMotion*> signal_dragged; + sigc::signal<bool, GdkEventButton*>::accumulated<RInt> signal_clicked; + sigc::signal<bool, GdkEventButton*>::accumulated<RInt> signal_doubleclicked; + sigc::signal<bool, GdkEventMotion*>::accumulated<Int> signal_grabbed; + sigc::signal<void, GdkEventButton*> signal_ungrabbed;*/ + /// @} + + /// @name Inspect the state of the control point + /// @{ + State state() { return _state; } + bool mouseovered() { return this == mouseovered_point; } + /// @} + + static ControlPoint *mouseovered_point; + static sigc::signal<void, ControlPoint*> signal_mouseover_change; + static Glib::ustring format_tip(char const *format, ...) G_GNUC_PRINTF(1,2); + + // temporarily public, until snap delay is refactored a little + virtual bool _eventHandler(GdkEvent *event); + +protected: + ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, Gtk::AnchorType anchor, + SPCtrlShapeType shape, unsigned int size, ColorSet *cset = 0, SPCanvasGroup *group = 0); + ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, Gtk::AnchorType anchor, + Glib::RefPtr<Gdk::Pixbuf> pixbuf, ColorSet *cset = 0, SPCanvasGroup *group = 0); + + /// @name Handle control point events in subclasses + /// @{ + /** + * Called when the user moves the point beyond the drag tolerance with the first button held + * down. Return true if you called transferGrab() during this method. + * @param event Motion event when drag tolerance was exceeded */ + virtual bool grabbed(GdkEventMotion *event); + /** + * Called while dragging, but before moving the knot to new position. + * @param pos Old position, always equal to position() + * @param new_pos New position (after drag). This is passed as a non-const reference, + * so you can change it from the handler - that's how constrained dragging is implemented. + * @param event Motion event */ + virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event); + /** + * @var ControlPoint::signal_ungrabbed + * Emitted when the control point finishes a drag. + * @param event Button release event + */ + virtual void ungrabbed(GdkEventButton *event); + /** + * Called when the control point is clicked, at mouse button release. Your override should + * return true if the click had some effect. If it did nothing, return false. Improperly + * implementing this method can cause the default context menu not to appear when a control + * point is right-clicked. + * @param event Button release event */ + virtual bool clicked(GdkEventButton *event); + /** + * Called when the control point is doubleclicked, at mouse button release. + * @param event Button release event */ + virtual bool doubleclicked(GdkEventButton *); + /// @} + + /// @name Manipulate the control point's appearance in subclasses + /// @{ + virtual void _setState(State state); + void _setColors(ColorEntry c); + + unsigned int _size() const; + SPCtrlShapeType _shape() const; + GtkAnchorType _anchor() const; + Glib::RefPtr<Gdk::Pixbuf> _pixbuf(); + + void _setSize(unsigned int size); + void _setShape(SPCtrlShapeType shape); + void _setAnchor(GtkAnchorType anchor); + void _setPixbuf(Glib::RefPtr<Gdk::Pixbuf>); + /// @} + + virtual Glib::ustring _getTip(unsigned /*state*/) { return ""; } + virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { return ""; } + virtual bool _hasDragTips() { return false; } + + SPDesktop *const _desktop; ///< The desktop this control point resides on. + SPCanvasItem * _canvas_item; ///< Visual representation of the control point. + ColorSet *_cset; ///< Colors used to represent the point + State _state; + + static int const _grab_event_mask; + static Geom::Point const &_last_click_event_point() { return _drag_event_origin; } + static Geom::Point const &_last_drag_origin() { return _drag_origin; } + +private: + ControlPoint(ControlPoint const &other); + void operator=(ControlPoint const &other); + + static int _event_handler(SPCanvasItem *item, GdkEvent *event, ControlPoint *point); + static void _setMouseover(ControlPoint *, unsigned state); + static void _clearMouseover(); + bool _updateTip(unsigned state); + bool _updateDragTip(GdkEventMotion *event); + void _setDefaultColors(); + void _commonInit(); + + Geom::Point _position; ///< Current position in desktop coordinates + gulong _event_handler_connection; + + static Geom::Point _drag_event_origin; + static Geom::Point _drag_origin; + static bool _event_grab; + static bool _drag_initiated; +}; + +extern ControlPoint::ColorSet invisible_cset; + + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/curve-drag-point.cpp b/src/ui/tool/curve-drag-point.cpp new file mode 100644 index 000000000..e761daf20 --- /dev/null +++ b/src/ui/tool/curve-drag-point.cpp @@ -0,0 +1,196 @@ +/** @file + * Control point that is dragged during path drag + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib/gi18n.h> +#include <2geom/bezier-curve.h> +#include "desktop.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/curve-drag-point.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/path-manipulator.h" +#include "ui/tool/node.h" + +namespace Inkscape { +namespace UI { + +/** + * @class CurveDragPoint + * An invisible point used to drag curves. This point is used by PathManipulator to allow editing + * of path segments by dragging them. It is defined in a separate file so that the node tool + * can check if the mouseovered control point is a curve drag point and update the cursor + * accordingly, without the need to drag in the full PathManipulator header. + */ + +// This point should be invisible to the user - use the invisible_cset from control-point.h +// TODO make some methods from path-manipulator.cpp public so that this point doesn't have +// to be declared as a friend + +bool CurveDragPoint::_drags_stroke = false; + +CurveDragPoint::CurveDragPoint(PathManipulator &pm) + : ControlPoint(pm._multi_path_manipulator._path_data.node_data.desktop, Geom::Point(), + Gtk::ANCHOR_CENTER, SP_CTRL_SHAPE_CIRCLE, 1.0, &invisible_cset, + pm._multi_path_manipulator._path_data.dragpoint_group) + , _pm(pm) +{ + setVisible(false); +} + +bool CurveDragPoint::_eventHandler(GdkEvent *event) +{ + // do not process any events when the manipulator is empty + if (_pm.empty()) { + setVisible(false); + return false; + } + return ControlPoint::_eventHandler(event); +} + +bool CurveDragPoint::grabbed(GdkEventMotion */*event*/) +{ + _pm._selection.hideTransformHandles(); + NodeList::iterator second = first.next(); + + // move the handles to 1/3 the length of the segment for line segments + if (first->front()->isDegenerate() && second->back()->isDegenerate()) { + + // delta is a vector equal 1/3 of distance from first to second + Geom::Point delta = (second->position() - first->position()) / 3.0; + first->front()->move(first->front()->position() + delta); + second->back()->move(second->back()->position() - delta); + + _pm.update(); + } + return false; +} + +void CurveDragPoint::dragged(Geom::Point &new_pos, GdkEventMotion *) +{ + NodeList::iterator second = first.next(); + // Magic Bezier Drag Equations follow! + // "weight" describes how the influence of the drag should be distributed + // among the handles; 0 = front handle only, 1 = back handle only. + double weight, t = _t; + if (t <= 1.0 / 6.0) weight = 0; + else if (t <= 0.5) weight = (pow((6 * t - 1) / 2.0, 3)) / 2; + else if (t <= 5.0 / 6.0) weight = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5; + else weight = 1; + + Geom::Point delta = new_pos - position(); + Geom::Point offset0 = ((1-weight)/(3*t*(1-t)*(1-t))) * delta; + Geom::Point offset1 = (weight/(3*t*t*(1-t))) * delta; + + first->front()->move(first->front()->position() + offset0); + second->back()->move(second->back()->position() + offset1); + + _pm.update(); +} + +void CurveDragPoint::ungrabbed(GdkEventButton *) +{ + _pm._updateDragPoint(_desktop->d2w(position())); + _pm._commit(_("Drag curve")); + _pm._selection.restoreTransformHandles(); +} + +bool CurveDragPoint::clicked(GdkEventButton *event) +{ + // This check is probably redundant + if (!first || event->button != 1) return false; + // the next iterator can be invalid if we click very near the end of path + NodeList::iterator second = first.next(); + if (!second) return false; + + // insert nodes on Ctrl+Alt+click + if (held_control(*event) && held_alt(*event)) { + _insertNode(false); + return true; + } + + if (held_shift(*event)) { + // if both nodes of the segment are selected, deselect; + // otherwise add to selection + if (first->selected() && second->selected()) { + _pm._selection.erase(first.ptr()); + _pm._selection.erase(second.ptr()); + } else { + _pm._selection.insert(first.ptr()); + _pm._selection.insert(second.ptr()); + } + } else { + // without Shift, take selection + _pm._selection.clear(); + _pm._selection.insert(first.ptr()); + _pm._selection.insert(second.ptr()); + } + return true; +} + +bool CurveDragPoint::doubleclicked(GdkEventButton *event) +{ + if (event->button != 1 || !first || !first.next()) return false; + _insertNode(true); + return true; +} + +void CurveDragPoint::_insertNode(bool take_selection) +{ + // The purpose of this call is to make way for the just created node. + // Otherwise clicks on the new node would only work after the user moves the mouse a bit. + // PathManipulator will restore visibility when necessary. + setVisible(false); + NodeList::iterator inserted = _pm.subdivideSegment(first, _t); + if (take_selection) { + _pm._selection.clear(); + } + _pm._selection.insert(inserted.ptr()); + + _pm.update(); + _pm._commit(_("Add node")); +} + +Glib::ustring CurveDragPoint::_getTip(unsigned state) +{ + if (_pm.empty()) return ""; + if (!first || !first.next()) return ""; + bool linear = first->front()->isDegenerate() && first.next()->back()->isDegenerate(); + if (state_held_shift(state)) { + return C_("Path segment tip", + "<b>Shift:</b> click to toggle segment selection"); + } + if (state_held_control(state) && state_held_alt(state)) { + return C_("Path segment tip", + "<b>Ctrl+Alt:</b> click to insert a node"); + } + if (linear) { + return C_("Path segment tip", + "<b>Linear segment:</b> drag to convert to a Bezier segment, " + "doubleclick to insert node, click to select (more: Shift, Ctrl+Alt)"); + } else { + return C_("Path segment tip", + "<b>Bezier segment:</b> drag to shape the segment, doubleclick to insert node, " + "click to select (more: Shift, Ctrl+Alt)"); + } +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/curve-drag-point.h b/src/ui/tool/curve-drag-point.h new file mode 100644 index 000000000..288ae6a8e --- /dev/null +++ b/src/ui/tool/curve-drag-point.h @@ -0,0 +1,62 @@ +/** @file + * Control point that is dragged during path drag + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_CURVE_DRAG_POINT_H +#define SEEN_UI_TOOL_CURVE_DRAG_POINT_H + +#include "ui/tool/control-point.h" +#include "ui/tool/node.h" + +class SPDesktop; +namespace Inkscape { +namespace UI { + +class PathManipulator; +struct PathSharedData; + +class CurveDragPoint : public ControlPoint { +public: + CurveDragPoint(PathManipulator &pm); + void setSize(double sz) { _setSize(sz); } + void setTimeValue(double t) { _t = t; } + void setIterator(NodeList::iterator i) { first = i; } + virtual bool _eventHandler(GdkEvent *event); +protected: + virtual Glib::ustring _getTip(unsigned state); + virtual void dragged(Geom::Point &, GdkEventMotion *); + virtual bool grabbed(GdkEventMotion *); + virtual void ungrabbed(GdkEventButton *); + virtual bool clicked(GdkEventButton *); + virtual bool doubleclicked(GdkEventButton *); + +private: + void _insertNode(bool take_selection); + double _t; + PathManipulator &_pm; + NodeList::iterator first; + static bool _drags_stroke; + static Geom::Point _stroke_drag_origin; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/event-utils.cpp b/src/ui/tool/event-utils.cpp new file mode 100644 index 000000000..91b2cdb04 --- /dev/null +++ b/src/ui/tool/event-utils.cpp @@ -0,0 +1,156 @@ +/** @file + * Collection of shorthands to deal with GDK events. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <cstring> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include "display/sp-canvas.h" +#include "ui/tool/event-utils.h" + +namespace Inkscape { +namespace UI { + + +guint shortcut_key(GdkEventKey const &event) +{ + guint shortcut_key = 0; + gdk_keymap_translate_keyboard_state( + gdk_keymap_get_for_display(gdk_display_get_default()), + event.hardware_keycode, + (GdkModifierType) event.state, + 0 /*event->key.group*/, + &shortcut_key, NULL, NULL, NULL); + return shortcut_key; +} + +unsigned combine_key_events(guint keyval, gint mask) +{ + GdkEvent *event_next; + gint i = 0; + + event_next = gdk_event_get(); + // while the next event is also a key notify with the same keyval and mask, + while (event_next && (event_next->type == GDK_KEY_PRESS || event_next->type == GDK_KEY_RELEASE) + && event_next->key.keyval == keyval + && (!mask || event_next->key.state & mask)) { + if (event_next->type == GDK_KEY_PRESS) + i ++; + // kill it + gdk_event_free(event_next); + // get next + event_next = gdk_event_get(); + } + // otherwise, put it back onto the queue + if (event_next) gdk_event_put(event_next); + + return i; +} + +unsigned combine_motion_events(SPCanvas *canvas, GdkEventMotion &event, gint mask) +{ + GdkEvent *event_next; + gint i = 0; + event.x -= canvas->x0; + event.y -= canvas->y0; + + event_next = gdk_event_get(); + // while the next event is also a motion notify + while (event_next && event_next->type == GDK_MOTION_NOTIFY + && (!mask || event_next->motion.state & mask)) + { + if (event_next->motion.device == event.device) { + GdkEventMotion &next = event_next->motion; + event.send_event = next.send_event; + event.time = next.time; + event.x = next.x; + event.y = next.y; + event.state = next.state; + event.is_hint = next.is_hint; + event.x_root = next.x_root; + event.y_root = next.y_root; + if (event.axes && next.axes) { + memcpy(event.axes, next.axes, event.device->num_axes); + } + } + + // kill it + gdk_event_free(event_next); + event_next = gdk_event_get(); + i++; + } + // otherwise, put it back onto the queue + if (event_next) + gdk_event_put(event_next); + event.x += canvas->x0; + event.y += canvas->y0; + + return i; +} + +/** Returns the modifier state valid after this event. Use this when you process events + * that change the modifier state. Currently handles only Shift, Ctrl, Alt. */ +unsigned state_after_event(GdkEvent *event) +{ + unsigned state = 0; + switch (event->type) { + case GDK_KEY_PRESS: + state = event->key.state; + switch(shortcut_key(event->key)) { + case GDK_Shift_L: + case GDK_Shift_R: + state |= GDK_SHIFT_MASK; + break; + case GDK_Control_L: + case GDK_Control_R: + state |= GDK_CONTROL_MASK; + break; + case GDK_Alt_L: + case GDK_Alt_R: + state |= GDK_MOD1_MASK; + break; + default: break; + } + break; + case GDK_KEY_RELEASE: + state = event->key.state; + switch(shortcut_key(event->key)) { + case GDK_Shift_L: + case GDK_Shift_R: + state &= ~GDK_SHIFT_MASK; + break; + case GDK_Control_L: + case GDK_Control_R: + state &= ~GDK_CONTROL_MASK; + break; + case GDK_Alt_L: + case GDK_Alt_R: + state &= ~GDK_MOD1_MASK; + break; + default: break; + } + break; + default: break; + } + return state; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/event-utils.h b/src/ui/tool/event-utils.h new file mode 100644 index 000000000..784855f56 --- /dev/null +++ b/src/ui/tool/event-utils.h @@ -0,0 +1,132 @@ +/** @file + * Collection of shorthands to deal with GDK events. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_EVENT_UTILS_H +#define SEEN_UI_TOOL_EVENT_UTILS_H + +#include <gdk/gdk.h> +#include <2geom/point.h> + +struct SPCanvas; + +namespace Inkscape { +namespace UI { + +inline bool state_held_shift(unsigned state) { + return state & GDK_SHIFT_MASK; +} +inline bool state_held_control(unsigned state) { + return state & GDK_CONTROL_MASK; +} +inline bool state_held_alt(unsigned state) { + return state & GDK_MOD1_MASK; +} +inline bool state_held_only_shift(unsigned state) { + return (state & GDK_SHIFT_MASK) && !(state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)); +} +inline bool state_held_only_control(unsigned state) { + return (state & GDK_CONTROL_MASK) && !(state & (GDK_SHIFT_MASK | GDK_MOD1_MASK)); +} +inline bool state_held_only_alt(unsigned state) { + return (state & GDK_MOD1_MASK) && !(state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)); +} +inline bool state_held_any_modifiers(unsigned state) { + return state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK); +} +inline bool state_held_no_modifiers(unsigned state) { + return !state_held_any_modifiers(state); +} +template <unsigned button> +inline bool state_held_button(unsigned state) { + return (button == 0 || button > 5) ? false : state & (GDK_BUTTON1_MASK << (button-1)); +} + + +/** Checks whether Shift was held when the event was generated. */ +template <typename E> +inline bool held_shift(E const &event) { + return state_held_shift(event.state); +} + +/** Checks whether Control was held when the event was generated. */ +template <typename E> +inline bool held_control(E const &event) { + return state_held_control(event.state); +} + +/** Checks whether Alt was held when the event was generated. */ +template <typename E> +inline bool held_alt(E const &event) { + return state_held_alt(event.state); +} + +/** True if from the set of Ctrl, Shift and Alt only Ctrl was held when the event + * was generated. */ +template <typename E> +inline bool held_only_control(E const &event) { + return state_held_only_control(event.state); +} + +/** True if from the set of Ctrl, Shift and Alt only Shift was held when the event + * was generated. */ +template <typename E> +inline bool held_only_shift(E const &event) { + return state_held_only_shift(event.state); +} + +/** True if from the set of Ctrl, Shift and Alt only Alt was held when the event + * was generated. */ +template <typename E> +inline bool held_only_alt(E const &event) { + return state_held_only_alt(event.state); +} + +template <typename E> +inline bool held_no_modifiers(E const &event) { + return state_held_no_modifiers(event.state); +} + +template <typename E> +inline bool held_any_modifiers(E const &event) { + return state_held_any_modifiers(event.state); +} + +template <typename E> +inline Geom::Point event_point(E const &event) { + return Geom::Point(event.x, event.y); +} + +/** Use like this: + * @code if (held_button<2>(event->motion)) { ... @endcode */ +template <unsigned button, typename E> +inline bool held_button(E const &event) { + return state_held_button<button>(event.state); +} + +guint shortcut_key(GdkEventKey const &event); +unsigned combine_key_events(guint keyval, gint mask); +unsigned combine_motion_events(SPCanvas *canvas, GdkEventMotion &event, gint mask); +unsigned state_after_event(GdkEvent *event); + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/manipulator.cpp b/src/ui/tool/manipulator.cpp new file mode 100644 index 000000000..b532fcab4 --- /dev/null +++ b/src/ui/tool/manipulator.cpp @@ -0,0 +1,89 @@ +/** @file + * Manipulator base class and manipulator group - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "ui/tool/manipulator.h" +#include "ui/tool/node.h" + +namespace Inkscape { +namespace UI { + +/* +void Manipulator::_grabEvents() +{ + if (_group) _group->_grabEvents(boost::shared_ptr<Manipulator>(this)); +} +void Manipulator::_ungrabEvents() +{ + if (_group) _group->_ungrabEvents(boost::shared_ptr<Manipulator>(this)); +} + +ManipulatorGroup::ManipulatorGroup(SPDesktop *d) : + _desktop(d) +{ +} +ManipulatorGroup::~ManipulatorGroup() +{ +} + +void ManipulatorGroup::_grabEvents(boost::shared_ptr<Manipulator> m) +{ + if (!_grab) _grab = m; +} +void ManipulatorGroup::_ungrabEvents(boost::shared_ptr<Manipulator> m) +{ + if (_grab == m) _grab.reset(); +} + +void ManipulatorGroup::add(boost::shared_ptr<Manipulator> m) +{ + m->_group = this; + push_back(m); +} +void ManipulatorGroup::remove(boost::shared_ptr<Manipulator> m) +{ + for (std::list<boost::shared_ptr<Manipulator> >::iterator i = begin(); i != end(); ++i) { + if ((*i) == m) { + erase(i); + break; + } + } + m->_group = 0; +} + +void ManipulatorGroup::clear() +{ + std::list<boost::shared_ptr<Manipulator> >::clear(); +} + +bool ManipulatorGroup::event(GdkEvent *event) +{ + if (_grab) { + return _grab->event(event); + } + + for (std::list<boost::shared_ptr<Manipulator> >::iterator i = begin(); i != end(); ++i) { + if ((*i)->event(event) || _grab) return true; + } + return false; +}*/ + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/manipulator.h b/src/ui/tool/manipulator.h new file mode 100644 index 000000000..799dad0d3 --- /dev/null +++ b/src/ui/tool/manipulator.h @@ -0,0 +1,165 @@ +/** @file + * Manipulator - edits something on-canvas + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_MANIPULATOR_H +#define SEEN_UI_TOOL_MANIPULATOR_H + +#include <set> +#include <map> +#include <sigc++/sigc++.h> +#include <glib.h> +#include <gdk/gdk.h> +#include <boost/shared_ptr.hpp> + +class SPDesktop; +namespace Inkscape { +namespace UI { + +class ManipulatorGroup; +class ControlPointSelection; + +/** + * @brief Tool component that processes events and does something in response to them. + * Note: this class is probably redundant. + */ +class Manipulator { +friend class ManipulatorGroup; +public: + Manipulator(SPDesktop *d) + : _desktop(d) + {} + virtual ~Manipulator() {} + + /// Handle input event. Returns true if handled. + virtual bool event(GdkEvent *)=0; +protected: + SPDesktop *const _desktop; +}; + +/** + * @brief Tool component that edits something on the canvas using selectable control points. + * Note: this class is probably redundant. + */ +class PointManipulator : public Manipulator, public sigc::trackable { +public: + PointManipulator(SPDesktop *d, ControlPointSelection &sel) + : Manipulator(d) + , _selection(sel) + {} +protected: + ControlPointSelection &_selection; +}; + +/** Manipulator that aggregates several manipulators of the same type. + * The order of invoking events on the member manipulators is undefined. + * To make this class more useful, derive from it and add actions that can be performed + * on all manipulators in the set. + * + * This is not used at the moment and is probably useless. */ +template <typename T> +class MultiManipulator : public PointManipulator { +public: + //typedef typename T::ItemType ItemType; + typedef typename std::pair<void*, boost::shared_ptr<T> > MapPair; + typedef typename std::map<void*, boost::shared_ptr<T> > MapType; + + MultiManipulator(SPDesktop *d, ControlPointSelection &sel) + : PointManipulator(d, sel) + {} + void addItem(void *item) { + boost::shared_ptr<T> m(_createManipulator(item)); + _mmap.insert(MapPair(item, m)); + } + void removeItem(void *item) { + _mmap.erase(item); + } + void clear() { + _mmap.clear(); + } + bool contains(void *item) { + return _mmap.find(item) != _mmap.end(); + } + bool empty() { + return _mmap.empty(); + } + void setItems(GSList const *list) { + std::set<void*> to_remove; + for (typename MapType::iterator mi = _mmap.begin(); mi != _mmap.end(); ++mi) { + to_remove.insert(mi->first); + } + for (GSList *i = const_cast<GSList*>(list); i; i = i->next) { + if (_isItemType(i->data)) { + // erase returns the number of items removed + // if nothing was removed, it means this item did not have a manipulator - add it + if (!to_remove.erase(i->data)) addItem(i->data); + } + } + typedef typename std::set<void*>::iterator RmIter; + for (RmIter ri = to_remove.begin(); ri != to_remove.end(); ++ri) { + removeItem(*ri); + } + } + + /** Invoke a method on all managed manipulators. + * Example: + * @code m.invokeForAll(&SomeManipulator::someMethod); @endcode + */ + template <typename R> + void invokeForAll(R (T::*method)()) { + for (typename MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(); + } + } + template <typename R, typename A> + void invokeForAll(R (T::*method)(A), A a) { + for (typename MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(a); + } + } + template <typename R, typename A> + void invokeForAll(R (T::*method)(A const &), A const &a) { + for (typename MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(a); + } + } + template <typename R, typename A, typename B> + void invokeForAll(R (T::*method)(A,B), A a, B b) { + for (typename MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(a, b); + } + } + + virtual bool event(GdkEvent *event) { + for (typename MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + if ((*i).second->event(event)) return true; + } + return false; + } +protected: + virtual T *_createManipulator(void *item) = 0; + virtual bool _isItemType(void *item) = 0; + MapType _mmap; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/modifier-tracker.cpp b/src/ui/tool/modifier-tracker.cpp new file mode 100644 index 000000000..8c6033bc7 --- /dev/null +++ b/src/ui/tool/modifier-tracker.cpp @@ -0,0 +1,93 @@ +/** @file + * Fine-grained modifier tracker for event handling. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include "ui/tool/event-utils.h" +#include "ui/tool/modifier-tracker.h" + +namespace Inkscape { +namespace UI { + +ModifierTracker::ModifierTracker() + : _left_shift(false) + , _right_shift(false) + , _left_ctrl(false) + , _right_ctrl(false) + , _left_alt(false) + , _right_alt(false) +{} + +bool ModifierTracker::event(GdkEvent *event) +{ + switch (event->type) { + case GDK_KEY_PRESS: + switch (shortcut_key(event->key)) { + case GDK_Shift_L: + _left_shift = true; + break; + case GDK_Shift_R: + _right_shift = true; + break; + case GDK_Control_L: + _left_ctrl = true; + break; + case GDK_Control_R: + _right_ctrl = true; + break; + case GDK_Alt_L: + _left_alt = true; + break; + case GDK_Alt_R: + _right_alt = true; + break; + } + break; + case GDK_KEY_RELEASE: + switch (shortcut_key(event->key)) { + case GDK_Shift_L: + _left_shift = false; + break; + case GDK_Shift_R: + _right_shift = false; + break; + case GDK_Control_L: + _left_ctrl = false; + break; + case GDK_Control_R: + _right_ctrl = false; + break; + case GDK_Alt_L: + _left_alt = false; + break; + case GDK_Alt_R: + _right_alt = false; + break; + } + break; + default: break; + } + + return false; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/modifier-tracker.h b/src/ui/tool/modifier-tracker.h new file mode 100644 index 000000000..55538ead6 --- /dev/null +++ b/src/ui/tool/modifier-tracker.h @@ -0,0 +1,54 @@ +/** @file + * Fine-grained modifier tracker for event handling. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_MODIFIER_TRACKER_H +#define SEEN_UI_TOOL_MODIFIER_TRACKER_H + +#include <gdk/gdk.h> + +namespace Inkscape { +namespace UI { + +class ModifierTracker { +public: + ModifierTracker(); + bool event(GdkEvent *); + + bool leftShift() const { return _left_shift; } + bool rightShift() const { return _right_shift; } + bool leftControl() const { return _left_ctrl; } + bool rightControl() const { return _right_ctrl; } + bool leftAlt() const { return _left_alt; } + bool rightAlt() const { return _right_alt; } + +private: + bool _left_shift; + bool _right_shift; + bool _left_ctrl; + bool _right_ctrl; + bool _left_alt; + bool _right_alt; +}; + +} // namespace UI +} // namespace Inkscape + +#endif // SEEN_UI_TOOL_MODIFIER_TRACKER_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/multi-path-manipulator.cpp b/src/ui/tool/multi-path-manipulator.cpp new file mode 100644 index 000000000..2025a12d7 --- /dev/null +++ b/src/ui/tool/multi-path-manipulator.cpp @@ -0,0 +1,717 @@ +/** @file + * Multi path manipulator - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <boost/shared_ptr.hpp> +#include <glib.h> +#include <glibmm/i18n.h> +#include "desktop.h" +#include "desktop-handles.h" +#include "document.h" +#include "live_effects/lpeobject.h" +#include "message-stack.h" +#include "preferences.h" +#include "sp-path.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/node.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/path-manipulator.h" +#include "util/unordered-containers.h" + +#ifdef USE_GNU_HASHES +namespace __gnu_cxx { +template<> +struct hash<Inkscape::UI::NodeList::iterator> { + size_t operator()(Inkscape::UI::NodeList::iterator const &n) const { + return reinterpret_cast<size_t>(n.ptr()); + } +}; +} // namespace __gnu_cxx +#endif // USE_GNU_HASHES + +namespace Inkscape { +namespace UI { + +namespace { + +struct hash_nodelist_iterator + : public std::unary_function<NodeList::iterator, std::size_t> +{ + std::size_t operator()(NodeList::iterator i) const { + return INK_HASH<NodeList::iterator::pointer>()(&*i); + } +}; + +typedef std::pair<NodeList::iterator, NodeList::iterator> IterPair; +typedef std::vector<IterPair> IterPairList; +typedef INK_UNORDERED_SET<NodeList::iterator, hash_nodelist_iterator> IterSet; +typedef std::multimap<double, IterPair> DistanceMap; +typedef std::pair<double, IterPair> DistanceMapItem; + +/** Find pairs of selected endnodes suitable for joining. */ +void find_join_iterators(ControlPointSelection &sel, IterPairList &pairs) +{ + IterSet join_iters; + DistanceMap dists; + + // find all endnodes in selection + for (ControlPointSelection::iterator i = sel.begin(); i != sel.end(); ++i) { + Node *node = dynamic_cast<Node*>(*i); + if (!node) continue; + NodeList::iterator iter = NodeList::get_iterator(node); + if (!iter.next() || !iter.prev()) join_iters.insert(iter); + } + + if (join_iters.size() < 2) return; + + // Below we find the closest pairs. The algorithm is O(N^3). + // We can go down to O(N^2 log N) by using O(N^2) memory, by putting all pairs + // with their distances in a multimap (not worth it IMO). + while (join_iters.size() >= 2) { + double closest = DBL_MAX; + IterPair closest_pair; + for (IterSet::iterator i = join_iters.begin(); i != join_iters.end(); ++i) { + for (IterSet::iterator j = join_iters.begin(); j != i; ++j) { + double dist = Geom::distance(**i, **j); + if (dist < closest) { + closest = dist; + closest_pair = std::make_pair(*i, *j); + } + } + } + pairs.push_back(closest_pair); + join_iters.erase(closest_pair.first); + join_iters.erase(closest_pair.second); + } +} + +/** After this function, first should be at the end of path and second at the beginnning. + * @returns True if the nodes are in the same subpath */ +bool prepare_join(IterPair &join_iters) +{ + if (&NodeList::get(join_iters.first) == &NodeList::get(join_iters.second)) { + if (join_iters.first.next()) // if first is begin, swap the iterators + std::swap(join_iters.first, join_iters.second); + return true; + } + + NodeList &sp_first = NodeList::get(join_iters.first); + NodeList &sp_second = NodeList::get(join_iters.second); + if (join_iters.first.next()) { // first is begin + if (join_iters.second.next()) { // second is begin + sp_first.reverse(); + } else { // second is end + std::swap(join_iters.first, join_iters.second); + } + } else { // first is end + if (join_iters.second.next()) { // second is begin + // do nothing + } else { // second is end + sp_second.reverse(); + } + } + return false; +} +} // anonymous namespace + + +MultiPathManipulator::MultiPathManipulator(PathSharedData &data, sigc::connection &chg) + : PointManipulator(data.node_data.desktop, *data.node_data.selection) + , _path_data(data) + , _changed(chg) +{ + _selection.signal_commit.connect( + sigc::mem_fun(*this, &MultiPathManipulator::_commit)); + _selection.signal_point_changed.connect( + sigc::hide( sigc::hide( + signal_coords_changed.make_slot()))); +} + +MultiPathManipulator::~MultiPathManipulator() +{ + _mmap.clear(); +} + +/** Remove empty manipulators. */ +void MultiPathManipulator::cleanup() +{ + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ) { + if (i->second->empty()) _mmap.erase(i++); + else ++i; + } +} + +/** @brief Change the set of items to edit. + * + * This method attempts to preserve as much of the state as possible. */ +void MultiPathManipulator::setItems(std::set<ShapeRecord> const &s) +{ + std::set<ShapeRecord> shapes(s); + + // iterate over currently edited items, modifying / removing them as necessary + for (MapType::iterator i = _mmap.begin(); i != _mmap.end();) { + std::set<ShapeRecord>::iterator si = shapes.find(i->first); + if (si == shapes.end()) { + // This item is no longer supposed to be edited - remove its manipulator + _mmap.erase(i++); + } else { + ShapeRecord const &sr = i->first; + ShapeRecord const &sr_new = *si; + // if the shape record differs, replace the key only and modify other values + if (sr.edit_transform != sr_new.edit_transform || + sr.role != sr_new.role) + { + boost::shared_ptr<PathManipulator> hold(i->second); + if (sr.edit_transform != sr_new.edit_transform) + hold->setControlsTransform(sr_new.edit_transform); + if (sr.role != sr_new.role) { + //hold->setOutlineColor(_getOutlineColor(sr_new.role)); + } + _mmap.erase(sr); + _mmap.insert(std::make_pair(sr_new, hold)); + } + shapes.erase(si); // remove the processed record + ++i; + } + } + + // add newly selected items + for (std::set<ShapeRecord>::iterator i = shapes.begin(); i != shapes.end(); ++i) { + ShapeRecord const &r = *i; + if (!SP_IS_PATH(r.item) && !IS_LIVEPATHEFFECT(r.item)) continue; + boost::shared_ptr<PathManipulator> newpm(new PathManipulator(*this, (SPPath*) r.item, + r.edit_transform, _getOutlineColor(r.role), r.lpe_key)); + newpm->showHandles(_show_handles); + // always show outlines for clips and masks + newpm->showOutline(_show_outline || r.role != SHAPE_ROLE_NORMAL); + newpm->showPathDirection(_show_path_direction); + newpm->setLiveOutline(_live_outline); + newpm->setLiveObjects(_live_objects); + _mmap.insert(std::make_pair(r, newpm)); + } +} + +void MultiPathManipulator::selectSubpaths() +{ + if (_selection.empty()) { + _selection.selectAll(); + } else { + invokeForAll(&PathManipulator::selectSubpaths); + } +} + +void MultiPathManipulator::shiftSelection(int dir) +{ + invokeForAll(&PathManipulator::shiftSelection, dir); +} + +void MultiPathManipulator::invertSelectionInSubpaths() +{ + invokeForAll(&PathManipulator::invertSelectionInSubpaths); +} + +void MultiPathManipulator::setNodeType(NodeType type) +{ + if (_selection.empty()) return; + for (ControlPointSelection::iterator i = _selection.begin(); i != _selection.end(); ++i) { + Node *node = dynamic_cast<Node*>(*i); + if (node) node->setType(type); + } + _done(_("Change node type")); +} + +void MultiPathManipulator::setSegmentType(SegmentType type) +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::setSegmentType, type); + if (type == SEGMENT_STRAIGHT) { + _done(_("Straighten segments")); + } else { + _done(_("Make segments curves")); + } +} + +void MultiPathManipulator::insertNodes() +{ + invokeForAll(&PathManipulator::insertNodes); + _done(_("Add nodes")); +} + +void MultiPathManipulator::joinNodes() +{ + invokeForAll(&PathManipulator::hideDragPoint); + // Node join has two parts. In the first one we join two subpaths by fusing endpoints + // into one. In the second we fuse nodes in each subpath. + IterPairList joins; + NodeList::iterator preserve_pos; + Node *mouseover_node = dynamic_cast<Node*>(ControlPoint::mouseovered_point); + if (mouseover_node) { + preserve_pos = NodeList::get_iterator(mouseover_node); + } + find_join_iterators(_selection, joins); + + for (IterPairList::iterator i = joins.begin(); i != joins.end(); ++i) { + bool same_path = prepare_join(*i); + NodeList &sp_first = NodeList::get(i->first); + NodeList &sp_second = NodeList::get(i->second); + i->first->setType(NODE_CUSP, false); + + Geom::Point joined_pos, pos_handle_front, pos_handle_back; + pos_handle_front = *i->second->front(); + pos_handle_back = *i->first->back(); + + // When we encounter the mouseover node, we unset the iterator - it will be invalidated + if (i->first == preserve_pos) { + joined_pos = *i->first; + preserve_pos = NodeList::iterator(); + } else if (i->second == preserve_pos) { + joined_pos = *i->second; + preserve_pos = NodeList::iterator(); + } else { + joined_pos = Geom::middle_point(*i->first, *i->second); + } + + // if the handles aren't degenerate, don't move them + i->first->move(joined_pos); + Node *joined_node = i->first.ptr(); + if (!i->second->front()->isDegenerate()) { + joined_node->front()->setPosition(pos_handle_front); + } + if (!i->first->back()->isDegenerate()) { + joined_node->back()->setPosition(pos_handle_back); + } + sp_second.erase(i->second); + + if (same_path) { + sp_first.setClosed(true); + } else { + sp_first.splice(sp_first.end(), sp_second); + sp_second.kill(); + } + _selection.insert(i->first.ptr()); + } + + if (joins.empty()) { + // Second part replaces contiguous selections of nodes with single nodes + invokeForAll(&PathManipulator::weldNodes, preserve_pos); + } + + _doneWithCleanup(_("Join nodes")); +} + +void MultiPathManipulator::breakNodes() +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::breakNodes); + _done(_("Break nodes")); +} + +void MultiPathManipulator::deleteNodes(bool keep_shape) +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::deleteNodes, keep_shape); + _doneWithCleanup(_("Delete nodes")); +} + +/** Join selected endpoints to create segments. */ +void MultiPathManipulator::joinSegments() +{ + IterPairList joins; + find_join_iterators(_selection, joins); + + for (IterPairList::iterator i = joins.begin(); i != joins.end(); ++i) { + bool same_path = prepare_join(*i); + NodeList &sp_first = NodeList::get(i->first); + NodeList &sp_second = NodeList::get(i->second); + i->first->setType(NODE_CUSP, false); + i->second->setType(NODE_CUSP, false); + if (same_path) { + sp_first.setClosed(true); + } else { + sp_first.splice(sp_first.end(), sp_second); + sp_second.kill(); + } + } + + if (joins.empty()) { + invokeForAll(&PathManipulator::weldSegments); + } + _doneWithCleanup("Join segments"); +} + +void MultiPathManipulator::deleteSegments() +{ + if (_selection.empty()) return; + invokeForAll(&PathManipulator::deleteSegments); + _doneWithCleanup("Delete segments"); +} + +void MultiPathManipulator::alignNodes(Geom::Dim2 d) +{ + _selection.align(d); + if (d == Geom::X) { + _done("Align nodes to a horizontal line"); + } else { + _done("Align nodes to a vertical line"); + } +} + +void MultiPathManipulator::distributeNodes(Geom::Dim2 d) +{ + _selection.distribute(d); + if (d == Geom::X) { + _done("Distrubute nodes horizontally"); + } else { + _done("Distribute nodes vertically"); + } +} + +void MultiPathManipulator::reverseSubpaths() +{ + if (_selection.empty()) { + invokeForAll(&PathManipulator::reverseSubpaths, false); + _done("Reverse subpaths"); + } else { + invokeForAll(&PathManipulator::reverseSubpaths, true); + _done("Reverse selected subpaths"); + } +} + +void MultiPathManipulator::move(Geom::Point const &delta) +{ + _selection.transform(Geom::Translate(delta)); + _done("Move nodes"); +} + +void MultiPathManipulator::showOutline(bool show) +{ + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + // always show outlines for clipping paths and masks + i->second->showOutline(show || i->first.role != SHAPE_ROLE_NORMAL); + } + _show_outline = show; +} + +void MultiPathManipulator::showHandles(bool show) +{ + invokeForAll(&PathManipulator::showHandles, show); + _show_handles = show; +} + +void MultiPathManipulator::showPathDirection(bool show) +{ + invokeForAll(&PathManipulator::showPathDirection, show); + _show_path_direction = show; +} + +/** @brief Set live outline update status + * When set to true, outline will be updated continuously when dragging + * or transforming nodes. Otherwise it will only update when changes are committed + * to XML. */ +void MultiPathManipulator::setLiveOutline(bool set) +{ + invokeForAll(&PathManipulator::setLiveOutline, set); + _live_outline = set; +} + +/** @brief Set live object update status + * When set to true, objects will be updated continuously when dragging + * or transforming nodes. Otherwise they will only update when changes are committed + * to XML. */ +void MultiPathManipulator::setLiveObjects(bool set) +{ + invokeForAll(&PathManipulator::setLiveObjects, set); + _live_objects = set; +} + +void MultiPathManipulator::updateOutlineColors() +{ + //for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + // i->second->setOutlineColor(_getOutlineColor(i->first.role)); + //} +} + +bool MultiPathManipulator::event(GdkEvent *event) +{ + _tracker.event(event); + guint key = 0; + if (event->type == GDK_KEY_PRESS) { + key = shortcut_key(event->key); + } + + // Single handle adjustments go here. + if (_selection.size() == 1 && event->type == GDK_KEY_PRESS) { + do { + Node *n = dynamic_cast<Node *>(*_selection.begin()); + if (!n) break; + + PathManipulator &pm = n->nodeList().subpathList().pm(); + + int which = 0; + if (_tracker.rightAlt() || _tracker.rightControl()) { + which = 1; + } + if (_tracker.leftAlt() || _tracker.leftControl()) { + if (which != 0) break; // ambiguous + which = -1; + } + if (which == 0) break; // no handle chosen + bool one_pixel = _tracker.leftAlt() || _tracker.rightAlt(); + bool handled = true; + + switch (key) { + // single handle functions + // rotation + case GDK_bracketleft: + case GDK_braceleft: + pm.rotateHandle(n, which, 1, one_pixel); + break; + case GDK_bracketright: + case GDK_braceright: + pm.rotateHandle(n, which, -1, one_pixel); + break; + // adjust length + case GDK_period: + case GDK_greater: + pm.scaleHandle(n, which, 1, one_pixel); + break; + case GDK_comma: + case GDK_less: + pm.scaleHandle(n, which, -1, one_pixel); + break; + default: + handled = false; + break; + } + + if (handled) return true; + } while(0); + } + + + switch (event->type) { + case GDK_KEY_PRESS: + switch (key) { + case GDK_Insert: + case GDK_KP_Insert: + // Insert - insert nodes in the middle of selected segments + insertNodes(); + return true; + case GDK_i: + case GDK_I: + if (held_only_shift(event->key)) { + // Shift+I - insert nodes (alternate keybinding for Mac keyboards + // that don't have the Insert key) + insertNodes(); + return true; + } + break; + case GDK_j: + case GDK_J: + if (held_only_shift(event->key)) { + // Shift+J - join nodes + joinNodes(); + return true; + } + if (held_only_alt(event->key)) { + // Alt+J - join segments + joinSegments(); + return true; + } + break; + case GDK_b: + case GDK_B: + if (held_only_shift(event->key)) { + // Shift+B - break nodes + breakNodes(); + return true; + } + break; + case GDK_Delete: + case GDK_KP_Delete: + case GDK_BackSpace: + if (held_shift(event->key)) break; + if (held_alt(event->key)) { + // Alt+Delete - delete segments + deleteSegments(); + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool del_preserves_shape = prefs->getBool("/tools/nodes/delete_preserves_shape", true); + // pass keep_shape = true when: + // a) del preserves shape, and control is not pressed + // b) ctrl+del preserves shape (del_preserves_shape is false), and control is pressed + // Hence xor + deleteNodes(del_preserves_shape ^ held_control(event->key)); + } + return true; + case GDK_c: + case GDK_C: + if (held_only_shift(event->key)) { + // Shift+C - make nodes cusp + setNodeType(NODE_CUSP); + return true; + } + break; + case GDK_s: + case GDK_S: + if (held_only_shift(event->key)) { + // Shift+S - make nodes smooth + setNodeType(NODE_SMOOTH); + return true; + } + break; + case GDK_a: + case GDK_A: + if (held_only_shift(event->key)) { + // Shift+A - make nodes auto-smooth + setNodeType(NODE_AUTO); + return true; + } + break; + case GDK_y: + case GDK_Y: + if (held_only_shift(event->key)) { + // Shift+Y - make nodes symmetric + setNodeType(NODE_SYMMETRIC); + return true; + } + break; + case GDK_r: + case GDK_R: + if (held_only_shift(event->key)) { + // Shift+R - reverse subpaths + reverseSubpaths(); + return true; + } + break; + default: + break; + } + break; + case GDK_MOTION_NOTIFY: + combine_motion_events(_desktop->canvas, event->motion, 0); + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + if (i->second->event(event)) return true; + } + break; + default: break; + } + + return false; +} + +/** Commit changes to XML and add undo stack entry based on the action that was done. Invoked + * by sub-manipulators, for example TransformHandleSet and ControlPointSelection. */ +void MultiPathManipulator::_commit(CommitEvent cps) +{ + gchar const *reason = NULL; + gchar const *key = NULL; + switch(cps) { + case COMMIT_MOUSE_MOVE: + reason = _("Move nodes"); + break; + case COMMIT_KEYBOARD_MOVE_X: + reason = _("Move nodes horizontally"); + key = "node:move:x"; + break; + case COMMIT_KEYBOARD_MOVE_Y: + reason = _("Move nodes vertically"); + key = "node:move:y"; + break; + case COMMIT_MOUSE_ROTATE: + reason = _("Rotate nodes"); + break; + case COMMIT_KEYBOARD_ROTATE: + reason = _("Rotate nodes"); + key = "node:rotate"; + break; + case COMMIT_MOUSE_SCALE_UNIFORM: + reason = _("Scale nodes uniformly"); + break; + case COMMIT_MOUSE_SCALE: + reason = _("Scale nodes"); + break; + case COMMIT_KEYBOARD_SCALE_UNIFORM: + reason = _("Scale nodes uniformly"); + key = "node:scale:uniform"; + break; + case COMMIT_KEYBOARD_SCALE_X: + reason = _("Scale nodes horizontally"); + key = "node:scale:x"; + break; + case COMMIT_KEYBOARD_SCALE_Y: + reason = _("Scale nodes vertically"); + key = "node:scale:y"; + break; + case COMMIT_FLIP_X: + reason = _("Flip nodes horizontally"); + break; + case COMMIT_FLIP_Y: + reason = _("Flip nodes vertically"); + break; + default: return; + } + + _selection.signal_update.emit(); + invokeForAll(&PathManipulator::writeXML); + if (key) { + sp_document_maybe_done(sp_desktop_document(_desktop), key, SP_VERB_CONTEXT_NODE, reason); + } else { + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_NODE, reason); + } + signal_coords_changed.emit(); +} + +/** Commits changes to XML and adds undo stack entry. */ +void MultiPathManipulator::_done(gchar const *reason) { + invokeForAll(&PathManipulator::update); + invokeForAll(&PathManipulator::writeXML); + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_NODE, reason); + signal_coords_changed.emit(); +} + +/** Commits changes to XML, adds undo stack entry and removes empty manipulators. */ +void MultiPathManipulator::_doneWithCleanup(gchar const *reason) { + _changed.block(); + _done(reason); + cleanup(); + _changed.unblock(); +} + +/** Get an outline color based on the shape's role (normal, mask, LPE parameter, etc.). */ +guint32 MultiPathManipulator::_getOutlineColor(ShapeRole role) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + switch(role) { + case SHAPE_ROLE_CLIPPING_PATH: + return prefs->getColor("/tools/nodes/clipping_path_color", 0x00ff00ff); + case SHAPE_ROLE_MASK: + return prefs->getColor("/tools/nodes/mask_color", 0x0000ffff); + case SHAPE_ROLE_LPE_PARAM: + return prefs->getColor("/tools/nodes/lpe_param_color", 0x009000ff); + case SHAPE_ROLE_NORMAL: + default: + return prefs->getColor("/tools/nodes/outline_color", 0xff0000ff); + } +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h new file mode 100644 index 000000000..181ae6d1d --- /dev/null +++ b/src/ui/tool/multi-path-manipulator.h @@ -0,0 +1,138 @@ +/** @file + * Multi path manipulator - a tool component that edits multiple paths at once + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_MULTI_PATH_MANIPULATOR_H +#define SEEN_UI_TOOL_MULTI_PATH_MANIPULATOR_H + +#include <sigc++/connection.h> +#include "display/display-forward.h" +#include "forward.h" +#include "ui/tool/commit-events.h" +#include "ui/tool/manipulator.h" +#include "ui/tool/modifier-tracker.h" +#include "ui/tool/node.h" +#include "ui/tool/node-types.h" +#include "ui/tool/shape-record.h" + +struct SPCanvasGroup; + +namespace Inkscape { +namespace UI { + +class PathManipulator; +class MultiPathManipulator; +struct PathSharedData; + +/** + * Manipulator that manages multiple path manipulators active at the same time. + */ +class MultiPathManipulator : public PointManipulator { +public: + MultiPathManipulator(PathSharedData &data, sigc::connection &chg); + virtual ~MultiPathManipulator(); + virtual bool event(GdkEvent *event); + + bool empty() { return _mmap.empty(); } + unsigned size() { return _mmap.empty(); } + void setItems(std::set<ShapeRecord> const &); + void clear() { _mmap.clear(); } + void cleanup(); + + void selectSubpaths(); + void shiftSelection(int dir); + void invertSelectionInSubpaths(); + + void setNodeType(NodeType t); + void setSegmentType(SegmentType t); + + void insertNodes(); + void joinNodes(); + void breakNodes(); + void deleteNodes(bool keep_shape = true); + void joinSegments(); + void deleteSegments(); + void alignNodes(Geom::Dim2 d); + void distributeNodes(Geom::Dim2 d); + void reverseSubpaths(); + void move(Geom::Point const &delta); + + void showOutline(bool show); + void showHandles(bool show); + void showPathDirection(bool show); + void setLiveOutline(bool set); + void setLiveObjects(bool set); + void updateOutlineColors(); + + sigc::signal<void> signal_coords_changed; /// Emitted whenever the coordinates + /// shown in the status bar need updating +private: + typedef std::pair<ShapeRecord, boost::shared_ptr<PathManipulator> > MapPair; + typedef std::map<ShapeRecord, boost::shared_ptr<PathManipulator> > MapType; + + template <typename R> + void invokeForAll(R (PathManipulator::*method)()) { + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(); + } + } + template <typename R, typename A> + void invokeForAll(R (PathManipulator::*method)(A), A a) { + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(a); + } + } + template <typename R, typename A> + void invokeForAll(R (PathManipulator::*method)(A const &), A const &a) { + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(a); + } + } + template <typename R, typename A, typename B> + void invokeForAll(R (PathManipulator::*method)(A,B), A a, B b) { + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { + ((i->second.get())->*method)(a, b); + } + } + + void _commit(CommitEvent cps); + void _done(gchar const *); + void _doneWithCleanup(gchar const *); + guint32 _getOutlineColor(ShapeRole role); + + MapType _mmap; +public: + PathSharedData const &_path_data; +private: + sigc::connection &_changed; + ModifierTracker _tracker; + bool _show_handles; + bool _show_outline; + bool _show_path_direction; + bool _live_outline; + bool _live_objects; + + friend class PathManipulator; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/node-tool.cpp b/src/ui/tool/node-tool.cpp new file mode 100644 index 000000000..443e7f258 --- /dev/null +++ b/src/ui/tool/node-tool.cpp @@ -0,0 +1,651 @@ +/** @file + * @brief New node tool - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> +#include <glib/gi18n.h> +#include "desktop.h" +#include "desktop-handles.h" +#include "display/canvas-bpath.h" +#include "display/curve.h" +#include "display/sp-canvas.h" +#include "document.h" +#include "live_effects/lpeobject.h" +#include "message-context.h" +#include "selection.h" +#include "shape-editor.h" // temporary! +#include "sp-clippath.h" +#include "sp-item-group.h" +#include "sp-mask.h" +#include "sp-object-group.h" +#include "sp-path.h" +#include "ui/tool/node-tool.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/curve-drag-point.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/manipulator.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/path-manipulator.h" +#include "ui/tool/selector.h" +#include "ui/tool/shape-record.h" + +#include "pixmaps/cursor-node.xpm" +#include "pixmaps/cursor-node-d.xpm" + +/** @struct InkNodeTool + * + * Node tool event context. + * + * @par Architectural overview of the tool + * @par + * Here's a breakdown of what each object does. + * - Handle: shows a handle and keeps the node type constraint (smooth / symmetric) by updating + * the other handle's position when dragged. Its move() method cannot violate the constraints. + * - Node: keeps node type constraints for auto nodes and smooth nodes at ends of linear segments. + * Its move() method cannot violate constraints. Handles linear grow and dispatches spatial grow + * to MultiPathManipulator. Keeps a reference to its NodeList. + * - NodeList: exposes an iterator-based interface to nodes. It is possible to obtain an iterator + * to a node from the node. Keeps a reference to its SubpathList. + * - SubpathList: list of NodeLists that represents an editable pathvector. Keeps a reference + * to its PathManipulator. + * - PathManipulator: performs most of the single-path actions like reverse subpaths, + * delete segment, shift selection, etc. Keeps a reference to MultiPathManipulator. + * - MultiPathManipulator: performs additional operations for actions that are not per-path, + * for example node joins and segment joins. Tracks the control transforms for PMs that edit + * clipping paths and masks. It is more or less equivalent to ShapeEditor and in the future + * it might handle all shapes. Handles XML commit of actions that affect all paths or + * the node selection and removes PathManipulators that have no nodes left after e.g. node + * deletes. + * - ControlPointSelection: keeps track of node selection and a set of nodes that can potentially + * be selected. There can be more than one selection. Performs actions that require no + * knowledge about the path, only about the nodes, like dragging and transforms. It is not + * specific to nodes and can accomodate any control point derived from SelectableControlPoint. + * Transforms nodes in response to transform handle events. + * - TransformHandleSet: displays nodeset transform handles and emits transform events. The aim + * is to eventually use a common class for object and control point transforms. + * - SelectableControlPoint: base for any type of selectable point. It can belong to only one + * selection. + * + * @par Plans for the future + * @par + * - MultiPathManipulator should become a generic shape editor that manages all active manipulator, + * more or less like the old ShapeEditor. + * - Knotholder should be rewritten into one manipulator class per shape, using the control point + * classes. Interesting features like dragging rectangle sides could be added along the way. + * - Better handling of clip and mask editing, particularly in response to undo. + * - High level refactoring of the event context hierarchy. All aspects of tools, like toolbox + * controls, icons, event handling should be collected in one class, though each aspect + * of a tool might be in an separate class for better modularity. The long term goal is to allow + * tools to be defined in extensions or shared library plugins. + */ + +namespace { +SPCanvasGroup *create_control_group(SPDesktop *d); +void ink_node_tool_class_init(InkNodeToolClass *klass); +void ink_node_tool_init(InkNodeTool *node_context); +void ink_node_tool_dispose(GObject *object); + +void ink_node_tool_setup(SPEventContext *ec); +gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event); +gint ink_node_tool_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event); +void ink_node_tool_set(SPEventContext *ec, Inkscape::Preferences::Entry *value); + +void ink_node_tool_update_tip(InkNodeTool *nt, GdkEvent *event); +void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel); +void ink_node_tool_select_area(InkNodeTool *nt, Geom::Rect const &, GdkEventButton *); +void ink_node_tool_select_point(InkNodeTool *nt, Geom::Point const &, GdkEventButton *); +void ink_node_tool_mouseover_changed(InkNodeTool *nt, Inkscape::UI::ControlPoint *p); +} // anonymous namespace + +GType ink_node_tool_get_type() +{ + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof(InkNodeToolClass), + NULL, NULL, + (GClassInitFunc) ink_node_tool_class_init, + NULL, NULL, + sizeof(InkNodeTool), + 4, + (GInstanceInitFunc) ink_node_tool_init, + NULL, /* value_table */ + }; + type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "InkNodeTool", &info, (GTypeFlags)0); + } + return type; +} + +namespace { + +SPCanvasGroup *create_control_group(SPDesktop *d) +{ + return reinterpret_cast<SPCanvasGroup*>(sp_canvas_item_new( + sp_desktop_controls(d), SP_TYPE_CANVAS_GROUP, NULL)); +} + +void destroy_group(SPCanvasGroup *g) +{ + gtk_object_destroy(GTK_OBJECT(g)); +} + +void ink_node_tool_class_init(InkNodeToolClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + SPEventContextClass *event_context_class = (SPEventContextClass *) klass; + + object_class->dispose = ink_node_tool_dispose; + + event_context_class->setup = ink_node_tool_setup; + event_context_class->set = ink_node_tool_set; + event_context_class->root_handler = ink_node_tool_root_handler; + event_context_class->item_handler = ink_node_tool_item_handler; +} + +void ink_node_tool_init(InkNodeTool *nt) +{ + SPEventContext *event_context = SP_EVENT_CONTEXT(nt); + + event_context->cursor_shape = cursor_node_xpm; + event_context->hot_x = 1; + event_context->hot_y = 1; + + new (&nt->_selection_changed_connection) sigc::connection(); + new (&nt->_selection_modified_connection) sigc::connection(); + new (&nt->_mouseover_changed_connection) sigc::connection(); + //new (&nt->_mgroup) Inkscape::UI::ManipulatorGroup(nt->desktop); + new (&nt->_selected_nodes) CSelPtr(); + new (&nt->_multipath) MultiPathPtr(); + new (&nt->_selector) SelectorPtr(); + new (&nt->_path_data) PathSharedDataPtr(); +} + +void ink_node_tool_dispose(GObject *object) +{ + InkNodeTool *nt = INK_NODE_TOOL(object); + + nt->enableGrDrag(false); + + nt->_selection_changed_connection.disconnect(); + nt->_selection_modified_connection.disconnect(); + nt->_mouseover_changed_connection.disconnect(); + nt->_multipath.~MultiPathPtr(); + nt->_selected_nodes.~CSelPtr(); + nt->_selector.~SelectorPtr(); + + Inkscape::UI::PathSharedData &data = *nt->_path_data; + destroy_group(data.node_data.node_group); + destroy_group(data.node_data.handle_group); + destroy_group(data.node_data.handle_line_group); + destroy_group(data.outline_group); + destroy_group(data.dragpoint_group); + destroy_group(nt->_transform_handle_group); + + nt->_path_data.~PathSharedDataPtr(); + nt->_selection_changed_connection.~connection(); + nt->_selection_modified_connection.~connection(); + nt->_mouseover_changed_connection.~connection(); + + if (nt->_node_message_context) { + delete nt->_node_message_context; + } + if (nt->shape_editor) { + nt->shape_editor->unset_item(SH_KNOTHOLDER); + delete nt->shape_editor; + } + + G_OBJECT_CLASS(g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)))->dispose(object); +} + +void ink_node_tool_setup(SPEventContext *ec) +{ + InkNodeTool *nt = INK_NODE_TOOL(ec); + + SPEventContextClass *parent = (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent->setup) parent->setup(ec); + + nt->_node_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack()); + + nt->_path_data.reset(new Inkscape::UI::PathSharedData()); + Inkscape::UI::PathSharedData &data = *nt->_path_data; + data.node_data.desktop = nt->desktop; + + // selector has to be created here, so that its hidden control point is on the bottom + nt->_selector.reset(new Inkscape::UI::Selector(nt->desktop)); + + // Prepare canvas groups for controls. This guarantees correct z-order, so that + // for example a dragpoint won't obscure a node + data.outline_group = create_control_group(nt->desktop); + data.node_data.handle_line_group = create_control_group(nt->desktop); + data.dragpoint_group = create_control_group(nt->desktop); + nt->_transform_handle_group = create_control_group(nt->desktop); + data.node_data.node_group = create_control_group(nt->desktop); + data.node_data.handle_group = create_control_group(nt->desktop); + + Inkscape::Selection *selection = sp_desktop_selection (ec->desktop); + nt->_selection_changed_connection.disconnect(); + nt->_selection_changed_connection = + selection->connectChanged( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_selection_changed), + nt)); + /*nt->_selection_modified_connection.disconnect(); + nt->_selection_modified_connection = + selection->connectModified( + sigc::hide(sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_selection_modified), + nt)));*/ + nt->_mouseover_changed_connection.disconnect(); + nt->_mouseover_changed_connection = + Inkscape::UI::ControlPoint::signal_mouseover_change.connect( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_mouseover_changed), + nt)); + + nt->_selected_nodes.reset( + new Inkscape::UI::ControlPointSelection(nt->desktop, nt->_transform_handle_group)); + data.node_data.selection = nt->_selected_nodes.get(); + nt->_multipath.reset(new Inkscape::UI::MultiPathManipulator(data, + nt->_selection_changed_connection)); + + nt->_selector->signal_point.connect( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_select_point), + nt)); + nt->_selector->signal_area.connect( + sigc::bind<0>( + sigc::ptr_fun(&ink_node_tool_select_area), + nt)); + + nt->_multipath->signal_coords_changed.connect( + sigc::bind( + sigc::mem_fun(*nt->desktop, &SPDesktop::emitToolSubselectionChanged), + (void*) 0)); + nt->_selected_nodes->signal_point_changed.connect( + sigc::hide( sigc::hide( + sigc::bind( + sigc::bind( + sigc::ptr_fun(ink_node_tool_update_tip), + (GdkEvent*)0), + nt)))); + + nt->cursor_drag = false; + nt->show_transform_handles = true; + nt->single_node_transform_handles = false; + nt->flash_tempitem = NULL; + nt->flashed_item = NULL; + nt->_last_over = NULL; + // TODO long term, fold ShapeEditor into MultiPathManipulator and rename MPM + // to something better + nt->shape_editor = new ShapeEditor(nt->desktop); + + // read prefs before adding items to selection to prevent momentarily showing the outline + sp_event_context_read(nt, "show_handles"); + sp_event_context_read(nt, "show_outline"); + sp_event_context_read(nt, "live_outline"); + sp_event_context_read(nt, "live_objects"); + sp_event_context_read(nt, "show_path_direction"); + sp_event_context_read(nt, "show_transform_handles"); + sp_event_context_read(nt, "single_node_transform_handles"); + sp_event_context_read(nt, "edit_clipping_paths"); + sp_event_context_read(nt, "edit_masks"); + + ink_node_tool_selection_changed(nt, selection); + ink_node_tool_update_tip(nt, NULL); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/tools/nodes/selcue")) { + ec->enableSelectionCue(); + } + if (prefs->getBool("/tools/nodes/gradientdrag")) { + ec->enableGrDrag(); + } + + nt->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive +} + +void ink_node_tool_set(SPEventContext *ec, Inkscape::Preferences::Entry *value) +{ + InkNodeTool *nt = INK_NODE_TOOL(ec); + Glib::ustring entry_name = value->getEntryName(); + + if (entry_name == "show_handles") { + nt->_multipath->showHandles(value->getBool(true)); + } else if (entry_name == "show_outline") { + nt->show_outline = value->getBool(); + nt->_multipath->showOutline(nt->show_outline); + } else if (entry_name == "live_outline") { + nt->live_outline = value->getBool(); + nt->_multipath->setLiveOutline(nt->live_outline); + } else if (entry_name == "live_objects") { + nt->live_objects = value->getBool(); + nt->_multipath->setLiveObjects(nt->live_objects); + } else if (entry_name == "show_path_direction") { + nt->show_path_direction = value->getBool(); + nt->_multipath->showPathDirection(nt->show_path_direction); + } else if (entry_name == "show_transform_handles") { + nt->show_transform_handles = value->getBool(true); + nt->_selected_nodes->showTransformHandles( + nt->show_transform_handles, nt->single_node_transform_handles); + } else if (entry_name == "single_node_transform_handles") { + nt->single_node_transform_handles = value->getBool(); + nt->_selected_nodes->showTransformHandles( + nt->show_transform_handles, nt->single_node_transform_handles); + } else if (entry_name == "edit_clipping_paths") { + nt->edit_clipping_paths = value->getBool(); + ink_node_tool_selection_changed(nt, nt->desktop->selection); + } else if (entry_name == "edit_masks") { + nt->edit_masks = value->getBool(); + ink_node_tool_selection_changed(nt, nt->desktop->selection); + } else { + SPEventContextClass *parent_class = + (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent_class->set) + parent_class->set(ec, value); + } +} + +/** Recursively collect ShapeRecords */ +void gather_items(InkNodeTool *nt, SPItem *base, SPObject *obj, Inkscape::UI::ShapeRole role, + std::set<Inkscape::UI::ShapeRecord> &s) +{ + using namespace Inkscape::UI; + if (!obj) return; + + if (SP_IS_PATH(obj) && obj->repr->attribute("inkscape:original-d") != NULL) { + ShapeRecord r; + r.item = static_cast<SPItem*>(obj); + r.edit_transform = Geom::identity(); // TODO wrong? + r.role = role; + s.insert(r); + } else if (role != SHAPE_ROLE_NORMAL && (SP_IS_GROUP(obj) || SP_IS_OBJECTGROUP(obj))) { + for (SPObject *c = obj->children; c; c = c->next) { + gather_items(nt, base, c, role, s); + } + } else if (SP_IS_ITEM(obj)) { + SPItem *item = static_cast<SPItem*>(obj); + ShapeRecord r; + r.item = item; + // TODO add support for objectBoundingBox + r.edit_transform = base ? sp_item_i2doc_affine(base) : Geom::identity(); + r.role = role; + if (s.insert(r).second) { + // this item was encountered the first time + if (nt->edit_clipping_paths && item->clip_ref) { + gather_items(nt, item, item->clip_ref->getObject(), SHAPE_ROLE_CLIPPING_PATH, s); + } + if (nt->edit_masks && item->mask_ref) { + gather_items(nt, item, item->mask_ref->getObject(), SHAPE_ROLE_MASK, s); + } + } + } +} + +void ink_node_tool_selection_changed(InkNodeTool *nt, Inkscape::Selection *sel) +{ + using namespace Inkscape::UI; + + std::set<ShapeRecord> shapes; + + GSList const *ilist = sel->itemList(); + + for (GSList *i = const_cast<GSList*>(ilist); i; i = i->next) { + SPObject *obj = static_cast<SPObject*>(i->data); + if (SP_IS_ITEM(obj)) { + gather_items(nt, NULL, static_cast<SPItem*>(obj), SHAPE_ROLE_NORMAL, shapes); + } + } + + // ugly hack: set the first editable non-path item for knotholder + // maybe use multiple ShapeEditors for now, to allow editing many shapes at once? + bool something_set = false; + for (std::set<ShapeRecord>::iterator i = shapes.begin(); i != shapes.end(); ++i) { + ShapeRecord const &r = *i; + if (SP_IS_SHAPE(r.item) || + (SP_IS_PATH(r.item) && r.item->repr->attribute("inkscape:original-d") != NULL)) + { + nt->shape_editor->set_item(r.item, SH_KNOTHOLDER); + something_set = true; + break; + } + } + if (!something_set) { + nt->shape_editor->unset_item(SH_KNOTHOLDER); + } + + nt->_multipath->setItems(shapes); + ink_node_tool_update_tip(nt, NULL); + nt->desktop->updateNow(); +} + +gint ink_node_tool_root_handler(SPEventContext *event_context, GdkEvent *event) +{ + /* things to handle here: + * 1. selection of items + * 2. passing events to manipulators + * 3. some keybindings + */ + using namespace Inkscape::UI; // pull in event helpers + + SPDesktop *desktop = event_context->desktop; + Inkscape::Selection *selection = desktop->selection; + InkNodeTool *nt = static_cast<InkNodeTool*>(event_context); + static Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (nt->_multipath->event(event)) return true; + if (nt->_selector->event(event)) return true; + if (nt->_selected_nodes->event(event)) return true; + + switch (event->type) + { + case GDK_MOTION_NOTIFY: { + combine_motion_events(desktop->canvas, event->motion, 0); + SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button), + FALSE, TRUE); + if (over_item != nt->_last_over) { + nt->_last_over = over_item; + ink_node_tool_update_tip(nt, event); + } + + // create pathflash outline + if (prefs->getBool("/tools/nodes/pathflash_enabled")) { + if (over_item == nt->flashed_item) break; + if (!prefs->getBool("/tools/nodes/pathflash_selected") && selection->includes(over_item)) break; + if (nt->flash_tempitem) { + desktop->remove_temporary_canvasitem(nt->flash_tempitem); + nt->flash_tempitem = NULL; + nt->flashed_item = NULL; + } + if (!SP_IS_PATH(over_item)) break; // for now, handle only paths + + nt->flashed_item = over_item; + SPCurve *c = sp_path_get_curve_for_edit(SP_PATH(over_item)); + c->transform(sp_item_i2d_affine(over_item)); + SPCanvasItem *flash = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), c); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(flash), + prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff), 1.0, + SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(flash), 0, SP_WIND_RULE_NONZERO); + nt->flash_tempitem = desktop->add_temporary_canvasitem(flash, + prefs->getInt("/tools/nodes/pathflash_timeout", 500)); + c->unref(); + } + } break; // do not return true, because we need to pass this event to the parent context + // otherwise some features cease to work + + case GDK_KEY_PRESS: + switch (get_group0_keyval(&event->key)) + { + case GDK_Escape: // deselect everything + if (nt->_selected_nodes->empty()) { + selection->clear(); + } else { + nt->_selected_nodes->clear(); + } + ink_node_tool_update_tip(nt, event); + return TRUE; + case GDK_a: + if (held_control(event->key) && held_alt(event->key)) { + nt->_selected_nodes->selectAll(); + // Ctrl+A is handled in selection-chemistry.cpp via verb + ink_node_tool_update_tip(nt, event); + return TRUE; + } + break; + default: + break; + } + ink_node_tool_update_tip(nt, event); + break; + case GDK_KEY_RELEASE: + ink_node_tool_update_tip(nt, event); + break; + default: break; + } + + SPEventContextClass *parent_class = (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent_class->root_handler) + return parent_class->root_handler(event_context, event); + return FALSE; +} + +void ink_node_tool_update_tip(InkNodeTool *nt, GdkEvent *event) +{ + using namespace Inkscape::UI; + if (event && (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE)) { + unsigned new_state = state_after_event(event); + if (new_state == event->key.state) return; + if (state_held_shift(new_state)) { + if (nt->_last_over) { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, + C_("Node tool tip", "<b>Shift:</b> drag to add nodes to the selection, " + "click to toggle object selection")); + } else { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, + C_("Node tool tip", "<b>Shift:</b> drag to add nodes to the selection")); + } + return; + } + } + unsigned sz = nt->_selected_nodes->size(); + unsigned total = nt->_selected_nodes->allPoints().size(); + if (sz != 0) { + if (nt->_last_over) { + char *dyntip = g_strdup_printf(C_("Node tool tip", + "<b>%u of %u nodes</b> selected. " + "Drag to select nodes, click to edit only this object (more: Shift)"), sz, total); + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, dyntip); + g_free(dyntip); + } else { + char *dyntip = g_strdup_printf(C_("Node tool tip", + "<b>%u of %u nodes</b> selected. " + "Drag to select nodes, click clear the selection"), sz, total); + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, dyntip); + g_free(dyntip); + } + } else if (!nt->_multipath->empty()) { + if (nt->_last_over) { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip", + "Drag to select nodes, click to edit only this object")); + } else { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip", + "Drag to select nodes, click to clear the selection")); + } + } else { + if (nt->_last_over) { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip", + "Drag to select objects to edit, click to edit this object (more: Shift)")); + } else { + nt->_node_message_context->set(Inkscape::NORMAL_MESSAGE, C_("Node tool tip", + "Drag to select objects to edit")); + } + } +} + +gint ink_node_tool_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event) +{ + SPEventContextClass *parent_class = + (SPEventContextClass *) g_type_class_peek(g_type_parent(INK_TYPE_NODE_TOOL)); + if (parent_class->item_handler) + return parent_class->item_handler(event_context, item, event); + return FALSE; +} + +void ink_node_tool_select_area(InkNodeTool *nt, Geom::Rect const &sel, GdkEventButton *event) +{ + using namespace Inkscape::UI; + if (nt->_multipath->empty()) { + // if multipath is empty, select rubberbanded items rather than nodes + Inkscape::Selection *selection = nt->desktop->selection; + GSList *items = sp_document_items_in_box( + sp_desktop_document(nt->desktop), nt->desktop->dkey, sel); + selection->setList(items); + g_slist_free(items); + } else { + if (!held_shift(*event)) nt->_selected_nodes->clear(); + nt->_selected_nodes->selectArea(sel); + } +} +void ink_node_tool_select_point(InkNodeTool *nt, Geom::Point const &/*sel*/, GdkEventButton *event) +{ + using namespace Inkscape::UI; // pull in event helpers + if (!event) return; + if (event->button != 1) return; + + Inkscape::Selection *selection = nt->desktop->selection; + + SPItem *item_clicked = sp_event_context_find_item (nt->desktop, event_point(*event), + (event->state & GDK_MOD1_MASK) && !(event->state & GDK_CONTROL_MASK), TRUE); + + if (item_clicked == NULL) { // nothing under cursor + // if no Shift, deselect + if (!(event->state & GDK_SHIFT_MASK)) { + selection->clear(); + } + } else { + if (held_shift(*event)) { + selection->toggle(item_clicked); + } else { + selection->set(item_clicked); + } + nt->desktop->updateNow(); + } +} + +void ink_node_tool_mouseover_changed(InkNodeTool *nt, Inkscape::UI::ControlPoint *p) +{ + using Inkscape::UI::CurveDragPoint; + CurveDragPoint *cdp = dynamic_cast<CurveDragPoint*>(p); + if (cdp && !nt->cursor_drag) { + nt->cursor_shape = cursor_node_d_xpm; + nt->hot_x = 1; + nt->hot_y = 1; + sp_event_context_update_cursor(nt); + nt->cursor_drag = true; + } else if (!cdp && nt->cursor_drag) { + nt->cursor_shape = cursor_node_xpm; + nt->hot_x = 1; + nt->hot_y = 1; + sp_event_context_update_cursor(nt); + nt->cursor_drag = false; + } +} + +} // anonymous namespace + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/node-tool.h b/src/ui/tool/node-tool.h new file mode 100644 index 000000000..baac642ac --- /dev/null +++ b/src/ui/tool/node-tool.h @@ -0,0 +1,88 @@ +/** @file + * @brief New node tool with support for multiple path editing + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_NODE_TOOL_H +#define SEEN_UI_TOOL_NODE_TOOL_H + +#include <memory> +#include <glib.h> +#include <sigc++/sigc++.h> +#include "event-context.h" +#include "forward.h" +#include "display/display-forward.h" +#include "ui/tool/node-types.h" + +#define INK_TYPE_NODE_TOOL (ink_node_tool_get_type ()) +#define INK_NODE_TOOL(obj) (GTK_CHECK_CAST ((obj), INK_TYPE_NODE_TOOL, InkNodeTool)) +#define INK_NODE_TOOL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), INK_TYPE_NODE_TOOL, InkNodeToolClass)) +#define INK_IS_NODE_TOOL(obj) (GTK_CHECK_TYPE ((obj), INK_TYPE_NODE_TOOL)) +#define INK_IS_NODE_TOOL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), INK_TYPE_NODE_TOOL)) + +class InkNodeTool; +class InkNodeToolClass; + +namespace Inkscape { +namespace UI { +class MultiPathManipulator; +class ControlPointSelection; +class Selector; +struct PathSharedData; +} +} + +typedef std::auto_ptr<Inkscape::UI::MultiPathManipulator> MultiPathPtr; +typedef std::auto_ptr<Inkscape::UI::ControlPointSelection> CSelPtr; +typedef std::auto_ptr<Inkscape::UI::Selector> SelectorPtr; +typedef std::auto_ptr<Inkscape::UI::PathSharedData> PathSharedDataPtr; + +struct InkNodeTool : public SPEventContext +{ + sigc::connection _selection_changed_connection; + sigc::connection _mouseover_changed_connection; + sigc::connection _selection_modified_connection; + Inkscape::MessageContext *_node_message_context; + SPItem *flashed_item; + Inkscape::Display::TemporaryItem *flash_tempitem; + CSelPtr _selected_nodes; + MultiPathPtr _multipath; + SelectorPtr _selector; + PathSharedDataPtr _path_data; + SPCanvasGroup *_transform_handle_group; + SPItem *_last_over; + + unsigned cursor_drag : 1; + unsigned show_outline : 1; + unsigned live_outline : 1; + unsigned live_objects : 1; + unsigned show_path_direction : 1; + unsigned show_transform_handles : 1; + unsigned single_node_transform_handles : 1; + unsigned edit_clipping_paths : 1; + unsigned edit_masks : 1; +}; + +struct InkNodeToolClass { + SPEventContextClass parent_class; +}; + +GType ink_node_tool_get_type (void); + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/node-types.h b/src/ui/tool/node-types.h new file mode 100644 index 000000000..80eaf4fa7 --- /dev/null +++ b/src/ui/tool/node-types.h @@ -0,0 +1,48 @@ +/** @file + * Node types and other small enums. + * This file exists to reduce the number of includes pulled in by toolbox.cpp. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_NODE_TYPES_H +#define SEEN_UI_TOOL_NODE_TYPES_H + +namespace Inkscape { +namespace UI { + +/** Types of nodes supported in the node tool. */ +enum NodeType { + NODE_CUSP, ///< Cusp node - no handle constraints + NODE_SMOOTH, ///< Smooth node - handles must be colinear + NODE_AUTO, ///< Auto node - handles adjusted automatically based on neighboring nodes + NODE_SYMMETRIC, ///< Symmetric node - handles must be colinear and of equal length + NODE_LAST_REAL_TYPE, ///< Last real type of node - used for ctrl+click on a node + NODE_PICK_BEST = 100 ///< Select type based on handle positions +}; + +/** Types of segments supported in the node tool. */ +enum SegmentType { + SEGMENT_STRAIGHT, ///< Straight linear segment + SEGMENT_CUBIC_BEZIER ///< Bezier curve with two control points +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp new file mode 100644 index 000000000..e9fa79fb3 --- /dev/null +++ b/src/ui/tool/node.cpp @@ -0,0 +1,1407 @@ +/** @file + * Editable node - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <iostream> +#include <stdexcept> +#include <boost/utility.hpp> +#include <glib.h> +#include <glib/gi18n.h> +#include <2geom/bezier-utils.h> +#include <2geom/transforms.h> + +#include "display/sp-ctrlline.h" +#include "display/sp-canvas.h" +#include "display/sp-canvas-util.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "preferences.h" +#include "snap.h" +#include "snap-preferences.h" +#include "sp-metrics.h" +#include "sp-namedview.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/node.h" +#include "ui/tool/path-manipulator.h" + +namespace Inkscape { +namespace UI { + +static SelectableControlPoint::ColorSet node_colors = { + { + {0xbfbfbf00, 0x000000ff}, // normal fill, stroke + {0xff000000, 0x000000ff}, // mouseover fill, stroke + {0xff000000, 0x000000ff} // clicked fill, stroke + }, + {0x0000ffff, 0x000000ff}, // normal fill, stroke when selected + {0xff000000, 0x000000ff}, // mouseover fill, stroke when selected + {0xff000000, 0x000000ff} // clicked fill, stroke when selected +}; + +static ControlPoint::ColorSet handle_colors = { + {0xffffffff, 0x000000ff}, // normal fill, stroke + {0xff000000, 0x000000ff}, // mouseover fill, stroke + {0xff000000, 0x000000ff} // clicked fill, stroke +}; + +std::ostream &operator<<(std::ostream &out, NodeType type) +{ + switch(type) { + case NODE_CUSP: out << 'c'; break; + case NODE_SMOOTH: out << 's'; break; + case NODE_AUTO: out << 'a'; break; + case NODE_SYMMETRIC: out << 'z'; break; + default: out << 'b'; break; + } + return out; +} + +/** Computes an unit vector of the direction from first to second control point */ +static Geom::Point direction(Geom::Point const &first, Geom::Point const &second) { + return Geom::unit_vector(second - first); +} + +/** + * @class Handle + * @brief Control point of a cubic Bezier curve in a path. + * + * Handle keeps the node type invariant only for the opposite handle of the same node. + * Keeping the invariant on node moves is left to the %Node class. + */ + +Geom::Point Handle::_saved_other_pos(0, 0); +double Handle::_saved_length = 0.0; +bool Handle::_drag_out = false; + +Handle::Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent) + : ControlPoint(data.desktop, initial_pos, Gtk::ANCHOR_CENTER, SP_CTRL_SHAPE_CIRCLE, 7.0, + &handle_colors, data.handle_group) + , _parent(parent) + , _degenerate(true) +{ + _cset = &handle_colors; + _handle_line = sp_canvas_item_new(data.handle_line_group, SP_TYPE_CTRLLINE, NULL); + setVisible(false); +} +Handle::~Handle() +{ + //sp_canvas_item_hide(_handle_line); + gtk_object_destroy(GTK_OBJECT(_handle_line)); +} + +void Handle::setVisible(bool v) +{ + ControlPoint::setVisible(v); + if (v) sp_canvas_item_show(_handle_line); + else sp_canvas_item_hide(_handle_line); +} + +void Handle::move(Geom::Point const &new_pos) +{ + Handle *other = this->other(); + Node *node_towards = _parent->nodeToward(this); // node in direction of this handle + Node *node_away = _parent->nodeAwayFrom(this); // node in the opposite direction + Handle *towards = node_towards ? node_towards->handleAwayFrom(_parent) : NULL; + Handle *towards_second = node_towards ? node_towards->handleToward(_parent) : NULL; + + if (Geom::are_near(new_pos, _parent->position())) { + // The handle becomes degenerate. If the segment between it and the node + // in its direction becomes linear and there are smooth nodes + // at its ends, make their handles colinear with the segment + if (towards && towards->isDegenerate()) { + if (node_towards->type() == NODE_SMOOTH) { + towards_second->setDirection(*_parent, *node_towards); + } + if (_parent->type() == NODE_SMOOTH) { + other->setDirection(*node_towards, *_parent); + } + } + setPosition(new_pos); + return; + } + + if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { + // restrict movement to the line joining the nodes + Geom::Point direction = _parent->position() - node_away->position(); + Geom::Point delta = new_pos - _parent->position(); + // project the relative position on the direction line + Geom::Point new_delta = (Geom::dot(delta, direction) + / Geom::L2sq(direction)) * direction; + setRelativePos(new_delta); + return; + } + + switch (_parent->type()) { + case NODE_AUTO: + _parent->setType(NODE_SMOOTH, false); + // fall through - auto nodes degrade into smooth nodes + case NODE_SMOOTH: { + /* for smooth nodes, we need to rotate the other handle so that it's colinear + * with the dragged one while conserving length. */ + other->setDirection(new_pos, *_parent); + } break; + case NODE_SYMMETRIC: + // for symmetric nodes, place the other handle on the opposite side + other->setRelativePos(-(new_pos - _parent->position())); + break; + default: break; + } + + setPosition(new_pos); +} + +void Handle::setPosition(Geom::Point const &p) +{ + ControlPoint::setPosition(p); + sp_ctrlline_set_coords(SP_CTRLLINE(_handle_line), _parent->position(), position()); + + // update degeneration info and visibility + if (Geom::are_near(position(), _parent->position())) + _degenerate = true; + else _degenerate = false; + if (_parent->_handles_shown && _parent->visible() && !_degenerate) { + setVisible(true); + } else { + setVisible(false); + } + // If both handles become degenerate, convert to parent cusp node + if (_parent->isDegenerate()) { + _parent->setType(NODE_CUSP, false); + } +} + +void Handle::setLength(double len) +{ + if (isDegenerate()) return; + Geom::Point dir = Geom::unit_vector(relativePos()); + setRelativePos(dir * len); +} + +void Handle::retract() +{ + setPosition(_parent->position()); +} + +void Handle::setDirection(Geom::Point const &from, Geom::Point const &to) +{ + setDirection(to - from); +} + +void Handle::setDirection(Geom::Point const &dir) +{ + Geom::Point unitdir = Geom::unit_vector(dir); + setRelativePos(unitdir * length()); +} + +char const *Handle::handle_type_to_localized_string(NodeType type) +{ + switch(type) { + case NODE_CUSP: return _("Cusp node handle"); + case NODE_SMOOTH: return _("Smooth node handle"); + case NODE_SYMMETRIC: return _("Symmetric node handle"); + case NODE_AUTO: return _("Auto-smooth node handle"); + default: return ""; + } +} + +bool Handle::grabbed(GdkEventMotion *) +{ + _saved_other_pos = other()->position(); + _saved_length = _drag_out ? 0 : length(); + _pm()._handleGrabbed(); + return false; +} + +void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event) +{ + Geom::Point parent_pos = _parent->position(); + Geom::Point origin = _last_drag_origin(); + SnapManager &sm = _desktop->namedview->snap_manager; + bool snap = sm.someSnapperMightSnap(); + + // with Alt, preserve length + if (held_alt(*event)) { + new_pos = parent_pos + Geom::unit_vector(new_pos - parent_pos) * _saved_length; + snap = false; + } + // with Ctrl, constrain to M_PI/rotationsnapsperpi increments from vertical + // and the original position. + if (held_control(*event)) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = 2 * prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + + // note: if snapping to the original position is only desired in the original + // direction of the handle, change to Ray instead of Line + Geom::Line original_line(parent_pos, origin); + Geom::Point snap_pos = parent_pos + Geom::constrain_angle( + Geom::Point(0,0), new_pos - parent_pos, snaps, Geom::Point(1,0)); + Geom::Point orig_pos = original_line.pointAt(original_line.nearestPoint(new_pos)); + + if (Geom::distance(snap_pos, new_pos) < Geom::distance(orig_pos, new_pos)) { + new_pos = snap_pos; + } else { + new_pos = orig_pos; + } + snap = false; + } + + std::vector<Inkscape::SnapCandidatePoint> unselected; + if (snap) { + typedef ControlPointSelection::Set Set; + Set &nodes = _parent->_selection.allPoints(); + for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { + Node *n = static_cast<Node*>(*i); + Inkscape::SnapCandidatePoint p(n->position(), n->_snapSourceType(), n->_snapTargetType()); + unselected.push_back(p); + } + sm.setupIgnoreSelection(_desktop, true, &unselected); + + Node *node_away = (this == &_parent->_front ? _parent->_prev() : _parent->_next()); + if (_parent->type() == NODE_SMOOTH && Node::_is_line_segment(_parent, node_away)) { + Inkscape::Snapper::ConstraintLine cl(_parent->position(), + _parent->position() - node_away->position()); + Inkscape::SnappedPoint p; + p = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, SNAPSOURCE_NODE_HANDLE), cl); + if (p.getSnapped()) { + p.getPoint(new_pos); + } + } else { + sm.freeSnapReturnByRef(new_pos, SNAPSOURCE_NODE_HANDLE); + } + } + + // with Shift, if the node is cusp, rotate the other handle as well + if (_parent->type() == NODE_CUSP && !_drag_out) { + if (held_shift(*event)) { + Geom::Point other_relpos = _saved_other_pos - parent_pos; + other_relpos *= Geom::Rotate(Geom::angle_between(origin - parent_pos, new_pos - parent_pos)); + other()->setRelativePos(other_relpos); + } else { + // restore the position + other()->setPosition(_saved_other_pos); + } + } + move(new_pos); // needed for correct update, even though it's redundant + _pm().update(); +} + +void Handle::ungrabbed(GdkEventButton *event) +{ + // hide the handle if it's less than dragtolerance away from the node + // TODO is this actually desired? + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int drag_tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); + + Geom::Point dist = _desktop->d2w(_parent->position()) - _desktop->d2w(position()); + if (dist.length() <= drag_tolerance) { + move(_parent->position()); + } + + // HACK: If the handle was dragged out, call parent's ungrabbed handler, + // so that transform handles reappear + if (_drag_out) { + _parent->ungrabbed(event); + } + _drag_out = false; + + _pm()._handleUngrabbed(); +} + +bool Handle::clicked(GdkEventButton *event) +{ + _pm()._handleClicked(this, event); + return true; +} + +Handle *Handle::other() +{ + if (this == &_parent->_front) return &_parent->_back; + return &_parent->_front; +} + +static double snap_increment_degrees() { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + return 180.0 / snaps; +} + +Glib::ustring Handle::_getTip(unsigned state) +{ + char const *more; + bool can_shift_rotate = _parent->type() == NODE_CUSP && !other()->isDegenerate(); + if (can_shift_rotate) { + more = C_("Path handle tip", "more: Shift, Ctrl, Alt"); + } else { + more = C_("Path handle tip", "more: Ctrl, Alt"); + } + if (state_held_alt(state)) { + if (state_held_control(state)) { + if (state_held_shift(state) && can_shift_rotate) { + return format_tip(C_("Path handle tip", + "<b>Shift+Ctrl+Alt</b>: preserve length and snap rotation angle to %g° " + "increments while rotating both handles"), + snap_increment_degrees()); + } else { + return format_tip(C_("Path handle tip", + "<b>Ctrl+Alt</b>: preserve length and snap rotation angle to %g° increments"), + snap_increment_degrees()); + } + } else { + if (state_held_shift(state) && can_shift_rotate) { + return C_("Path handle tip", + "<b>Shift+Alt:</b> preserve handle length and rotate both handles"); + } else { + return C_("Path handle tip", + "<b>Alt:</b> preserve handle length while dragging"); + } + } + } else { + if (state_held_control(state)) { + if (state_held_shift(state) && can_shift_rotate) { + return format_tip(C_("Path handle tip", + "<b>Shift+Ctrl:</b> snap rotation angle to %g° increments and rotate both handles"), + snap_increment_degrees()); + } else { + return format_tip(C_("Path handle tip", + "<b>Ctrl:</b> snap rotation angle to %g° increments, click to retract"), + snap_increment_degrees()); + } + } else if (state_held_shift(state) && can_shift_rotate) { + return C_("Path hande tip", + "<b>Shift</b>: rotate both handles by the same angle"); + } + } + + switch (_parent->type()) { + case NODE_AUTO: + return format_tip(C_("Path handle tip", + "<b>Auto node handle:</b> drag to convert to smooth node (%s)"), more); + default: + return format_tip(C_("Path handle tip", + "<b>%s:</b> drag to shape the segment (%s)"), + handle_type_to_localized_string(_parent->type()), more); + } +} + +Glib::ustring Handle::_getDragTip(GdkEventMotion */*event*/) +{ + Geom::Point dist = position() - _last_drag_origin(); + // report angle in mathematical convention + double angle = Geom::angle_between(Geom::Point(-1,0), position() - _parent->position()); + angle += M_PI; // angle is (-M_PI...M_PI] - offset by +pi and scale to 0...360 + angle *= 360.0 / (2 * M_PI); + GString *x = SP_PX_TO_METRIC_STRING(dist[Geom::X], _desktop->namedview->getDefaultMetric()); + GString *y = SP_PX_TO_METRIC_STRING(dist[Geom::Y], _desktop->namedview->getDefaultMetric()); + GString *len = SP_PX_TO_METRIC_STRING(length(), _desktop->namedview->getDefaultMetric()); + Glib::ustring ret = format_tip(C_("Path handle tip", + "Move handle by %s, %s; angle %.2f°, length %s"), x->str, y->str, angle, len->str); + g_string_free(x, TRUE); + g_string_free(y, TRUE); + g_string_free(len, TRUE); + return ret; +} + +/** + * @class Node + * @brief Curve endpoint in an editable path. + * + * The method move() keeps node type invariants during translations. + */ + +Node::Node(NodeSharedData const &data, Geom::Point const &initial_pos) + : SelectableControlPoint(data.desktop, initial_pos, Gtk::ANCHOR_CENTER, + SP_CTRL_SHAPE_DIAMOND, 9.0, *data.selection, &node_colors, data.node_group) + , _front(data, initial_pos, this) + , _back(data, initial_pos, this) + , _type(NODE_CUSP) + , _handles_shown(false) +{ + // NOTE we do not set type here, because the handles are still degenerate +} + +// NOTE: not using iterators won't make this much quicker because iterators can be 100% inlined. +Node *Node::_next() +{ + NodeList::iterator n = NodeList::get_iterator(this).next(); + if (n) return n.ptr(); + return NULL; +} +Node *Node::_prev() +{ + NodeList::iterator p = NodeList::get_iterator(this).prev(); + if (p) return p.ptr(); + return NULL; +} + +void Node::move(Geom::Point const &new_pos) +{ + // move handles when the node moves. + Geom::Point old_pos = position(); + Geom::Point delta = new_pos - position(); + setPosition(new_pos); + _front.setPosition(_front.position() + delta); + _back.setPosition(_back.position() + delta); + + // if the node has a smooth handle after a line segment, it should be kept colinear + // with the segment + _fixNeighbors(old_pos, new_pos); +} + +void Node::transform(Geom::Matrix const &m) +{ + Geom::Point old_pos = position(); + setPosition(position() * m); + _front.setPosition(_front.position() * m); + _back.setPosition(_back.position() * m); + + /* Affine transforms keep handle invariants for smooth and symmetric nodes, + * but smooth nodes at ends of linear segments and auto nodes need special treatment */ + _fixNeighbors(old_pos, position()); +} + +Geom::Rect Node::bounds() +{ + Geom::Rect b(position(), position()); + b.expandTo(_front.position()); + b.expandTo(_back.position()); + return b; +} + +void Node::_fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos) +{ + /* This method restores handle invariants for neighboring nodes, + * and invariants that are based on positions of those nodes for this one. */ + + /* Fix auto handles */ + if (_type == NODE_AUTO) _updateAutoHandles(); + if (old_pos != new_pos) { + if (_next() && _next()->_type == NODE_AUTO) _next()->_updateAutoHandles(); + if (_prev() && _prev()->_type == NODE_AUTO) _prev()->_updateAutoHandles(); + } + + /* Fix smooth handles at the ends of linear segments. + * Rotate the appropriate handle to be colinear with the segment. + * If there is a smooth node at the other end of the segment, rotate it too. */ + Handle *handle, *other_handle; + Node *other; + if (_is_line_segment(this, _next())) { + handle = &_back; + other = _next(); + other_handle = &_next()->_front; + } else if (_is_line_segment(_prev(), this)) { + handle = &_front; + other = _prev(); + other_handle = &_prev()->_back; + } else return; + + if (_type == NODE_SMOOTH && !handle->isDegenerate()) { + handle->setDirection(other->position(), new_pos); + } + // also update the handle on the other end of the segment + if (other->_type == NODE_SMOOTH && !other_handle->isDegenerate()) { + other_handle->setDirection(new_pos, other->position()); + } +} + +void Node::_updateAutoHandles() +{ + // Recompute the position of automatic handles. + // For endnodes, retract both handles. (It's only possible to create an end auto node + // through the XML editor.) + if (isEndNode()) { + _front.retract(); + _back.retract(); + return; + } + + // Auto nodes automaticaly adjust their handles to give an appearance of smoothness, + // no matter what their surroundings are. + Geom::Point vec_next = _next()->position() - position(); + Geom::Point vec_prev = _prev()->position() - position(); + double len_next = vec_next.length(), len_prev = vec_prev.length(); + if (len_next > 0 && len_prev > 0) { + // "dir" is an unit vector perpendicular to the bisector of the angle created + // by the previous node, this auto node and the next node. + Geom::Point dir = Geom::unit_vector((len_prev / len_next) * vec_next - vec_prev); + // Handle lengths are equal to 1/3 of the distance from the adjacent node. + _back.setRelativePos(-dir * (len_prev / 3)); + _front.setRelativePos(dir * (len_next / 3)); + } else { + // If any of the adjacent nodes coincides, retract both handles. + _front.retract(); + _back.retract(); + } +} + +void Node::showHandles(bool v) +{ + _handles_shown = v; + if (!_front.isDegenerate()) _front.setVisible(v); + if (!_back.isDegenerate()) _back.setVisible(v); +} + +/** Sets the node type and optionally restores the invariants associated with the given type. + * @param type The type to set + * @param update_handles Whether to restore invariants associated with the given type. + * Passing false is useful e.g. wen initially creating the path, + * and when making cusp nodes during some node algorithms. + * Pass true when used in response to an UI node type button. + */ +void Node::setType(NodeType type, bool update_handles) +{ + if (type == NODE_PICK_BEST) { + pickBestType(); + updateState(); // The size of the control might have changed + return; + } + + // if update_handles is true, adjust handle positions to match the node type + // handle degenerate handles appropriately + if (update_handles) { + switch (type) { + case NODE_CUSP: + // if the existing type is also NODE_CUSP, retract handles + if (_type == NODE_CUSP) { + _front.retract(); + _back.retract(); + } + break; + case NODE_AUTO: + // auto handles make no sense for endnodes + if (isEndNode()) return; + _updateAutoHandles(); + break; + case NODE_SMOOTH: { + // rotate handles to be colinear + // for degenerate nodes set positions like auto handles + bool prev_line = _is_line_segment(_prev(), this); + bool next_line = _is_line_segment(this, _next()); + if (_type == NODE_SMOOTH) { + // for a node that is already smooth and has a degenerate handle, + // drag out the second handle to 1/3 the length of the linear segment + if (_front.isDegenerate()) { + double dist = Geom::distance(_next()->position(), position()); + _front.setRelativePos(Geom::unit_vector(-_back.relativePos()) * dist / 3); + } + if (_back.isDegenerate()) { + double dist = Geom::distance(_prev()->position(), position()); + _back.setRelativePos(Geom::unit_vector(-_front.relativePos()) * dist / 3); + } + } else if (isDegenerate()) { + _updateAutoHandles(); + } else if (_front.isDegenerate()) { + // if the front handle is degenerate and this...next is a line segment, + // make back colinear; otherwise pull out the other handle + // to 1/3 of distance to prev + if (next_line) { + _back.setDirection(*_next(), *this); + } else if (_prev()) { + Geom::Point dir = direction(_back, *this); + _front.setRelativePos(Geom::distance(_prev()->position(), position()) / 3 * dir); + } + } else if (_back.isDegenerate()) { + if (prev_line) { + _front.setDirection(*_prev(), *this); + } else if (_next()) { + Geom::Point dir = direction(_front, *this); + _back.setRelativePos(Geom::distance(_next()->position(), position()) / 3 * dir); + } + } else { + // both handles are extended. make colinear while keeping length + // first make back colinear with the vector front ---> back, + // then make front colinear with back ---> node + // (not back ---> front because back's position was changed in the first call) + _back.setDirection(_front, _back); + _front.setDirection(_back, *this); + } + } break; + case NODE_SYMMETRIC: + if (isEndNode()) return; // symmetric handles make no sense for endnodes + if (isDegenerate()) { + // similar to auto handles but set the same length for both + Geom::Point vec_next = _next()->position() - position(); + Geom::Point vec_prev = _prev()->position() - position(); + double len_next = vec_next.length(), len_prev = vec_prev.length(); + double len = (len_next + len_prev) / 6; // take 1/3 of average + if (len == 0) return; + + Geom::Point dir = Geom::unit_vector((len_prev / len_next) * vec_next - vec_prev); + _back.setRelativePos(-dir * len); + _front.setRelativePos(dir * len); + } else { + // Both handles are extended. Compute average length, use direction from + // back handle to front handle. This also works correctly for degenerates + double len = (_front.length() + _back.length()) / 2; + Geom::Point dir = direction(_back, _front); + _front.setRelativePos(dir * len); + _back.setRelativePos(-dir * len); + } + break; + default: break; + } + } + _type = type; + _setShape(_node_type_to_shape(type)); + updateState(); +} + +/** Pick the best type for this node, based on the position of its handles. + * This is what assigns types to nodes created using the pen tool. */ +void Node::pickBestType() +{ + _type = NODE_CUSP; + bool front_degen = _front.isDegenerate(); + bool back_degen = _back.isDegenerate(); + bool both_degen = front_degen && back_degen; + bool neither_degen = !front_degen && !back_degen; + do { + // if both handles are degenerate, do nothing + if (both_degen) break; + // if neither are degenerate, check their respective positions + if (neither_degen) { + Geom::Point front_delta = _front.position() - position(); + Geom::Point back_delta = _back.position() - position(); + // for now do not automatically make nodes symmetric, it can be annoying + /*if (Geom::are_near(front_delta, -back_delta)) { + _type = NODE_SYMMETRIC; + break; + }*/ + if (Geom::are_near(Geom::unit_vector(front_delta), + Geom::unit_vector(-back_delta))) + { + _type = NODE_SMOOTH; + break; + } + } + // check whether the handle aligns with the previous line segment. + // we know that if front is degenerate, back isn't, because + // both_degen was false + if (front_degen && _next() && _next()->_back.isDegenerate()) { + Geom::Point segment_delta = Geom::unit_vector(_next()->position() - position()); + Geom::Point handle_delta = Geom::unit_vector(_back.position() - position()); + if (Geom::are_near(segment_delta, -handle_delta)) { + _type = NODE_SMOOTH; + break; + } + } else if (back_degen && _prev() && _prev()->_front.isDegenerate()) { + Geom::Point segment_delta = Geom::unit_vector(_prev()->position() - position()); + Geom::Point handle_delta = Geom::unit_vector(_front.position() - position()); + if (Geom::are_near(segment_delta, -handle_delta)) { + _type = NODE_SMOOTH; + break; + } + } + } while (false); + _setShape(_node_type_to_shape(_type)); + updateState(); +} + +bool Node::isEndNode() +{ + return !_prev() || !_next(); +} + +/** Move the node to the bottom of its canvas group. Useful for node break, to ensure that + * the selected nodes are above the unselected ones. */ +void Node::sink() +{ + sp_canvas_item_move_to_z(_canvas_item, 0); +} + +NodeType Node::parse_nodetype(char x) +{ + switch (x) { + case 'a': return NODE_AUTO; + case 'c': return NODE_CUSP; + case 's': return NODE_SMOOTH; + case 'z': return NODE_SYMMETRIC; + default: return NODE_PICK_BEST; + } +} + +/** Customized event handler to catch scroll events needed for selection grow/shrink. */ +bool Node::_eventHandler(GdkEvent *event) +{ + static NodeList::iterator origin; + static int dir; + + switch (event->type) + { + case GDK_SCROLL: + if (event->scroll.direction == GDK_SCROLL_UP) { + dir = 1; + } else if (event->scroll.direction == GDK_SCROLL_DOWN) { + dir = -1; + } else break; + if (held_control(event->scroll)) { + _selection.spatialGrow(this, dir); + } else { + _linearGrow(dir); + } + return true; + default: + break; + } + return ControlPoint::_eventHandler(event); +} + +// TODO Move this to 2Geom! +static double bezier_length (Geom::Point a0, Geom::Point a1, Geom::Point a2, Geom::Point a3) +{ + double lower = Geom::distance(a0, a3); + double upper = Geom::distance(a0, a1) + Geom::distance(a1, a2) + Geom::distance(a2, a3); + + if (upper - lower < Geom::EPSILON) return (lower + upper)/2; + + Geom::Point // Casteljau subdivision + b0 = a0, + c0 = a3, + b1 = 0.5*(a0 + a1), + t0 = 0.5*(a1 + a2), + c1 = 0.5*(a2 + a3), + b2 = 0.5*(b1 + t0), + c2 = 0.5*(t0 + c1), + b3 = 0.5*(b2 + c2); // == c3 + return bezier_length(b0, b1, b2, b3) + bezier_length(b3, c2, c1, c0); +} + +/** Select or deselect a node in this node's subpath based on its path distance from this node. + * @param dir If negative, shrink selection by one node; if positive, grow by one node */ +void Node::_linearGrow(int dir) +{ + // Interestingly, we do not need any help from PathManipulator when doing linear grow. + // First handle the trivial case of growing over an unselected node. + if (!selected() && dir > 0) { + _selection.insert(this); + return; + } + + NodeList::iterator this_iter = NodeList::get_iterator(this); + NodeList::iterator fwd = this_iter, rev = this_iter; + double distance_back = 0, distance_front = 0; + + // Linear grow is simple. We find the first unselected nodes in each direction + // and compare the linear distances to them. + if (dir > 0) { + if (!selected()) { + _selection.insert(this); + return; + } + + // find first unselected nodes on both sides + while (fwd && fwd->selected()) { + NodeList::iterator n = fwd.next(); + distance_front += bezier_length(*fwd, fwd->_front, n->_back, *n); + fwd = n; + if (fwd == this_iter) + // there is no unselected node in this cyclic subpath + return; + } + // do the same for the second direction. Do not check for equality with + // this node, because there is at least one unselected node in the subpath, + // so we are guaranteed to stop. + while (rev && rev->selected()) { + NodeList::iterator p = rev.prev(); + distance_back += bezier_length(*rev, rev->_back, p->_front, *p); + rev = p; + } + + NodeList::iterator t; // node to select + if (fwd && rev) { + if (distance_front <= distance_back) t = fwd; + else t = rev; + } else { + if (fwd) t = fwd; + if (rev) t = rev; + } + if (t) _selection.insert(t.ptr()); + + // Linear shrink is more complicated. We need to find the farthest selected node. + // This means we have to check the entire subpath. We go in the direction in which + // the distance we traveled is lower. We do this until we run out of nodes (ends of path) + // or the two iterators meet. On the way, we store the last selected node and its distance + // in each direction (if any). At the end, we choose the one that is farther and deselect it. + } else { + // both iterators that store last selected nodes are initially empty + NodeList::iterator last_fwd, last_rev; + double last_distance_back = 0, last_distance_front = 0; + + while (rev || fwd) { + if (fwd && (!rev || distance_front <= distance_back)) { + if (fwd->selected()) { + last_fwd = fwd; + last_distance_front = distance_front; + } + NodeList::iterator n = fwd.next(); + if (n) distance_front += bezier_length(*fwd, fwd->_front, n->_back, *n); + fwd = n; + } else if (rev && (!fwd || distance_front > distance_back)) { + if (rev->selected()) { + last_rev = rev; + last_distance_back = distance_back; + } + NodeList::iterator p = rev.prev(); + if (p) distance_back += bezier_length(*rev, rev->_back, p->_front, *p); + rev = p; + } + // Check whether we walked the entire cyclic subpath. + // This is initially true because both iterators start from this node, + // so this check cannot go in the while condition. + // When this happens, we need to check the last node, pointed to by the iterators. + if (fwd && fwd == rev) { + if (!fwd->selected()) break; + NodeList::iterator fwdp = fwd.prev(), revn = rev.next(); + double df = distance_front + bezier_length(*fwdp, fwdp->_front, fwd->_back, *fwd); + double db = distance_back + bezier_length(*revn, revn->_back, rev->_front, *rev); + if (df > db) { + last_fwd = fwd; + last_distance_front = df; + } else { + last_rev = rev; + last_distance_back = db; + } + break; + } + } + + NodeList::iterator t; + if (last_fwd && last_rev) { + if (last_distance_front >= last_distance_back) t = last_fwd; + else t = last_rev; + } else { + if (last_fwd) t = last_fwd; + if (last_rev) t = last_rev; + } + if (t) _selection.erase(t.ptr()); + } +} + +void Node::_setState(State state) +{ + // change node size to match type and selection state + switch (_type) { + case NODE_AUTO: + case NODE_CUSP: + if (selected()) _setSize(11); + else _setSize(9); + break; + default: + if(selected()) _setSize(9); + else _setSize(7); + break; + } + SelectableControlPoint::_setState(state); +} + +bool Node::grabbed(GdkEventMotion *event) +{ + if (SelectableControlPoint::grabbed(event)) + return true; + + // Dragging out handles with Shift + drag on a node. + if (!held_shift(*event)) return false; + + Handle *h; + Geom::Point evp = event_point(*event); + Geom::Point rel_evp = evp - _last_click_event_point(); + + // This should work even if dragtolerance is zero and evp coincides with node position. + double angle_next = HUGE_VAL; + double angle_prev = HUGE_VAL; + bool has_degenerate = false; + // determine which handle to drag out based on degeneration and the direction of drag + if (_front.isDegenerate() && _next()) { + Geom::Point next_relpos = _desktop->d2w(_next()->position()) + - _desktop->d2w(position()); + angle_next = fabs(Geom::angle_between(rel_evp, next_relpos)); + has_degenerate = true; + } + if (_back.isDegenerate() && _prev()) { + Geom::Point prev_relpos = _desktop->d2w(_prev()->position()) + - _desktop->d2w(position()); + angle_prev = fabs(Geom::angle_between(rel_evp, prev_relpos)); + has_degenerate = true; + } + if (!has_degenerate) return false; + h = angle_next < angle_prev ? &_front : &_back; + + h->setPosition(_desktop->w2d(evp)); + h->setVisible(true); + h->transferGrab(this, event); + Handle::_drag_out = true; + return true; +} + +void Node::dragged(Geom::Point &new_pos, GdkEventMotion *event) +{ + // For a note on how snapping is implemented in Inkscape, see snap.h. + SnapManager &sm = _desktop->namedview->snap_manager; + bool snap = sm.someSnapperMightSnap(); + std::vector<Inkscape::SnapCandidatePoint> unselected; + if (snap) { + /* setup + * TODO We are doing this every time a snap happens. It should once be done only once + * per drag - maybe in the grabbed handler? + * TODO Unselected nodes vector must be valid during the snap run, because it is not + * copied. Fix this in snap.h and snap.cpp, then the above. + * TODO Snapping to unselected segments of selected paths doesn't work yet. */ + + // Build the list of unselected nodes. + typedef ControlPointSelection::Set Set; + Set &nodes = _selection.allPoints(); + for (Set::iterator i = nodes.begin(); i != nodes.end(); ++i) { + if (!(*i)->selected()) { + Node *n = static_cast<Node*>(*i); + Inkscape::SnapCandidatePoint p(n->position(), n->_snapSourceType(), n->_snapTargetType()); + unselected.push_back(p); + } + } + sm.setupIgnoreSelection(_desktop, false, &unselected); + } + + if (held_control(*event)) { + Geom::Point origin = _last_drag_origin(); + Inkscape::SnappedPoint fp, bp; + if (held_alt(*event)) { + // with Ctrl+Alt, constrain to handle lines + // project the new position onto a handle line that is closer + boost::optional<Geom::Point> front_point, back_point; + boost::optional<Inkscape::Snapper::ConstraintLine> line_front, line_back; + if (_front.isDegenerate()) { + if (_is_line_segment(this, _next())) + front_point = _next()->position() - origin; + } else { + front_point = _front.relativePos(); + } + if (_back.isDegenerate()) { + if (_is_line_segment(_prev(), this)) + back_point = _prev()->position() - origin; + } else { + back_point = _back.relativePos(); + } + if (front_point) + line_front = Inkscape::Snapper::ConstraintLine(origin, *front_point); + if (back_point) + line_back = Inkscape::Snapper::ConstraintLine(origin, *back_point); + + // TODO: combine the snap and non-snap branches by modifying snap.h / snap.cpp + if (snap) { + if (line_front) { + fp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, + _snapSourceType()), *line_front); + } + if (line_back) { + bp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, + _snapSourceType()), *line_back); + } + } + if (fp.getSnapped() || bp.getSnapped()) { + if (fp.isOtherSnapBetter(bp, false)) { + fp = bp; + } + fp.getPoint(new_pos); + _desktop->snapindicator->set_new_snaptarget(fp); + } else { + boost::optional<Geom::Point> pos; + if (line_front) { + pos = line_front->projection(new_pos); + } + if (line_back) { + Geom::Point pos2 = line_back->projection(new_pos); + if (!pos || (pos && Geom::distance(new_pos, *pos) > Geom::distance(new_pos, pos2))) + pos = pos2; + } + if (pos) { + new_pos = *pos; + } else { + new_pos = origin; + } + } + } else { + // with Ctrl, constrain to axes + // TODO combine the two branches + if (snap) { + Inkscape::Snapper::ConstraintLine line_x(origin, Geom::Point(1, 0)); + Inkscape::Snapper::ConstraintLine line_y(origin, Geom::Point(0, 1)); + fp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_x); + bp = sm.constrainedSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType()), line_y); + } + if (fp.getSnapped() || bp.getSnapped()) { + if (fp.isOtherSnapBetter(bp, false)) { + fp = bp; + } + fp.getPoint(new_pos); + if (fp.getTarget() != SNAPTARGET_CONSTRAINT) { + _desktop->snapindicator->set_new_snaptarget(fp); + } + } else { + Geom::Point origin = _last_drag_origin(); + Geom::Point delta = new_pos - origin; + Geom::Dim2 d = (fabs(delta[Geom::X]) < fabs(delta[Geom::Y])) ? Geom::X : Geom::Y; + new_pos[d] = origin[d]; + } + } + } else if (snap) { + Inkscape::SnappedPoint p = sm.freeSnap(Inkscape::SnapCandidatePoint(new_pos, _snapSourceType())); + if (p.getSnapped()) { + p.getPoint(new_pos); + _desktop->snapindicator->set_new_snaptarget(p); + } + } + + SelectableControlPoint::dragged(new_pos, event); +} + +bool Node::clicked(GdkEventButton *event) +{ + if(_pm()._nodeClicked(this, event)) + return true; + return SelectableControlPoint::clicked(event); +} + +Inkscape::SnapSourceType Node::_snapSourceType() +{ + if (_type == NODE_SMOOTH || _type == NODE_AUTO) + return SNAPSOURCE_NODE_SMOOTH; + return SNAPSOURCE_NODE_CUSP; +} +Inkscape::SnapTargetType Node::_snapTargetType() +{ + if (_type == NODE_SMOOTH || _type == NODE_AUTO) + return SNAPTARGET_NODE_SMOOTH; + return SNAPTARGET_NODE_CUSP; +} + +/** @brief Gets the handle that faces the given adjacent node. + * Will abort with error if the given node is not adjacent. */ +Handle *Node::handleToward(Node *to) +{ + if (_next() == to) { + return front(); + } + if (_prev() == to) { + return back(); + } + g_error("Node::handleToward(): second node is not adjacent!"); +} + +/** @brief Gets the node in the direction of the given handle. + * Will abort with error if the handle doesn't belong to this node. */ +Node *Node::nodeToward(Handle *dir) +{ + if (front() == dir) { + return _next(); + } + if (back() == dir) { + return _prev(); + } + g_error("Node::nodeToward(): handle is not a child of this node!"); +} + +/** @brief Gets the handle that goes in the direction opposite to the given adjacent node. + * Will abort with error if the given node is not adjacent. */ +Handle *Node::handleAwayFrom(Node *to) +{ + if (_next() == to) { + return back(); + } + if (_prev() == to) { + return front(); + } + g_error("Node::handleAwayFrom(): second node is not adjacent!"); +} + +/** @brief Gets the node in the direction opposite to the given handle. + * Will abort with error if the handle doesn't belong to this node. */ +Node *Node::nodeAwayFrom(Handle *h) +{ + if (front() == h) { + return _prev(); + } + if (back() == h) { + return _next(); + } + g_error("Node::nodeAwayFrom(): handle is not a child of this node!"); +} + +Glib::ustring Node::_getTip(unsigned state) +{ + if (state_held_shift(state)) { + bool can_drag_out = (_next() && _front.isDegenerate()) || (_prev() && _back.isDegenerate()); + if (can_drag_out) { + /*if (state_held_control(state)) { + return format_tip(C_("Path node tip", + "<b>Shift+Ctrl:</b> drag out a handle and snap its angle " + "to %f° increments"), snap_increment_degrees()); + }*/ + return C_("Path node tip", + "<b>Shift:</b> drag out a handle, click to toggle selection"); + } + return C_("Path node tip", "<b>Shift:</b> click to toggle selection"); + } + + if (state_held_control(state)) { + if (state_held_alt(state)) { + return C_("Path node tip", "<b>Ctrl+Alt:</b> move along handle lines, click to delete node"); + } + return C_("Path node tip", + "<b>Ctrl:</b> move along axes, click to change node type"); + } + + if (state_held_alt(state)) { + return C_("Path node tip", "<b>Alt:</b> sculpt nodes"); + } + + // No modifiers: assemble tip from node type + char const *nodetype = node_type_to_localized_string(_type); + if (_selection.transformHandlesEnabled() && selected()) { + if (_selection.size() == 1) { + return format_tip(C_("Path node tip", + "<b>%s:</b> drag to shape the path (more: Shift, Ctrl, Alt)"), nodetype); + } + return format_tip(C_("Path node tip", + "<b>%s:</b> drag to shape the path, click to toggle scale/rotation handles (more: Shift, Ctrl, Alt)"), nodetype); + } + return format_tip(C_("Path node tip", + "<b>%s:</b> drag to shape the path, click to select only this node (more: Shift, Ctrl, Alt)"), nodetype); +} + +Glib::ustring Node::_getDragTip(GdkEventMotion */*event*/) +{ + Geom::Point dist = position() - _last_drag_origin(); + GString *x = SP_PX_TO_METRIC_STRING(dist[Geom::X], _desktop->namedview->getDefaultMetric()); + GString *y = SP_PX_TO_METRIC_STRING(dist[Geom::Y], _desktop->namedview->getDefaultMetric()); + Glib::ustring ret = format_tip(C_("Path node tip", "Move node by %s, %s"), + x->str, y->str); + g_string_free(x, TRUE); + g_string_free(y, TRUE); + return ret; +} + +char const *Node::node_type_to_localized_string(NodeType type) +{ + switch (type) { + case NODE_CUSP: return _("Cusp node"); + case NODE_SMOOTH: return _("Smooth node"); + case NODE_SYMMETRIC: return _("Symmetric node"); + case NODE_AUTO: return _("Auto-smooth node"); + default: return ""; + } +} + +/** Determine whether two nodes are joined by a linear segment. */ +bool Node::_is_line_segment(Node *first, Node *second) +{ + if (!first || !second) return false; + if (first->_next() == second) + return first->_front.isDegenerate() && second->_back.isDegenerate(); + if (second->_next() == first) + return second->_front.isDegenerate() && first->_back.isDegenerate(); + return false; +} + +SPCtrlShapeType Node::_node_type_to_shape(NodeType type) +{ + switch(type) { + case NODE_CUSP: return SP_CTRL_SHAPE_DIAMOND; + case NODE_SMOOTH: return SP_CTRL_SHAPE_SQUARE; + case NODE_AUTO: return SP_CTRL_SHAPE_CIRCLE; + case NODE_SYMMETRIC: return SP_CTRL_SHAPE_SQUARE; + default: return SP_CTRL_SHAPE_DIAMOND; + } +} + + +/** + * @class NodeList + * @brief An editable list of nodes representing a subpath. + * + * It can optionally be cyclic to represent a closed path. + * The list has iterators that act like plain node iterators, but can also be used + * to obtain shared pointers to nodes. + */ + +NodeList::NodeList(SubpathList &splist) + : _list(splist) + , _closed(false) +{ + this->ln_list = this; + this->ln_next = this; + this->ln_prev = this; +} + +NodeList::~NodeList() +{ + clear(); +} + +bool NodeList::empty() +{ + return ln_next == this; +} + +NodeList::size_type NodeList::size() +{ + size_type sz = 0; + for (ListNode *ln = ln_next; ln != this; ln = ln->ln_next) ++sz; + return sz; +} + +bool NodeList::closed() +{ + return _closed; +} + +/** A subpath is degenerate if it has no segments - either one node in an open path + * or no nodes in a closed path */ +bool NodeList::degenerate() +{ + return closed() ? empty() : ++begin() == end(); +} + +NodeList::iterator NodeList::before(double t, double *fracpart) +{ + double intpart; + *fracpart = std::modf(t, &intpart); + int index = intpart; + + iterator ret = begin(); + std::advance(ret, index); + return ret; +} + +// insert a node before i +NodeList::iterator NodeList::insert(iterator i, Node *x) +{ + ListNode *ins = i._node; + x->ln_next = ins; + x->ln_prev = ins->ln_prev; + ins->ln_prev->ln_next = x; + ins->ln_prev = x; + x->ln_list = this; + return iterator(x); +} + +void NodeList::splice(iterator pos, NodeList &list) +{ + splice(pos, list, list.begin(), list.end()); +} + +void NodeList::splice(iterator pos, NodeList &list, iterator i) +{ + NodeList::iterator j = i; + ++j; + splice(pos, list, i, j); +} + +void NodeList::splice(iterator pos, NodeList &/*list*/, iterator first, iterator last) +{ + ListNode *ins_beg = first._node, *ins_end = last._node, *at = pos._node; + for (ListNode *ln = ins_beg; ln != ins_end; ln = ln->ln_next) { + ln->ln_list = this; + } + ins_beg->ln_prev->ln_next = ins_end; + ins_end->ln_prev->ln_next = at; + at->ln_prev->ln_next = ins_beg; + + ListNode *atprev = at->ln_prev; + at->ln_prev = ins_end->ln_prev; + ins_end->ln_prev = ins_beg->ln_prev; + ins_beg->ln_prev = atprev; +} + +void NodeList::shift(int n) +{ + // 1. make the list perfectly cyclic + ln_next->ln_prev = ln_prev; + ln_prev->ln_next = ln_next; + // 2. find new begin + ListNode *new_begin = ln_next; + if (n > 0) { + for (; n > 0; --n) new_begin = new_begin->ln_next; + } else { + for (; n < 0; ++n) new_begin = new_begin->ln_prev; + } + // 3. relink begin to list + ln_next = new_begin; + ln_prev = new_begin->ln_prev; + new_begin->ln_prev->ln_next = this; + new_begin->ln_prev = this; +} + +void NodeList::reverse() +{ + for (ListNode *ln = ln_next; ln != this; ln = ln->ln_prev) { + std::swap(ln->ln_next, ln->ln_prev); + Node *node = static_cast<Node*>(ln); + Geom::Point save_pos = node->front()->position(); + node->front()->setPosition(node->back()->position()); + node->back()->setPosition(save_pos); + } + std::swap(ln_next, ln_prev); +} + +void NodeList::clear() +{ + for (iterator i = begin(); i != end();) erase (i++); +} + +NodeList::iterator NodeList::erase(iterator i) +{ + // some gymnastics are required to ensure that the node is valid when deleted; + // otherwise the code that updates handle visibility will break + Node *rm = static_cast<Node*>(i._node); + ListNode *rmnext = rm->ln_next, *rmprev = rm->ln_prev; + ++i; + delete rm; + rmprev->ln_next = rmnext; + rmnext->ln_prev = rmprev; + return i; +} + +// TODO this method is very ugly! +// converting SubpathList to an intrusive list might allow us to get rid of it +void NodeList::kill() +{ + for (SubpathList::iterator i = _list.begin(); i != _list.end(); ++i) { + if (i->get() == this) { + _list.erase(i); + return; + } + } +} + +NodeList &NodeList::get(Node *n) { + return n->nodeList(); +} +NodeList &NodeList::get(iterator const &i) { + return *(i._node->ln_list); +} + + +/** + * @class SubpathList + * @brief Editable path composed of one or more subpaths + */ + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h new file mode 100644 index 000000000..af4cd7e3a --- /dev/null +++ b/src/ui/tool/node.h @@ -0,0 +1,411 @@ +/** @file + * Editable node and associated data structures. + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_NODE_H +#define SEEN_UI_TOOL_NODE_H + +#include <glib.h> +#include <iterator> +#include <iosfwd> +#include <stdexcept> +#include <tr1/functional> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> +#include <boost/operators.hpp> +#include "snapped-point.h" +#include "ui/tool/selectable-control-point.h" +#include "ui/tool/node-types.h" + + +namespace Inkscape { +namespace UI { +template <typename> class NodeIterator; +} +} + +namespace std { +namespace tr1 { +template <typename N> struct hash< Inkscape::UI::NodeIterator<N> >; +} +} + +namespace Inkscape { +namespace UI { + +class PathManipulator; +class MultiPathManipulator; + +class Node; +class Handle; +class NodeList; +class SubpathList; +template <typename> class NodeIterator; + +std::ostream &operator<<(std::ostream &, NodeType); + +/* +template <typename T> +struct ListMember { + T *next; + T *prev; +}; +struct SubpathMember : public ListMember<NodeListMember> { + Subpath *list; +}; +struct SubpathListMember : public ListMember<SubpathListMember> { + SubpathList *list; +}; +*/ + +struct ListNode { + ListNode *ln_next; + ListNode *ln_prev; + NodeList *ln_list; +}; + +struct NodeSharedData { + SPDesktop *desktop; + ControlPointSelection *selection; + SPCanvasGroup *node_group; + SPCanvasGroup *handle_group; + SPCanvasGroup *handle_line_group; +}; + +class Handle : public ControlPoint { +public: + virtual ~Handle(); + inline Geom::Point relativePos(); + inline double length(); + bool isDegenerate() { return _degenerate; } + + virtual void setVisible(bool); + virtual void move(Geom::Point const &p); + + virtual void setPosition(Geom::Point const &p); + inline void setRelativePos(Geom::Point const &p); + void setLength(double len); + void retract(); + void setDirection(Geom::Point const &from, Geom::Point const &to); + void setDirection(Geom::Point const &dir); + Node *parent() { return _parent; } + Handle *other(); + + static char const *handle_type_to_localized_string(NodeType type); +protected: + Handle(NodeSharedData const &data, Geom::Point const &initial_pos, Node *parent); + + virtual void dragged(Geom::Point &, GdkEventMotion *); + virtual bool grabbed(GdkEventMotion *); + virtual void ungrabbed(GdkEventButton *); + virtual bool clicked(GdkEventButton *); + + virtual Glib::ustring _getTip(unsigned state); + virtual Glib::ustring _getDragTip(GdkEventMotion *event); + virtual bool _hasDragTips() { return true; } +private: + inline PathManipulator &_pm(); + Node *_parent; // the handle's lifetime does not extend beyond that of the parent node, + // so a naked pointer is OK and allows setting it during Node's construction + SPCanvasItem *_handle_line; + bool _degenerate; // this is used often internally so it makes sense to cache this + + static Geom::Point _saved_other_pos; + static double _saved_length; + static bool _drag_out; + friend class Node; +}; + +class Node : ListNode, public SelectableControlPoint { +public: + Node(NodeSharedData const &data, Geom::Point const &pos); + virtual void move(Geom::Point const &p); + virtual void transform(Geom::Matrix const &m); + virtual Geom::Rect bounds(); + + NodeType type() { return _type; } + void setType(NodeType type, bool update_handles = true); + void showHandles(bool v); + void pickBestType(); // automatically determine the type from handle positions + bool isDegenerate() { return _front.isDegenerate() && _back.isDegenerate(); } + bool isEndNode(); + Handle *front() { return &_front; } + Handle *back() { return &_back; } + Handle *handleToward(Node *to); + Node *nodeToward(Handle *h); + Handle *handleAwayFrom(Node *to); + Node *nodeAwayFrom(Handle *h); + NodeList &nodeList() { return *(static_cast<ListNode*>(this)->ln_list); } + void sink(); + + static NodeType parse_nodetype(char x); + static char const *node_type_to_localized_string(NodeType type); + // temporarily public + virtual bool _eventHandler(GdkEvent *event); +protected: + virtual void dragged(Geom::Point &, GdkEventMotion *); + virtual bool grabbed(GdkEventMotion *); + virtual bool clicked(GdkEventButton *); + + virtual void _setState(State state); + virtual Glib::ustring _getTip(unsigned state); + virtual Glib::ustring _getDragTip(GdkEventMotion *event); + virtual bool _hasDragTips() { return true; } +private: + Node(Node const &); + void _fixNeighbors(Geom::Point const &old_pos, Geom::Point const &new_pos); + void _updateAutoHandles(); + void _linearGrow(int dir); + Node *_next(); + Node *_prev(); + Inkscape::SnapSourceType _snapSourceType(); + Inkscape::SnapTargetType _snapTargetType(); + inline PathManipulator &_pm(); + static SPCtrlShapeType _node_type_to_shape(NodeType type); + static bool _is_line_segment(Node *first, Node *second); + + // Handles are always present, but are not visible if they coincide with the node + // (are degenerate). A segment that has both handles degenerate is always treated + // as a line segment + Handle _front; ///< Node handle in the backward direction of the path + Handle _back; ///< Node handle in the forward direction of the path + NodeType _type; ///< Type of node - cusp, smooth... + bool _handles_shown; + friend class Handle; + friend class NodeList; + friend class NodeIterator<Node>; + friend class NodeIterator<Node const>; +}; + +/// Iterator for editable nodes +/** Use this class for all operations that require some knowledge about the node's + * neighbors. It is a bidirectional iterator. + * + * Because paths can be cyclic, node iterators have two different ways to + * increment and decrement them. When using ++/--, the end iterator will eventually + * be returned. Whent using advance()/retreat(), the end iterator will only be returned + * when the path is open. If it's closed, calling advance() will cycle indefinitely. + * This is particularly useful for cases where the adjacency of nodes is more important + * than their sequence order. + * + * When @a i is a node iterator, then: + * - <code>++i</code> moves the iterator to the next node in sequence order; + * - <code>--i</code> moves the iterator to the previous node in sequence order; + * - <code>i.next()</code> returns the next node with wrap-around; + * - <code>i.prev()</code> returns the previous node with wrap-around; + * - <code>i.advance()</code> moves the iterator to the next node with wrap-around; + * - <code>i.retreat()</code> moves the iterator to the previous node with wrap-around. + * + * next() and prev() do not change their iterator. They can return the end iterator + * if the path is open. + * + * Unlike most other iterators, you can check whether you've reached the end of the list + * without having access to the iterator's container. + * Simply use <code>if (i) { ...</code> + * */ +template <typename N> +class NodeIterator + : public boost::bidirectional_iterator_helper<NodeIterator<N>, N, std::ptrdiff_t, + N *, N &> +{ +public: + typedef NodeIterator self; + NodeIterator() + : _node(0) + {} + // default copy, default assign + + self &operator++() { + _node = _node->ln_next; + return *this; + } + self &operator--() { + _node = _node->ln_prev; + return *this; + } + bool operator==(self const &other) const { return _node == other._node; } + N &operator*() const { return *static_cast<N*>(_node); } + inline operator bool() const; // define after NodeList + /// Get a pointer to the underlying node. Equivalent to <code>&*i</code>. + N *get_pointer() const { return static_cast<N*>(_node); } + /// @see get_pointer() + N *ptr() const { return static_cast<N*>(_node); } + + self next() const { + self r(*this); + r.advance(); + return r; + } + self prev() const { + self r(*this); + r.retreat(); + return r; + } + self &advance(); + self &retreat(); +private: + NodeIterator(ListNode const *n) + : _node(const_cast<ListNode*>(n)) + {} + ListNode *_node; + friend class NodeList; +}; + +class NodeList : ListNode, boost::noncopyable, public boost::enable_shared_from_this<NodeList> { +public: + typedef std::size_t size_type; + typedef Node &reference; + typedef Node const &const_reference; + typedef Node *pointer; + typedef Node const *const_pointer; + typedef Node value_type; + typedef NodeIterator<value_type> iterator; + typedef NodeIterator<value_type const> const_iterator; + + // TODO Lame. Make this private and make SubpathList a factory + NodeList(SubpathList &_list); + ~NodeList(); + + // iterators + iterator begin() { return iterator(ln_next); } + iterator end() { return iterator(this); } + const_iterator begin() const { return const_iterator(ln_next); } + const_iterator end() const { return const_iterator(this); } + + // size + bool empty(); + size_type size(); + + // extra node-specific methods + bool closed(); + bool degenerate(); + void setClosed(bool c) { _closed = c; } + iterator before(double t, double *fracpart = NULL); + const_iterator before(double t, double *fracpart = NULL) const { + return const_iterator(before(t, fracpart)._node); + } + + // list operations + iterator insert(iterator pos, Node *x); + template <class InputIterator> + void insert(iterator pos, InputIterator first, InputIterator last) { + for (; first != last; ++first) insert(pos, *first); + } + void splice(iterator pos, NodeList &list); + void splice(iterator pos, NodeList &list, iterator i); + void splice(iterator pos, NodeList &list, iterator first, iterator last); + void reverse(); + void shift(int n); + void push_front(Node *x) { insert(begin(), x); } + void pop_front() { erase(begin()); } + void push_back(Node *x) { insert(end(), x); } + void pop_back() { erase(--end()); } + void clear(); + iterator erase(iterator pos); + iterator erase(iterator first, iterator last) { + NodeList::iterator ret = first; + while (first != last) ret = erase(first++); + return ret; + } + + // member access - undefined results when the list is empty + Node &front() { return *static_cast<Node*>(ln_next); } + Node &back() { return *static_cast<Node*>(ln_prev); } + + // HACK remove this subpath from its path. This will be removed later. + void kill(); + SubpathList &subpathList() { return _list; } + + static iterator get_iterator(Node *n) { return iterator(n); } + static const_iterator get_iterator(Node const *n) { return const_iterator(n); } + static NodeList &get(Node *n); + static NodeList &get(iterator const &i); +private: + // no copy or assign + NodeList(NodeList const &); + void operator=(NodeList const &); + + SubpathList &_list; + bool _closed; + + friend class Node; + friend class Handle; // required to access handle and handle line groups + friend class NodeIterator<Node>; + friend class NodeIterator<Node const>; +}; + +/** List of node lists. Represents an editable path. */ +class SubpathList : public std::list< boost::shared_ptr<NodeList> > { +public: + typedef std::list< boost::shared_ptr<NodeList> > list_type; + + SubpathList(PathManipulator &pm) : _path_manipulator(pm) {} + PathManipulator &pm() { return _path_manipulator; } + +private: + list_type _nodelists; + PathManipulator &_path_manipulator; + friend class NodeList; + friend class Node; + friend class Handle; +}; + + + +// define inline Handle funcs after definition of Node +inline Geom::Point Handle::relativePos() { + return position() - _parent->position(); +} +inline void Handle::setRelativePos(Geom::Point const &p) { + setPosition(_parent->position() + p); +} +inline double Handle::length() { + return relativePos().length(); +} +inline PathManipulator &Handle::_pm() { + return _parent->_pm(); +} +inline PathManipulator &Node::_pm() { + return nodeList().subpathList().pm(); +} + +// definitions for node iterator +template <typename N> +NodeIterator<N>::operator bool() const { + return _node && static_cast<ListNode*>(_node->ln_list) != _node; +} +template <typename N> +NodeIterator<N> &NodeIterator<N>::advance() { + ++(*this); + if (G_UNLIKELY(!*this) && _node->ln_list->closed()) ++(*this); + return *this; +} +template <typename N> +NodeIterator<N> &NodeIterator<N>::retreat() { + --(*this); + if (G_UNLIKELY(!*this) && _node->ln_list->closed()) --(*this); + return *this; +} + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp new file mode 100644 index 000000000..66f72f379 --- /dev/null +++ b/src/ui/tool/path-manipulator.cpp @@ -0,0 +1,1462 @@ +/** @file + * Path manipulator - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <string> +#include <sstream> +#include <deque> +#include <stdexcept> +#include <boost/shared_ptr.hpp> +#include <2geom/bezier-curve.h> +#include <2geom/bezier-utils.h> +#include <2geom/svg-path.h> +#include <glibmm.h> +#include <glibmm/i18n.h> +#include "ui/tool/path-manipulator.h" +#include "desktop.h" +#include "desktop-handles.h" +#include "display/sp-canvas.h" +#include "display/sp-canvas-util.h" +#include "display/curve.h" +#include "display/canvas-bpath.h" +#include "document.h" +#include "live_effects/effect.h" +#include "live_effects/lpeobject.h" +#include "live_effects/parameter/path.h" +#include "sp-path.h" +#include "helper/geom.h" +#include "preferences.h" +#include "style.h" +#include "ui/tool/control-point-selection.h" +#include "ui/tool/curve-drag-point.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/multi-path-manipulator.h" +#include "xml/node.h" +#include "xml/node-observer.h" + +namespace Inkscape { +namespace UI { + +namespace { +/// Types of path changes that we must react to. +enum PathChange { + PATH_CHANGE_D, + PATH_CHANGE_TRANSFORM +}; + +} // anonymous namespace + +/** + * Notifies the path manipulator when something changes the path being edited + * (e.g. undo / redo) + */ +class PathManipulatorObserver : public Inkscape::XML::NodeObserver { +public: + PathManipulatorObserver(PathManipulator *p, Inkscape::XML::Node *node) + : _pm(p) + , _node(node) + , _blocked(false) + { + Inkscape::GC::anchor(_node); + _node->addObserver(*this); + } + + ~PathManipulatorObserver() { + _node->removeObserver(*this); + Inkscape::GC::release(_node); + } + + virtual void notifyAttributeChanged(Inkscape::XML::Node &/*node*/, GQuark attr, + Util::ptr_shared<char>, Util::ptr_shared<char>) + { + // do nothing if blocked + if (_blocked) return; + + GQuark path_d = g_quark_from_static_string("d"); + GQuark path_transform = g_quark_from_static_string("transform"); + GQuark lpe_quark = _pm->_lpe_key.empty() ? 0 : g_quark_from_string(_pm->_lpe_key.data()); + + // only react to "d" (path data) and "transform" attribute changes + if (attr == lpe_quark || attr == path_d) { + _pm->_externalChange(PATH_CHANGE_D); + } else if (attr == path_transform) { + _pm->_externalChange(PATH_CHANGE_TRANSFORM); + } + } + + void block() { _blocked = true; } + void unblock() { _blocked = false; } +private: + PathManipulator *_pm; + Inkscape::XML::Node *_node; + bool _blocked; +}; + +void build_segment(Geom::PathBuilder &, Node *, Node *); + +PathManipulator::PathManipulator(MultiPathManipulator &mpm, SPPath *path, + Geom::Matrix const &et, guint32 outline_color, Glib::ustring lpe_key) + : PointManipulator(mpm._path_data.node_data.desktop, *mpm._path_data.node_data.selection) + , _subpaths(*this) + , _multi_path_manipulator(mpm) + , _path(path) + , _spcurve(new SPCurve()) + , _dragpoint(new CurveDragPoint(*this)) + , _observer(new PathManipulatorObserver(this, SP_OBJECT(path)->repr)) + , _edit_transform(et) + , _num_selected(0) + , _show_handles(true) + , _show_outline(false) + , _show_path_direction(false) + , _live_outline(true) + , _live_objects(true) + , _lpe_key(lpe_key) +{ + if (_lpe_key.empty()) { + _i2d_transform = sp_item_i2d_affine(SP_ITEM(path)); + } else { + _i2d_transform = Geom::identity(); + } + _d2i_transform = _i2d_transform.inverse(); + _dragpoint->setVisible(false); + + _getGeometry(); + + _outline = sp_canvas_bpath_new(_multi_path_manipulator._path_data.outline_group, NULL); + sp_canvas_item_hide(_outline); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(_outline), outline_color, 1.0, + SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(_outline), 0, SP_WIND_RULE_NONZERO); + + _selection.signal_update.connect( + sigc::mem_fun(*this, &PathManipulator::update)); + _selection.signal_point_changed.connect( + sigc::mem_fun(*this, &PathManipulator::_selectionChanged)); + _desktop->signal_zoom_changed.connect( + sigc::hide( sigc::mem_fun(*this, &PathManipulator::_updateOutlineOnZoomChange))); + + _createControlPointsFromGeometry(); +} + +PathManipulator::~PathManipulator() +{ + delete _dragpoint; + delete _observer; + gtk_object_destroy(_outline); + _spcurve->unref(); + clear(); +} + +/** Handle motion events to update the position of the curve drag point. */ +bool PathManipulator::event(GdkEvent *event) +{ + if (empty()) return false; + + switch (event->type) + { + case GDK_MOTION_NOTIFY: + _updateDragPoint(event_point(event->motion)); + break; + default: break; + } + return false; +} + +/** Check whether the manipulator has any nodes. */ +bool PathManipulator::empty() { + return !_path || _subpaths.empty(); +} + +/** Update the display and the outline of the path. */ +void PathManipulator::update() +{ + _createGeometryFromControlPoints(); +} + +/** Store the changes to the path in XML. */ +void PathManipulator::writeXML() +{ + if (!_live_outline) + _updateOutline(); + if (!_live_objects) + _setGeometry(); + + if (!_path) return; + _observer->block(); + if (!empty()) { + SP_OBJECT(_path)->updateRepr(); + _getXMLNode()->setAttribute(_nodetypesKey().data(), _createTypeString().data()); + } else { + // this manipulator will have to be destroyed right after this call + _getXMLNode()->removeObserver(*_observer); + sp_object_ref(_path); + _path->deleteObject(true, true); + sp_object_unref(_path); + _path = 0; + } + _observer->unblock(); +} + +/** Remove all nodes from the path. */ +void PathManipulator::clear() +{ + // no longer necessary since nodes remove themselves from selection on destruction + //_removeNodesFromSelection(); + _subpaths.clear(); +} + +/** Select all nodes in subpaths that have something selected. */ +void PathManipulator::selectSubpaths() +{ + for (std::list<SubpathPtr>::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + NodeList::iterator sp_start = (*i)->begin(), sp_end = (*i)->end(); + for (NodeList::iterator j = sp_start; j != sp_end; ++j) { + if (j->selected()) { + // if at least one of the nodes from this subpath is selected, + // select all nodes from this subpath + for (NodeList::iterator ins = sp_start; ins != sp_end; ++ins) + _selection.insert(ins.ptr()); + continue; + } + } + } +} + +/** Move the selection forward or backward by one node in each subpath, based on the sign + * of the parameter. */ +void PathManipulator::shiftSelection(int dir) +{ + if (dir == 0) return; + if (_num_selected == 0) { + // select the first node of the path. + SubpathList::iterator s = _subpaths.begin(); + if (s == _subpaths.end()) return; + NodeList::iterator n = (*s)->begin(); + if (n != (*s)->end()) + _selection.insert(n.ptr()); + return; + } + // We cannot do any tricks here, like iterating in different directions based on + // the sign and only setting the selection of nodes behind us, because it would break + // for closed paths. + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + std::deque<bool> sels; // I hope this is specialized for bools! + unsigned num = 0; + + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + sels.push_back(j->selected()); + _selection.erase(j.ptr()); + ++num; + } + if (num == 0) continue; // should never happen! zero-node subpaths are not allowed + + num = 0; + // In closed subpath, shift the selection cyclically. In an open one, + // let the selection 'slide into nothing' at ends. + if (dir > 0) { + if ((*i)->closed()) { + bool last = sels.back(); + sels.pop_back(); + sels.push_front(last); + } else { + sels.push_front(false); + } + } else { + if ((*i)->closed()) { + bool first = sels.front(); + sels.pop_front(); + sels.push_back(first); + } else { + sels.push_back(false); + num = 1; + } + } + + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + if (sels[num]) _selection.insert(j.ptr()); + ++num; + } + } +} + +/** Invert selection in the selected subpaths. */ +void PathManipulator::invertSelectionInSubpaths() +{ + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + if (j->selected()) { + // found selected node - invert selection in this subpath + for (NodeList::iterator k = (*i)->begin(); k != (*i)->end(); ++k) { + if (k->selected()) _selection.erase(k.ptr()); + else _selection.insert(k.ptr()); + } + // next subpath + break; + } + } + } +} + +/** Insert a new node in the middle of each selected segment. */ +void PathManipulator::insertNodes() +{ + if (_num_selected < 2) return; + + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + NodeList::iterator k = j.next(); + if (k && j->selected() && k->selected()) { + j = subdivideSegment(j, 0.5); + _selection.insert(j.ptr()); + } + } + } +} + +/** Replace contiguous selections of nodes in each subpath with one node. */ +void PathManipulator::weldNodes(NodeList::iterator preserve_pos) +{ + if (_num_selected < 2) return; + hideDragPoint(); + + bool pos_valid = preserve_pos; + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + SubpathPtr sp = *i; + unsigned num_selected = 0, num_unselected = 0; + for (NodeList::iterator j = sp->begin(); j != sp->end(); ++j) { + if (j->selected()) ++num_selected; + else ++num_unselected; + } + if (num_selected < 2) continue; + if (num_unselected == 0) { + // if all nodes in a subpath are selected, the operation doesn't make much sense + continue; + } + + // Start from unselected node in closed paths, so that we don't start in the middle + // of a selection + NodeList::iterator sel_beg = sp->begin(), sel_end; + if (sp->closed()) { + while (sel_beg->selected()) ++sel_beg; + } + + // Work loop + while (num_selected > 0) { + // Find selected node + while (sel_beg && !sel_beg->selected()) sel_beg = sel_beg.next(); + if (!sel_beg) throw std::logic_error("Join nodes: end of open path reached, " + "but there are still nodes to process!"); + + // note: this is initialized to zero, because the loop below counts sel_beg as well + // the loop conditions are simpler that way + unsigned num_points = 0; + bool use_pos = false; + Geom::Point back_pos, front_pos; + back_pos = *sel_beg->back(); + + for (sel_end = sel_beg; sel_end && sel_end->selected(); sel_end = sel_end.next()) { + ++num_points; + front_pos = *sel_end->front(); + if (pos_valid && sel_end == preserve_pos) use_pos = true; + } + if (num_points > 1) { + Geom::Point joined_pos; + if (use_pos) { + joined_pos = preserve_pos->position(); + pos_valid = false; + } else { + joined_pos = Geom::middle_point(back_pos, front_pos); + } + sel_beg->setType(NODE_CUSP, false); + sel_beg->move(joined_pos); + // do not move handles if they aren't degenerate + if (!sel_beg->back()->isDegenerate()) { + sel_beg->back()->setPosition(back_pos); + } + if (!sel_end.prev()->front()->isDegenerate()) { + sel_beg->front()->setPosition(front_pos); + } + sel_beg = sel_beg.next(); + while (sel_beg != sel_end) { + NodeList::iterator next = sel_beg.next(); + sp->erase(sel_beg); + sel_beg = next; + --num_selected; + } + } + --num_selected; // for the joined node or single selected node + } + } +} + +/** Remove nodes in the middle of selected segments. */ +void PathManipulator::weldSegments() +{ + if (_num_selected < 2) return; + hideDragPoint(); + + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + SubpathPtr sp = *i; + unsigned num_selected = 0, num_unselected = 0; + for (NodeList::iterator j = sp->begin(); j != sp->end(); ++j) { + if (j->selected()) ++num_selected; + else ++num_unselected; + } + if (num_selected < 3) continue; + if (num_unselected == 0 && sp->closed()) { + // if all nodes in a closed subpath are selected, the operation doesn't make much sense + continue; + } + + // Start from unselected node in closed paths, so that we don't start in the middle + // of a selection + NodeList::iterator sel_beg = sp->begin(), sel_end; + if (sp->closed()) { + while (sel_beg->selected()) ++sel_beg; + } + + // Work loop + while (num_selected > 0) { + // Find selected node + while (sel_beg && !sel_beg->selected()) sel_beg = sel_beg.next(); + if (!sel_beg) throw std::logic_error("Join nodes: end of open path reached, " + "but there are still nodes to process!"); + + // note: this is initialized to zero, because the loop below counts sel_beg as well + // the loop conditions are simpler that way + unsigned num_points = 0; + + // find the end of selected segment + for (sel_end = sel_beg; sel_end && sel_end->selected(); sel_end = sel_end.next()) { + ++num_points; + } + if (num_points > 2) { + // remove nodes in the middle + sel_beg = sel_beg.next(); + while (sel_beg != sel_end.prev()) { + NodeList::iterator next = sel_beg.next(); + sp->erase(sel_beg); + sel_beg = next; + } + sel_beg = sel_end; + } + num_selected -= num_points; + } + } +} + +/** Break the subpath at selected nodes. It also works for single node closed paths. */ +void PathManipulator::breakNodes() +{ + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + SubpathPtr sp = *i; + NodeList::iterator cur = sp->begin(), end = sp->end(); + if (!sp->closed()) { + // Each open path must have at least two nodes so no checks are required. + // For 2-node open paths, cur == end + ++cur; + --end; + } + for (; cur != end; ++cur) { + if (!cur->selected()) continue; + SubpathPtr ins; + bool becomes_open = false; + + if (sp->closed()) { + // Move the node to break at to the beginning of path + if (cur != sp->begin()) + sp->splice(sp->begin(), *sp, cur, sp->end()); + sp->setClosed(false); + ins = sp; + becomes_open = true; + } else { + SubpathPtr new_sp(new NodeList(_subpaths)); + new_sp->splice(new_sp->end(), *sp, sp->begin(), cur); + _subpaths.insert(i, new_sp); + ins = new_sp; + } + + Node *n = new Node(_multi_path_manipulator._path_data.node_data, cur->position()); + ins->insert(ins->end(), n); + cur->setType(NODE_CUSP, false); + n->back()->setRelativePos(cur->back()->relativePos()); + cur->back()->retract(); + n->sink(); + + if (becomes_open) { + cur = sp->begin(); // this will be increased to ++sp->begin() + end = --sp->end(); + } + } + } +} + +/** Delete selected nodes in the path, optionally substituting deleted segments with bezier curves + * in a way that attempts to preserve the original shape of the curve. */ +void PathManipulator::deleteNodes(bool keep_shape) +{ + if (_num_selected == 0) return; + hideDragPoint(); + + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end();) { + SubpathPtr sp = *i; + + // If there are less than 2 unselected nodes in an open subpath or no unselected nodes + // in a closed one, delete entire subpath. + unsigned num_unselected = 0, num_selected = 0; + for (NodeList::iterator j = sp->begin(); j != sp->end(); ++j) { + if (j->selected()) ++num_selected; + else ++num_unselected; + } + if (num_selected == 0) { + ++i; + continue; + } + if (sp->closed() ? (num_unselected < 1) : (num_unselected < 2)) { + _subpaths.erase(i++); + continue; + } + + // In closed paths, start from an unselected node - otherwise we might start in the middle + // of a selected stretch and the resulting bezier fit would be suboptimal + NodeList::iterator sel_beg = sp->begin(), sel_end; + if (sp->closed()) { + while (sel_beg->selected()) ++sel_beg; + } + sel_end = sel_beg; + + while (num_selected > 0) { + while (sel_beg && !sel_beg->selected()) { + sel_beg = sel_beg.next(); + } + sel_end = sel_beg; + + while (sel_end && sel_end->selected()) { + sel_end = sel_end.next(); + } + + num_selected -= _deleteStretch(sel_beg, sel_end, keep_shape); + sel_beg = sel_end; + } + ++i; + } +} + +/** @brief Delete nodes between the two iterators. + * The given range can cross the beginning of the subpath in closed subpaths. + * @param start Beginning of the range to delete + * @param end End of the range + * @param keep_shape Whether to fit the handles at surrounding nodes to approximate + * the shape before deletion + * @return Number of deleted nodes */ +unsigned PathManipulator::_deleteStretch(NodeList::iterator start, NodeList::iterator end, bool keep_shape) +{ + unsigned const samples_per_segment = 10; + double const t_step = 1.0 / samples_per_segment; + + unsigned del_len = 0; + for (NodeList::iterator i = start; i != end; i = i.next()) { + ++del_len; + } + if (del_len == 0) return 0; + + // set surrounding node types to cusp if: + // 1. keep_shape is on, or + // 2. we are deleting at the end or beginning of an open path + if ((keep_shape || !end) && start.prev()) start.prev()->setType(NODE_CUSP, false); + if ((keep_shape || !start.prev()) && end) end->setType(NODE_CUSP, false); + + if (keep_shape && start.prev() && end) { + unsigned num_samples = (del_len + 1) * samples_per_segment + 1; + Geom::Point *bezier_data = new Geom::Point[num_samples]; + Geom::Point result[4]; + unsigned seg = 0; + + for (NodeList::iterator cur = start.prev(); cur != end; cur = cur.next()) { + Geom::CubicBezier bc(*cur, *cur->front(), *cur.next(), *cur.next()->back()); + for (unsigned s = 0; s < samples_per_segment; ++s) { + bezier_data[seg * samples_per_segment + s] = bc.pointAt(t_step * s); + } + ++seg; + } + // Fill last point + bezier_data[num_samples - 1] = end->position(); + // Compute replacement bezier curve + // TODO the fitting algorithm sucks - rewrite it to be awesome + bezier_fit_cubic(result, bezier_data, num_samples, 0.5); + delete[] bezier_data; + + start.prev()->front()->setPosition(result[1]); + end->back()->setPosition(result[2]); + } + + // We can't use nl->erase(start, end), because it would break when the stretch + // crosses the beginning of a closed subpath + NodeList &nl = start->nodeList(); + while (start != end) { + NodeList::iterator next = start.next(); + nl.erase(start); + start = next; + } + + return del_len; +} + +/** Removes selected segments */ +void PathManipulator::deleteSegments() +{ + if (_num_selected == 0) return; + hideDragPoint(); + + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end();) { + SubpathPtr sp = *i; + bool has_unselected = false; + unsigned num_selected = 0; + for (NodeList::iterator j = sp->begin(); j != sp->end(); ++j) { + if (j->selected()) { + ++num_selected; + } else { + has_unselected = true; + } + } + if (!has_unselected) { + _subpaths.erase(i++); + continue; + } + + NodeList::iterator sel_beg = sp->begin(); + if (sp->closed()) { + while (sel_beg && sel_beg->selected()) ++sel_beg; + } + while (num_selected > 0) { + if (!sel_beg->selected()) { + sel_beg = sel_beg.next(); + continue; + } + NodeList::iterator sel_end = sel_beg; + unsigned num_points = 0; + while (sel_end && sel_end->selected()) { + sel_end = sel_end.next(); + ++num_points; + } + if (num_points >= 2) { + // Retract end handles + sel_end.prev()->setType(NODE_CUSP, false); + sel_end.prev()->back()->retract(); + sel_beg->setType(NODE_CUSP, false); + sel_beg->front()->retract(); + if (sp->closed()) { + // In closed paths, relocate the beginning of the path to the last selected + // node and then unclose it. Remove the nodes from the first selected node + // to the new end of path. + if (sel_end.prev() != sp->begin()) + sp->splice(sp->begin(), *sp, sel_end.prev(), sp->end()); + sp->setClosed(false); + sp->erase(sel_beg.next(), sp->end()); + } else { + // for open paths: + // 1. At end or beginning, delete including the node on the end or beginning + // 2. In the middle, delete only inner nodes + if (sel_beg == sp->begin()) { + sp->erase(sp->begin(), sel_end.prev()); + } else if (sel_end == sp->end()) { + sp->erase(sel_beg.next(), sp->end()); + } else { + SubpathPtr new_sp(new NodeList(_subpaths)); + new_sp->splice(new_sp->end(), *sp, sp->begin(), sel_beg.next()); + _subpaths.insert(i, new_sp); + if (sel_end.prev()) + sp->erase(sp->begin(), sel_end.prev()); + } + } + } + sel_beg = sel_end; + num_selected -= num_points; + } + ++i; + } +} + +/** Reverse subpaths of the path. + * @param selected_only If true, only paths that have at least one selected node + * will be reversed. Otherwise all subpaths will be reversed. */ +void PathManipulator::reverseSubpaths(bool selected_only) +{ + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + if (selected_only) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + if (j->selected()) { + (*i)->reverse(); + break; // continue with the next subpath + } + } + } else { + (*i)->reverse(); + } + } +} + +/** Make selected segments curves / lines. */ +void PathManipulator::setSegmentType(SegmentType type) +{ + if (_num_selected == 0) return; + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + NodeList::iterator k = j.next(); + if (!(k && j->selected() && k->selected())) continue; + switch (type) { + case SEGMENT_STRAIGHT: + if (j->front()->isDegenerate() && k->back()->isDegenerate()) + break; + j->front()->move(*j); + k->back()->move(*k); + break; + case SEGMENT_CUBIC_BEZIER: + if (!j->front()->isDegenerate() || !k->back()->isDegenerate()) + break; + // move both handles to 1/3 of the line + j->front()->move(j->position() + (k->position() - j->position()) / 3); + k->back()->move(k->position() + (j->position() - k->position()) / 3); + break; + } + } + } +} + +void PathManipulator::scaleHandle(Node *n, int which, int dir, bool pixel) +{ + if (n->type() == NODE_SYMMETRIC || n->type() == NODE_AUTO) { + n->setType(NODE_SMOOTH); + } + Handle *h = _chooseHandle(n, which); + double length_change; + + if (pixel) { + length_change = 1.0 / _desktop->current_zoom() * dir; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + length_change = prefs->getDoubleLimited("/options/defaultscale/value", 2, 1, 1000); + length_change *= dir; + } + + Geom::Point relpos; + if (h->isDegenerate()) { + if (dir < 0) return; + Node *nh = n->nodeToward(h); + if (!nh) return; + relpos = Geom::unit_vector(nh->position() - n->position()) * length_change; + } else { + relpos = h->relativePos(); + double rellen = relpos.length(); + relpos *= ((rellen + length_change) / rellen); + } + h->setRelativePos(relpos); + update(); + + gchar const *key = which < 0 ? "handle:scale:left" : "handle:scale:right"; + _commit(_("Scale handle"), key); +} + +void PathManipulator::rotateHandle(Node *n, int which, int dir, bool pixel) +{ + if (n->type() != NODE_CUSP) { + n->setType(NODE_CUSP); + } + Handle *h = _chooseHandle(n, which); + if (h->isDegenerate()) return; + + double angle; + if (pixel) { + // Rotate by "one pixel" + angle = atan2(1.0 / _desktop->current_zoom(), h->length()) * dir; + } else { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + angle = M_PI * dir / snaps; + } + + h->setRelativePos(h->relativePos() * Geom::Rotate(angle)); + update(); + gchar const *key = which < 0 ? "handle:rotate:left" : "handle:rotate:right"; + _commit(_("Rotate handle"), key); +} + +Handle *PathManipulator::_chooseHandle(Node *n, int which) +{ + NodeList::iterator i = NodeList::get_iterator(n); + Node *prev = i.prev().ptr(); + Node *next = i.next().ptr(); + + // on an endnode, the remaining handle automatically wins + if (!next) return n->back(); + if (!prev) return n->front(); + + // compare X coord ofline segments + Geom::Point npos = next->position(); + Geom::Point ppos = prev->position(); + if (which < 0) { + // pick left handle. + // we just swap the handles and pick the right handle below. + std::swap(npos, ppos); + } + + if (npos[Geom::X] >= ppos[Geom::X]) { + return n->front(); + } else { + return n->back(); + } +} + +/** Set the visibility of handles. */ +void PathManipulator::showHandles(bool show) +{ + if (show == _show_handles) return; + if (show) { + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + if (!j->selected()) continue; + j->showHandles(true); + if (j.prev()) j.prev()->showHandles(true); + if (j.next()) j.next()->showHandles(true); + } + } + } else { + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + j->showHandles(false); + } + } + } + _show_handles = show; +} + +/** Set the visibility of outline. */ +void PathManipulator::showOutline(bool show) +{ + if (show == _show_outline) return; + _show_outline = show; + _updateOutline(); +} + +void PathManipulator::showPathDirection(bool show) +{ + if (show == _show_path_direction) return; + _show_path_direction = show; + _updateOutline(); +} + +void PathManipulator::setLiveOutline(bool set) +{ + _live_outline = set; +} + +void PathManipulator::setLiveObjects(bool set) +{ + _live_objects = set; +} + +void PathManipulator::setControlsTransform(Geom::Matrix const &tnew) +{ + Geom::Matrix delta = _i2d_transform.inverse() * _edit_transform.inverse() * tnew * _i2d_transform; + _edit_transform = tnew; + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + j->transform(delta); + } + } + _createGeometryFromControlPoints(); +} + +/** Hide the curve drag point until the next motion event. + * This should be called at the beginning of every method that can delete nodes. + * Otherwise the invalidated iterator in the dragpoint can cause crashes. */ +void PathManipulator::hideDragPoint() +{ + _dragpoint->setVisible(false); + _dragpoint->setIterator(NodeList::iterator()); +} + +/** Insert a node in the segment beginning with the supplied iterator, + * at the given time value */ +NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, double t) +{ + if (!first) throw std::invalid_argument("Subdivide after invalid iterator"); + NodeList &list = NodeList::get(first); + NodeList::iterator second = first.next(); + if (!second) throw std::invalid_argument("Subdivide after last node in open path"); + + // We need to insert the segment after 'first'. We can't simply use 'second' + // as the point of insertion, because when 'first' is the last node of closed path, + // the new node will be inserted as the first node instead. + NodeList::iterator insert_at = first; + ++insert_at; + + NodeList::iterator inserted; + if (first->front()->isDegenerate() && second->back()->isDegenerate()) { + // for a line segment, insert a cusp node + Node *n = new Node(_multi_path_manipulator._path_data.node_data, + Geom::lerp(t, first->position(), second->position())); + n->setType(NODE_CUSP, false); + inserted = list.insert(insert_at, n); + } else { + // build bezier curve and subdivide + Geom::CubicBezier temp(first->position(), first->front()->position(), + second->back()->position(), second->position()); + std::pair<Geom::CubicBezier, Geom::CubicBezier> div = temp.subdivide(t); + std::vector<Geom::Point> seg1 = div.first.points(), seg2 = div.second.points(); + + // set new handle positions + Node *n = new Node(_multi_path_manipulator._path_data.node_data, seg2[0]); + n->back()->setPosition(seg1[2]); + n->front()->setPosition(seg2[1]); + n->setType(NODE_SMOOTH, false); + inserted = list.insert(insert_at, n); + + first->front()->move(seg1[1]); + second->back()->move(seg2[2]); + } + return inserted; +} + +/** Find the node that is closest/farthest from the origin + * @param origin Point of reference + * @param search_selected Consider selected nodes + * @param search_unselected Consider unselected nodes + * @param closest If true, return closest node, if false, return farthest + * @return The matching node, or an empty iterator if none found + */ +NodeList::iterator PathManipulator::extremeNode(NodeList::iterator origin, bool search_selected, + bool search_unselected, bool closest) +{ + NodeList::iterator match; + double extr_dist = closest ? HUGE_VAL : -HUGE_VAL; + if (_num_selected == 0 && !search_unselected) return match; + + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + if(j->selected()) { + if (!search_selected) continue; + } else { + if (!search_unselected) continue; + } + double dist = Geom::distance(*j, *origin); + bool cond = closest ? (dist < extr_dist) : (dist > extr_dist); + if (cond) { + match = j; + extr_dist = dist; + } + } + } + return match; +} + +/** Called by the XML observer when something else than us modifies the path. */ +void PathManipulator::_externalChange(unsigned type) +{ + switch (type) { + case PATH_CHANGE_D: { + _getGeometry(); + + // ugly: stored offsets of selected nodes in a vector + // vector<bool> should be specialized so that it takes only 1 bit per value + std::vector<bool> selpos; + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + selpos.push_back(j->selected()); + } + } + unsigned size = selpos.size(), curpos = 0; + + _createControlPointsFromGeometry(); + + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + if (curpos >= size) goto end_restore; + if (selpos[curpos]) _selection.insert(j.ptr()); + ++curpos; + } + } + end_restore: + + _updateOutline(); + } break; + case PATH_CHANGE_TRANSFORM: { + Geom::Matrix i2d_change = _d2i_transform; + _i2d_transform = sp_item_i2d_affine(SP_ITEM(_path)); + _d2i_transform = _i2d_transform.inverse(); + i2d_change *= _i2d_transform; + for (SubpathList::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + j->transform(i2d_change); + } + } + _updateOutline(); + } break; + default: break; + } +} + +/** Create nodes and handles based on the XML of the edited path. */ +void PathManipulator::_createControlPointsFromGeometry() +{ + clear(); + + // sanitize pathvector and store it in SPCurve, + // so that _updateDragPoint doesn't crash on paths with naked movetos + Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(_spcurve->get_pathvector()); + for (Geom::PathVector::iterator i = pathv.begin(); i != pathv.end(); ) { + // NOTE: this utilizes the fact that Geom::PathVector is an std::vector. + // When we erase an element, the next one slides into position, + // so we do not increment the iterator even though it is theoretically invalidated. + if (i->empty()) { + pathv.erase(i); + } else { + ++i; + } + } + _spcurve->set_pathvector(pathv); + + pathv *= (_edit_transform * _i2d_transform); + + // in this loop, we know that there are no zero-segment subpaths + for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { + // prepare new subpath + SubpathPtr subpath(new NodeList(_subpaths)); + _subpaths.push_back(subpath); + + Node *previous_node = new Node(_multi_path_manipulator._path_data.node_data, pit->initialPoint()); + subpath->push_back(previous_node); + Geom::Curve const &cseg = pit->back_closed(); + bool fuse_ends = pit->closed() + && Geom::are_near(cseg.initialPoint(), cseg.finalPoint()); + + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { + Geom::Point pos = cit->finalPoint(); + Node *current_node; + // if the closing segment is degenerate and the path is closed, we need to move + // the handle of the first node instead of creating a new one + if (fuse_ends && cit == --(pit->end_open())) { + current_node = subpath->begin().get_pointer(); + } else { + /* regardless of segment type, create a new node at the end + * of this segment (unless this is the last segment of a closed path + * with a degenerate closing segment */ + current_node = new Node(_multi_path_manipulator._path_data.node_data, pos); + subpath->push_back(current_node); + } + // if this is a bezier segment, move handles appropriately + if (Geom::CubicBezier const *cubic_bezier = + dynamic_cast<Geom::CubicBezier const*>(&*cit)) + { + std::vector<Geom::Point> points = cubic_bezier->points(); + + previous_node->front()->setPosition(points[1]); + current_node ->back() ->setPosition(points[2]); + } + previous_node = current_node; + } + // If the path is closed, make the list cyclic + if (pit->closed()) subpath->setClosed(true); + } + + // we need to set the nodetypes after all the handles are in place, + // so that pickBestType works correctly + // TODO maybe migrate to inkscape:node-types? + // TODO move this into SPPath - do not manipulate directly + gchar const *nts_raw = _path ? _path->repr->attribute(_nodetypesKey().data()) : 0; + std::string nodetype_string = nts_raw ? nts_raw : ""; + /* Calculate the needed length of the nodetype string. + * For closed paths, the entry is duplicated for the starting node, + * so we can just use the count of segments including the closing one + * to include the extra end node. */ + std::string::size_type nodetype_len = 0; + for (Geom::PathVector::const_iterator i = pathv.begin(); i != pathv.end(); ++i) { + if (i->empty()) continue; + nodetype_len += i->size_closed(); + } + /* pad the string to required length with a bogus value. + * 'b' and any other letter not recognized by the parser causes the best fit to be set + * as the node type */ + if (nodetype_len > nodetype_string.size()) { + nodetype_string.append(nodetype_len - nodetype_string.size(), 'b'); + } + std::string::iterator tsi = nodetype_string.begin(); + for (std::list<SubpathPtr>::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + j->setType(Node::parse_nodetype(*tsi++), false); + } + if ((*i)->closed()) { + // STUPIDITY ALERT: it seems we need to use the duplicate type symbol instead of + // the first one to remain backward compatible. + (*i)->begin()->setType(Node::parse_nodetype(*tsi++), false); + } + } +} + +/** Construct the geometric representation of nodes and handles, update the outline + * and display */ +void PathManipulator::_createGeometryFromControlPoints() +{ + Geom::PathBuilder builder; + for (std::list<SubpathPtr>::iterator spi = _subpaths.begin(); spi != _subpaths.end(); ) { + SubpathPtr subpath = *spi; + if (subpath->empty()) { + _subpaths.erase(spi++); + continue; + } + NodeList::iterator prev = subpath->begin(); + builder.moveTo(prev->position()); + + for (NodeList::iterator i = ++subpath->begin(); i != subpath->end(); ++i) { + build_segment(builder, prev.ptr(), i.ptr()); + prev = i; + } + if (subpath->closed()) { + // Here we link the last and first node if the path is closed. + // If the last segment is Bezier, we add it. + if (!prev->front()->isDegenerate() || !subpath->begin()->back()->isDegenerate()) { + build_segment(builder, prev.ptr(), subpath->begin().ptr()); + } + // if that segment is linear, we just call closePath(). + builder.closePath(); + } + ++spi; + } + builder.finish(); + _spcurve->set_pathvector(builder.peek() * (_edit_transform * _i2d_transform).inverse()); + if (_live_outline) + _updateOutline(); + if (_live_objects) + _setGeometry(); +} + +/** Build one segment of the geometric representation. + * @relates PathManipulator */ +void build_segment(Geom::PathBuilder &builder, Node *prev_node, Node *cur_node) +{ + if (cur_node->back()->isDegenerate() && prev_node->front()->isDegenerate()) + { + // NOTE: It seems like the renderer cannot correctly handle vline / hline segments, + // and trying to display a path using them results in funny artifacts. + builder.lineTo(cur_node->position()); + } else { + // this is a bezier segment + builder.curveTo( + prev_node->front()->position(), + cur_node->back()->position(), + cur_node->position()); + } +} + +/** Construct a node type string to store in the sodipodi:nodetypes attribute. */ +std::string PathManipulator::_createTypeString() +{ + // precondition: no single-node subpaths + std::stringstream tstr; + for (std::list<SubpathPtr>::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + tstr << j->type(); + } + // nodestring format peculiarity: first node is counted twice for closed paths + if ((*i)->closed()) tstr << (*i)->begin()->type(); + } + return tstr.str(); +} + +/** Update the path outline. */ +void PathManipulator::_updateOutline() +{ + if (!_show_outline) { + sp_canvas_item_hide(_outline); + return; + } + + Geom::PathVector pv = _spcurve->get_pathvector(); + pv *= (_edit_transform * _i2d_transform); + // This SPCurve thing has to be killed with extreme prejudice + SPCurve *_hc = new SPCurve(); + if (_show_path_direction) { + // To show the direction, we append additional subpaths which consist of a single + // linear segment that starts at the time value of 0.5 and extends for 10 pixels + // at an angle 150 degrees from the unit tangent. This creates the appearance + // of little 'harpoons' that show the direction of the subpaths. + Geom::PathVector arrows; + for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) { + Geom::Path &path = *i; + for (Geom::Path::const_iterator j = path.begin(); j != path.end_default(); ++j) { + Geom::Point at = j->pointAt(0.5); + Geom::Point ut = j->unitTangentAt(0.5); + // rotate the point + ut *= Geom::Rotate(150.0 / 180.0 * M_PI); + Geom::Point arrow_end = _desktop->w2d( + _desktop->d2w(at) + Geom::unit_vector(_desktop->d2w(ut)) * 10.0); + + Geom::Path arrow(at); + arrow.appendNew<Geom::LineSegment>(arrow_end); + arrows.push_back(arrow); + } + } + pv.insert(pv.end(), arrows.begin(), arrows.end()); + } + _hc->set_pathvector(pv); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(_outline), _hc); + sp_canvas_item_show(_outline); + _hc->unref(); +} + +/** Retrieve the geometry of the edited object from the object tree */ +void PathManipulator::_getGeometry() +{ + using namespace Inkscape::LivePathEffect; + if (!_lpe_key.empty()) { + Effect *lpe = LIVEPATHEFFECT(_path)->get_lpe(); + if (lpe) { + PathParam *pathparam = dynamic_cast<PathParam *>(lpe->getParameter(_lpe_key.data())); + _spcurve->unref(); + _spcurve = new SPCurve(pathparam->get_pathvector()); + } + } else { + _spcurve->unref(); + _spcurve = sp_path_get_curve_for_edit(_path); + } +} + +/** Set the geometry of the edited object in the object tree, but do not commit to XML */ +void PathManipulator::_setGeometry() +{ + using namespace Inkscape::LivePathEffect; + if (empty()) return; + + if (!_lpe_key.empty()) { + // copied from nodepath.cpp + // NOTE: if we are editing an LPE param, _path is not actually an SPPath, it is + // a LivePathEffectObject. (mad laughter) + Effect *lpe = LIVEPATHEFFECT(_path)->get_lpe(); + if (lpe) { + PathParam *pathparam = dynamic_cast<PathParam *>(lpe->getParameter(_lpe_key.data())); + pathparam->set_new_value(_spcurve->get_pathvector(), false); + LIVEPATHEFFECT(_path)->requestModified(SP_OBJECT_MODIFIED_FLAG); + } + } else { + if (_path->repr->attribute("inkscape:original-d")) + sp_path_set_original_curve(_path, _spcurve, false, false); + else + sp_shape_set_curve(SP_SHAPE(_path), _spcurve, false); + } +} + +/** Figure out in what attribute to store the nodetype string. */ +Glib::ustring PathManipulator::_nodetypesKey() +{ + if (_lpe_key.empty()) return "sodipodi:nodetypes"; + return _lpe_key + "-nodetypes"; +} + +/** Return the XML node we are editing. + * This method is wrong but necessary at the moment. */ +Inkscape::XML::Node *PathManipulator::_getXMLNode() +{ + if (_lpe_key.empty()) return _path->repr; + return LIVEPATHEFFECT(_path)->repr; +} + +bool PathManipulator::_nodeClicked(Node *n, GdkEventButton *event) +{ + if (event->button != 1) return false; + if (held_alt(*event) && held_control(*event)) { + // Ctrl+Alt+click: delete nodes + hideDragPoint(); + NodeList::iterator iter = NodeList::get_iterator(n); + NodeList &nl = iter->nodeList(); + + if (nl.size() <= 1 || (nl.size() <= 2 && !nl.closed())) { + // Removing last node of closed path - delete it + nl.kill(); + } else { + // In other cases, delete the node under cursor + _deleteStretch(iter, iter.next(), true); + } + + if (!empty()) { + update(); + } + // We need to call MPM's method because it could have been our last node + _multi_path_manipulator._doneWithCleanup(_("Delete node")); + + return true; + } else if (held_control(*event)) { + // Ctrl+click: cycle between node types + if (n->isEndNode()) { + if (n->type() == NODE_CUSP) { + n->setType(NODE_SMOOTH); + } else { + n->setType(NODE_CUSP); + } + } else { + n->setType(static_cast<NodeType>((n->type() + 1) % NODE_LAST_REAL_TYPE)); + } + update(); + _commit(_("Cycle node type")); + return true; + } + return false; +} + +void PathManipulator::_handleGrabbed() +{ + _selection.hideTransformHandles(); +} + +void PathManipulator::_handleUngrabbed() +{ + _selection.restoreTransformHandles(); + _commit(_("Drag handle")); +} + +bool PathManipulator::_handleClicked(Handle *h, GdkEventButton *event) +{ + // retracting by Ctrl+click + if (event->button == 1 && held_control(*event)) { + h->move(h->parent()->position()); + update(); + _commit(_("Retract handle")); + return true; + } + return false; +} + +void PathManipulator::_selectionChanged(SelectableControlPoint *p, bool selected) +{ + if (selected) ++_num_selected; + else --_num_selected; + + // don't do anything if we do not show handles + if (!_show_handles) return; + + // only do something if a node changed selection state + Node *node = dynamic_cast<Node*>(p); + if (!node) return; + + // update handle display + NodeList::iterator iters[5]; + iters[2] = NodeList::get_iterator(node); + iters[1] = iters[2].prev(); + iters[3] = iters[2].next(); + if (selected) { + // selection - show handles on this node and adjacent ones + node->showHandles(true); + if (iters[1]) iters[1]->showHandles(true); + if (iters[3]) iters[3]->showHandles(true); + } else { + /* Deselection is more complex. + * The change might affect 3 nodes - this one and two adjacent. + * If the node and both its neighbors are deselected, hide handles. + * Otherwise, leave as is. */ + if (iters[1]) iters[0] = iters[1].prev(); + if (iters[3]) iters[4] = iters[3].next(); + bool nodesel[5]; + for (int i = 0; i < 5; ++i) { + nodesel[i] = iters[i] && iters[i]->selected(); + } + for (int i = 1; i < 4; ++i) { + if (iters[i] && !nodesel[i-1] && !nodesel[i] && !nodesel[i+1]) { + iters[i]->showHandles(false); + } + } + } +} + +/** Removes all nodes belonging to this manipulator from the control pont selection */ +void PathManipulator::_removeNodesFromSelection() +{ + // remove this manipulator's nodes from selection + for (std::list<SubpathPtr>::iterator i = _subpaths.begin(); i != _subpaths.end(); ++i) { + for (NodeList::iterator j = (*i)->begin(); j != (*i)->end(); ++j) { + _selection.erase(j.get_pointer()); + } + } +} + +/** Update the XML representation and put the specified annotation on the undo stack */ +void PathManipulator::_commit(Glib::ustring const &annotation) +{ + writeXML(); + sp_document_done(sp_desktop_document(_desktop), SP_VERB_CONTEXT_NODE, annotation.data()); +} + +void PathManipulator::_commit(Glib::ustring const &annotation, gchar const *key) +{ + writeXML(); + sp_document_maybe_done(sp_desktop_document(_desktop), key, SP_VERB_CONTEXT_NODE, + annotation.data()); +} + +/** Update the position of the curve drag point such that it is over the nearest + * point of the path. */ +void PathManipulator::_updateDragPoint(Geom::Point const &evp) +{ + Geom::Matrix to_desktop = _edit_transform * _i2d_transform; + Geom::PathVector pv = _spcurve->get_pathvector(); + boost::optional<Geom::PathVectorPosition> pvp + = Geom::nearestPoint(pv, _desktop->w2d(evp) * to_desktop.inverse()); + if (!pvp) return; + Geom::Point nearest_point = _desktop->d2w(pv.at(pvp->path_nr).pointAt(pvp->t) * to_desktop); + + double fracpart; + std::list<SubpathPtr>::iterator spi = _subpaths.begin(); + for (unsigned i = 0; i < pvp->path_nr; ++i, ++spi) {} + NodeList::iterator first = (*spi)->before(pvp->t, &fracpart); + + double stroke_tolerance = _getStrokeTolerance(); + if (Geom::distance(evp, nearest_point) < stroke_tolerance) { + _dragpoint->setVisible(true); + _dragpoint->setPosition(_desktop->w2d(nearest_point)); + _dragpoint->setSize(2 * stroke_tolerance); + _dragpoint->setTimeValue(fracpart); + _dragpoint->setIterator(first); + } else { + _dragpoint->setVisible(false); + } +} + +/// This is called on zoom change to update the direction arrows +void PathManipulator::_updateOutlineOnZoomChange() +{ + if (_show_path_direction) _updateOutline(); +} + +/** Compute the radius from the edge of the path where clicks chould initiate a curve drag + * or segment selection, in window coordinates. */ +double PathManipulator::_getStrokeTolerance() +{ + /* Stroke event tolerance is equal to half the stroke's width plus the global + * drag tolerance setting. */ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + double ret = prefs->getIntLimited("/options/dragtolerance/value", 2, 0, 100); + if (_path && SP_OBJECT_STYLE(_path) && !SP_OBJECT_STYLE(_path)->stroke.isNone()) { + ret += SP_OBJECT_STYLE(_path)->stroke_width.computed * 0.5 + * (_edit_transform * _i2d_transform).descrim() // scale to desktop coords + * _desktop->current_zoom(); // == _d2w.descrim() - scale to window coords + } + return ret; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/path-manipulator.h b/src/ui/tool/path-manipulator.h new file mode 100644 index 000000000..a8f1c957e --- /dev/null +++ b/src/ui/tool/path-manipulator.h @@ -0,0 +1,165 @@ +/** @file + * Path manipulator - a component that edits a single path on-canvas + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_PATH_MANIPULATOR_H +#define SEEN_UI_TOOL_PATH_MANIPULATOR_H + +#include <string> +#include <memory> +#include <2geom/pathvector.h> +#include <2geom/matrix.h> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "display/display-forward.h" +#include "forward.h" +#include "ui/tool/node.h" +#include "ui/tool/manipulator.h" + +struct SPCanvasItem; + +namespace Inkscape { +namespace XML { class Node; } + +namespace UI { + +class PathManipulator; +class ControlPointSelection; +class PathManipulatorObserver; +class CurveDragPoint; +class PathCanvasGroups; +class MultiPathManipulator; +class Node; +class Handle; + +struct PathSharedData { + NodeSharedData node_data; + SPCanvasGroup *outline_group; + SPCanvasGroup *dragpoint_group; +}; + +/** + * Manipulator that edits a single path using nodes with handles. + * Currently only cubic bezier and linear segments are supported, but this might change + * some time in the future. + */ +class PathManipulator : public PointManipulator { +public: + typedef SPPath *ItemType; + + PathManipulator(MultiPathManipulator &mpm, SPPath *path, Geom::Matrix const &edit_trans, + guint32 outline_color, Glib::ustring lpe_key); + ~PathManipulator(); + virtual bool event(GdkEvent *); + + bool empty(); + void writeXML(); + void update(); // update display, but don't commit + void clear(); // remove all nodes from manipulator + SPPath *item() { return _path; } + + void selectSubpaths(); + void shiftSelection(int dir); + void invertSelectionInSubpaths(); + + void insertNodes(); + void weldNodes(NodeList::iterator preserve_pos = NodeList::iterator()); + void weldSegments(); + void breakNodes(); + void deleteNodes(bool keep_shape = true); + void deleteSegments(); + void reverseSubpaths(bool selected_only); + void setSegmentType(SegmentType); + + void scaleHandle(Node *n, int which, int dir, bool pixel); + void rotateHandle(Node *n, int which, int dir, bool pixel); + + void showOutline(bool show); + void showHandles(bool show); + void showPathDirection(bool show); + void setLiveOutline(bool set); + void setLiveObjects(bool set); + void setControlsTransform(Geom::Matrix const &); + void hideDragPoint(); + MultiPathManipulator &mpm() { return _multi_path_manipulator; } + + NodeList::iterator subdivideSegment(NodeList::iterator after, double t); + NodeList::iterator extremeNode(NodeList::iterator origin, bool search_selected, + bool search_unselected, bool closest); + + static bool is_item_type(void *item); +private: + typedef NodeList Subpath; + typedef boost::shared_ptr<NodeList> SubpathPtr; + + void _createControlPointsFromGeometry(); + void _createGeometryFromControlPoints(); + unsigned _deleteStretch(NodeList::iterator first, NodeList::iterator last, bool keep_shape); + std::string _createTypeString(); + void _updateOutline(); + //void _setOutline(Geom::PathVector const &); + void _getGeometry(); + void _setGeometry(); + Glib::ustring _nodetypesKey(); + Inkscape::XML::Node *_getXMLNode(); + + void _selectionChanged(SelectableControlPoint *p, bool selected); + bool _nodeClicked(Node *, GdkEventButton *); + void _handleGrabbed(); + bool _handleClicked(Handle *, GdkEventButton *); + void _handleUngrabbed(); + + void _externalChange(unsigned type); + void _removeNodesFromSelection(); + void _commit(Glib::ustring const &annotation); + void _commit(Glib::ustring const &annotation, gchar const *key); + void _updateDragPoint(Geom::Point const &); + void _updateOutlineOnZoomChange(); + double _getStrokeTolerance(); + Handle *_chooseHandle(Node *n, int which); + + SubpathList _subpaths; + MultiPathManipulator &_multi_path_manipulator; + SPPath *_path; + SPCurve *_spcurve; // in item coordinates + SPCanvasItem *_outline; + CurveDragPoint *_dragpoint; // an invisible control point hoverng over curve + PathManipulatorObserver *_observer; + Geom::Matrix _d2i_transform; ///< desktop-to-item transform + Geom::Matrix _i2d_transform; ///< item-to-desktop transform, inverse of _d2i_transform + Geom::Matrix _edit_transform; ///< additional transform to apply to editing controls + unsigned _num_selected; ///< number of selected nodes + bool _show_handles; + bool _show_outline; + bool _show_path_direction; + bool _live_outline; + bool _live_objects; + Glib::ustring _lpe_key; + + friend class PathManipulatorObserver; + friend class CurveDragPoint; + friend class Node; + friend class Handle; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/selectable-control-point.cpp b/src/ui/tool/selectable-control-point.cpp new file mode 100644 index 000000000..76028dd82 --- /dev/null +++ b/src/ui/tool/selectable-control-point.cpp @@ -0,0 +1,139 @@ +/** @file + * Desktop-bound selectable control object - implementation + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "ui/tool/control-point-selection.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/selectable-control-point.h" + +namespace Inkscape { +namespace UI { + +static SelectableControlPoint::ColorSet default_scp_color_set = { + { + {0xffffff00, 0x01000000}, // normal fill, stroke + {0xff0000ff, 0x01000000}, // mouseover fill, stroke + {0x0000ffff, 0x01000000} // clicked fill, stroke + }, + {0x0000ffff, 0x000000ff}, // normal fill, stroke when selected + {0xff000000, 0x000000ff}, // mouseover fill, stroke when selected + {0xff000000, 0x000000ff} // clicked fill, stroke when selected +}; + +SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, SPCtrlShapeType shape, unsigned int size, + ControlPointSelection &sel, ColorSet *cset, SPCanvasGroup *group) + : ControlPoint (d, initial_pos, anchor, shape, size, + cset ? reinterpret_cast<ControlPoint::ColorSet*>(cset) + : reinterpret_cast<ControlPoint::ColorSet*>(&default_scp_color_set), group) + , _selection (sel) +{ + _selection.allPoints().insert(this); +} +SelectableControlPoint::SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pixbuf, + ControlPointSelection &sel, ColorSet *cset, SPCanvasGroup *group) + : ControlPoint (d, initial_pos, anchor, pixbuf, + cset ? reinterpret_cast<ControlPoint::ColorSet*>(cset) + : reinterpret_cast<ControlPoint::ColorSet*>(&default_scp_color_set), group) + , _selection (sel) +{ + _selection.allPoints().insert(this); +} + +SelectableControlPoint::~SelectableControlPoint() +{ + _selection.erase(this); + _selection.allPoints().erase(this); +} + +bool SelectableControlPoint::grabbed(GdkEventMotion *) +{ + // if a point is dragged while not selected, it should select itself + if (!selected()) { + _takeSelection(); + } + _selection._pointGrabbed(this); + return false; +} + +void SelectableControlPoint::dragged(Geom::Point &new_pos, GdkEventMotion *event) +{ + _selection._pointDragged(new_pos, event); +} + +void SelectableControlPoint::ungrabbed(GdkEventButton *) +{ + _selection._pointUngrabbed(); +} + +bool SelectableControlPoint::clicked(GdkEventButton *event) +{ + if (_selection._pointClicked(this, event)) + return true; + + if (event->button != 1) return false; + if (held_shift(*event)) { + if (selected()) { + _selection.erase(this); + } else { + _selection.insert(this); + } + } else { + _takeSelection(); + } + return true; +} + +void SelectableControlPoint::_takeSelection() +{ + _selection.clear(); + _selection.insert(this); +} + +bool SelectableControlPoint::selected() const +{ + SelectableControlPoint *p = const_cast<SelectableControlPoint*>(this); + return _selection.find(p) != _selection.end(); +} + +void SelectableControlPoint::_setState(State state) +{ + if (!selected()) { + ControlPoint::_setState(state); + return; + } + + ColorSet *cset = reinterpret_cast<ColorSet*>(_cset); + ColorEntry current = {0, 0}; + switch (state) { + case STATE_NORMAL: + current = cset->selected_normal; break; + case STATE_MOUSEOVER: + current = cset->selected_mouseover; break; + case STATE_CLICKED: + current = cset->selected_clicked; break; + } + _setColors(current); + _state = state; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/selectable-control-point.h b/src/ui/tool/selectable-control-point.h new file mode 100644 index 000000000..2fde16ea9 --- /dev/null +++ b/src/ui/tool/selectable-control-point.h @@ -0,0 +1,72 @@ +/** @file + * Desktop-bound selectable control object + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_SELECTABLE_CONTROL_POINT_H +#define SEEN_UI_TOOL_SELECTABLE_CONTROL_POINT_H + +#include <boost/enable_shared_from_this.hpp> +#include "ui/tool/control-point.h" + +namespace Inkscape { +namespace UI { + +class ControlPointSelection; + +class SelectableControlPoint : public ControlPoint { +public: + struct ColorSet { + ControlPoint::ColorSet cpset; + ColorEntry selected_normal; + ColorEntry selected_mouseover; + ColorEntry selected_clicked; + }; + + ~SelectableControlPoint(); + bool selected() const; + void updateState() const { const_cast<SelectableControlPoint*>(this)->_setState(_state); } + virtual Geom::Rect bounds() { + return Geom::Rect(position(), position()); + } +protected: + SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, SPCtrlShapeType shape, + unsigned int size, ControlPointSelection &sel, ColorSet *cset = 0, + SPCanvasGroup *group = 0); + SelectableControlPoint(SPDesktop *d, Geom::Point const &initial_pos, + Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pixbuf, + ControlPointSelection &sel, ColorSet *cset = 0, SPCanvasGroup *group = 0); + + virtual void _setState(State state); + + virtual void dragged(Geom::Point &, GdkEventMotion *); + virtual bool grabbed(GdkEventMotion *); + virtual void ungrabbed(GdkEventButton *); + virtual bool clicked(GdkEventButton *); + + ControlPointSelection &_selection; +private: + void _takeSelection(); +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/selector.cpp b/src/ui/tool/selector.cpp new file mode 100644 index 000000000..d766d5be3 --- /dev/null +++ b/src/ui/tool/selector.cpp @@ -0,0 +1,133 @@ +/** @file + * Selector component (click and rubberband) + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "desktop.h" +#include "desktop-handles.h" +#include "display/sodipodi-ctrlrect.h" +#include "event-context.h" +#include "preferences.h" +#include "ui/tool/control-point.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/selector.h" + +namespace Inkscape { +namespace UI { + +/** A hidden control point used for rubberbanding and selection. + * It uses a clever hack: the canvas item is hidden and only receives events when they + * are passed to it using Selector's event() function. When left mouse button + * is pressed, it grabs events and handles drags and clicks in the usual way. */ +class SelectorPoint : public ControlPoint { +public: + SelectorPoint(SPDesktop *d, SPCanvasGroup *group, Selector *s) + : ControlPoint(d, Geom::Point(0,0), Gtk::ANCHOR_CENTER, SP_CTRL_SHAPE_SQUARE, + 1, &invisible_cset, group) + , _selector(s) + , _cancel(false) + { + setVisible(false); + _rubber = static_cast<CtrlRect*>(sp_canvas_item_new(sp_desktop_controls(_desktop), + SP_TYPE_CTRLRECT, NULL)); + sp_canvas_item_hide(_rubber); + } + ~SelectorPoint() { + gtk_object_destroy(_rubber); + } + SPDesktop *desktop() { return _desktop; } + bool event(GdkEvent *e) { + return _eventHandler(e); + } + +protected: + virtual bool _eventHandler(GdkEvent *event) { + if (event->type == GDK_KEY_PRESS && shortcut_key(event->key) == GDK_Escape && + sp_canvas_item_is_visible(_rubber)) + { + _cancel = true; + sp_canvas_item_hide(_rubber); + return true; + } + return ControlPoint::_eventHandler(event); + } + +private: + virtual bool grabbed(GdkEventMotion *) { + _cancel = false; + _start = position(); + sp_canvas_item_show(_rubber); + return false; + } + virtual void dragged(Geom::Point &new_pos, GdkEventMotion *) { + if (_cancel) return; + Geom::Rect sel(_start, new_pos); + _rubber->setRectangle(sel); + } + virtual void ungrabbed(GdkEventButton *event) { + if (_cancel) return; + sp_canvas_item_hide(_rubber); + Geom::Rect sel(_start, position()); + _selector->signal_area.emit(sel, event); + } + virtual bool clicked(GdkEventButton *event) { + if (event->button != 1) return false; + _selector->signal_point.emit(position(), event); + return true; + } + CtrlRect *_rubber; + Selector *_selector; + Geom::Point _start; + bool _cancel; +}; + + +Selector::Selector(SPDesktop *d) + : Manipulator(d) + , _dragger(new SelectorPoint(d, sp_desktop_controls(d), this)) +{ + _dragger->setVisible(false); +} + +Selector::~Selector() +{ + delete _dragger; +} + +bool Selector::event(GdkEvent *event) +{ + // The hidden control point will capture all events after it obtains the grab, + // but it relies on this function to initiate it. If we pass only first button + // press events here, it won't interfere with any other event handling. + switch (event->type) { + case GDK_BUTTON_PRESS: + // Do not pass button presses other than left button to the control point. + // This way middle click and right click can be handled in SPEventContext. + if (event->button.button == 1) { + _dragger->setPosition(_desktop->w2d(event_point(event->motion))); + return _dragger->event(event); + } + break; + default: break; + } + return false; +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/selector.h b/src/ui/tool/selector.h new file mode 100644 index 000000000..f7c00ea71 --- /dev/null +++ b/src/ui/tool/selector.h @@ -0,0 +1,59 @@ +/** @file + * Selector component (click and rubberband) + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_SELECTOR_H +#define SEEN_UI_TOOL_SELECTOR_H + +#include <memory> +#include <gdk/gdk.h> +#include <2geom/rect.h> +#include "display/display-forward.h" +#include "ui/tool/manipulator.h" + +class SPDesktop; +class CtrlRect; + +namespace Inkscape { +namespace UI { + +class SelectorPoint; + +class Selector : public Manipulator { +public: + Selector(SPDesktop *d); + virtual ~Selector(); + virtual bool event(GdkEvent *); + + sigc::signal<void, Geom::Rect const &, GdkEventButton*> signal_area; + sigc::signal<void, Geom::Point const &, GdkEventButton*> signal_point; +private: + SelectorPoint *_dragger; + Geom::Point _start; + CtrlRect *_rubber; + gulong _connection; + bool _cancel; + friend class SelectorPoint; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/shape-record.h b/src/ui/tool/shape-record.h new file mode 100644 index 000000000..edfad1401 --- /dev/null +++ b/src/ui/tool/shape-record.h @@ -0,0 +1,61 @@ +/** @file + * Structures that store data needed for shape editing which are not contained + * directly in the XML node + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_SHAPE_RECORD_H +#define SEEN_UI_TOOL_SHAPE_RECORD_H + +#include <glibmm/ustring.h> +#include <boost/operators.hpp> +#include <2geom/matrix.h> + +class SPItem; +namespace Inkscape { +namespace UI { + +/** Role of the shape in the drawing - affects outline display and color */ +enum ShapeRole { + SHAPE_ROLE_NORMAL, + SHAPE_ROLE_CLIPPING_PATH, + SHAPE_ROLE_MASK, + SHAPE_ROLE_LPE_PARAM // implies edit_original set to true in ShapeRecord +}; + +struct ShapeRecord : + public boost::totally_ordered<ShapeRecord> +{ + SPItem *item; // SP node for the edited shape + Geom::Matrix edit_transform; // how to transform controls - used for clipping paths and masks + ShapeRole role; + Glib::ustring lpe_key; // name of LPE shape param being edited + + inline bool operator==(ShapeRecord const &o) const { + return item == o.item && lpe_key == o.lpe_key; + } + inline bool operator<(ShapeRecord const &o) const { + return item == o.item ? (lpe_key < o.lpe_key) : (item < o.item); + } +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/transform-handle-set.cpp b/src/ui/tool/transform-handle-set.cpp new file mode 100644 index 000000000..1af848b96 --- /dev/null +++ b/src/ui/tool/transform-handle-set.cpp @@ -0,0 +1,657 @@ +/** @file + * Affine transform handles component + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <math.h> +#include <algorithm> +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdk.h> +#include <2geom/transforms.h> +#include "desktop.h" +#include "desktop-handles.h" +#include "display/sodipodi-ctrlrect.h" +#include "preferences.h" +#include "ui/tool/commit-events.h" +#include "ui/tool/control-point.h" +#include "ui/tool/event-utils.h" +#include "ui/tool/transform-handle-set.h" + +// FIXME BRAIN DAMAGE WARNING: this is a global variable in select-context.cpp +// It should be moved to a header +extern GdkPixbuf *handles[]; +GType sp_select_context_get_type(); + +namespace Inkscape { +namespace UI { + +namespace { +Gtk::AnchorType corner_to_anchor(unsigned c) { + switch (c % 4) { + case 0: return Gtk::ANCHOR_NE; + case 1: return Gtk::ANCHOR_NW; + case 2: return Gtk::ANCHOR_SW; + default: return Gtk::ANCHOR_SE; + } +} +Gtk::AnchorType side_to_anchor(unsigned s) { + switch (s % 4) { + case 0: return Gtk::ANCHOR_N; + case 1: return Gtk::ANCHOR_W; + case 2: return Gtk::ANCHOR_S; + default: return Gtk::ANCHOR_E; + } +} + +// TODO move those two functions into a common place +double snap_angle(double a) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + double unit_angle = M_PI / snaps; + return CLAMP(unit_angle * round(a / unit_angle), -M_PI, M_PI); +} +double snap_increment_degrees() { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int snaps = prefs->getIntLimited("/options/rotationsnapsperpi/value", 12, 1, 1000); + return 180.0 / snaps; +} + +ControlPoint::ColorSet thandle_cset = { + {0x000000ff, 0x000000ff}, + {0x00ff6600, 0x000000ff}, + {0x00ff6600, 0x000000ff} +}; + +ControlPoint::ColorSet center_cset = { + {0x00000000, 0x000000ff}, + {0x00000000, 0xff0000b0}, + {0x00000000, 0xff0000b0} +}; +} // anonymous namespace + +/** Base class for node transform handles to simplify implementation */ +class TransformHandle : public ControlPoint { +public: + TransformHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb) + : ControlPoint(th._desktop, Geom::Point(), anchor, pb, &thandle_cset, + th._transform_handle_group) + , _th(th) + { + setVisible(false); + } +protected: + virtual void startTransform() {} + virtual void endTransform() {} + virtual Geom::Matrix computeTransform(Geom::Point const &pos, GdkEventMotion *event) = 0; + virtual CommitEvent getCommitEvent() = 0; + + Geom::Matrix _last_transform; + Geom::Point _origin; + TransformHandleSet &_th; +private: + virtual bool grabbed(GdkEventMotion *) { + _origin = position(); + _last_transform.setIdentity(); + startTransform(); + + _th._setActiveHandle(this); + _cset = &invisible_cset; + _setState(_state); + return false; + } + virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event) + { + Geom::Matrix t = computeTransform(new_pos, event); + // protect against degeneracies + if (t.isSingular()) return; + Geom::Matrix incr = _last_transform.inverse() * t; + if (incr.isSingular()) return; + _th.signal_transform.emit(incr); + _last_transform = t; + } + virtual void ungrabbed(GdkEventButton *) { + _th._clearActiveHandle(); + _cset = &thandle_cset; + _setState(_state); + endTransform(); + _th.signal_commit.emit(getCommitEvent()); + } +}; + +class ScaleHandle : public TransformHandle { +public: + ScaleHandle(TransformHandleSet &th, Gtk::AnchorType anchor, Glib::RefPtr<Gdk::Pixbuf> pb) + : TransformHandle(th, anchor, pb) + {} +protected: + virtual Glib::ustring _getTip(unsigned state) { + if (state_held_control(state)) { + if (state_held_shift(state)) { + return C_("Transform handle tip", + "<b>Shift+Ctrl:</b> scale uniformly about the rotation center"); + } + return C_("Transform handle tip", "<b>Ctrl:</b> scale uniformly"); + } + if (state_held_shift(state)) { + if (state_held_alt(state)) { + return C_("Transform handle tip", + "<b>Shift+Alt:</b> scale using an integer ratio about the rotation center"); + } + return C_("Transform handle tip", "<b>Shift:</b> scale from the rotation center"); + } + if (state_held_alt(state)) { + return C_("Transform handle tip", "<b>Alt:</b> scale using an integer ratio"); + } + return C_("Transform handle tip", "<b>Scale handle:</b> drag to scale the selection"); + } + + virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { + return format_tip(C_("Transform handle tip", + "Scale by %.2f%% x %.2f%%"), _last_scale_x * 100, _last_scale_y * 100); + } + + virtual bool _hasDragTips() { return true; } + + static double _last_scale_x, _last_scale_y; +}; +double ScaleHandle::_last_scale_x = 1.0; +double ScaleHandle::_last_scale_y = 1.0; + +/// Corner scaling handle for node transforms +class ScaleCornerHandle : public ScaleHandle { +public: + ScaleCornerHandle(TransformHandleSet &th, unsigned corner) + : ScaleHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner)) + , _corner(corner) + {} +protected: + virtual void startTransform() { + _sc_center = _th.rotationCenter(); + _sc_opposite = _th.bounds().corner(_corner + 2); + _last_scale_x = _last_scale_y = 1.0; + } + virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { + Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite; + Geom::Point vold = _origin - scc, vnew = new_pos - scc; + // avoid exploding the selection + if (Geom::are_near(vold[Geom::X], 0) || Geom::are_near(vold[Geom::Y], 0)) + return Geom::identity(); + + double scale[2] = { vnew[Geom::X] / vold[Geom::X], vnew[Geom::Y] / vold[Geom::Y] }; + if (held_alt(*event)) { + for (unsigned i = 0; i < 2; ++i) { + if (scale[i] >= 1.0) scale[i] = round(scale[i]); + else scale[i] = 1.0 / round(1.0 / scale[i]); + } + } else if (held_control(*event)) { + scale[0] = scale[1] = std::min(scale[0], scale[1]); + } + _last_scale_x = scale[0]; + _last_scale_y = scale[1]; + Geom::Matrix t = Geom::Translate(-scc) + * Geom::Scale(scale[0], scale[1]) + * Geom::Translate(scc); + return t; + } + virtual CommitEvent getCommitEvent() { + return _last_transform.isUniformScale() + ? COMMIT_MOUSE_SCALE_UNIFORM + : COMMIT_MOUSE_SCALE; + } +private: + static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) { + sp_select_context_get_type(); + switch (c % 2) { + case 0: return Glib::wrap(handles[1], true); + default: return Glib::wrap(handles[0], true); + } + } + Geom::Point _sc_center; + Geom::Point _sc_opposite; + unsigned _corner; +}; + +/// Side scaling handle for node transforms +class ScaleSideHandle : public ScaleHandle { +public: + ScaleSideHandle(TransformHandleSet &th, unsigned side) + : ScaleHandle(th, side_to_anchor(side), _side_to_pixbuf(side)) + , _side(side) + {} +protected: + virtual void startTransform() { + _sc_center = _th.rotationCenter(); + Geom::Rect b = _th.bounds(); + _sc_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3)); + _last_scale_x = _last_scale_y = 1.0; + } + virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) { + Geom::Point scc = held_shift(*event) ? _sc_center : _sc_opposite; + Geom::Point vs; + Geom::Dim2 d1 = static_cast<Geom::Dim2>((_side + 1) % 2); + Geom::Dim2 d2 = static_cast<Geom::Dim2>(_side % 2); + + // avoid exploding the selection + if (Geom::are_near(scc[d1], _origin[d1])) + return Geom::identity(); + + vs[d1] = (new_pos - scc)[d1] / (_origin - scc)[d1]; + if (held_alt(*event)) { + if (vs[d1] >= 1.0) vs[d1] = round(vs[d1]); + else vs[d1] = 1.0 / round(1.0 / vs[d1]); + } + vs[d2] = held_control(*event) ? vs[d1] : 1.0; + + _last_scale_x = vs[Geom::X]; + _last_scale_y = vs[Geom::Y]; + Geom::Matrix t = Geom::Translate(-scc) + * Geom::Scale(vs) + * Geom::Translate(scc); + return t; + } + virtual CommitEvent getCommitEvent() { + return _last_transform.isUniformScale() + ? COMMIT_MOUSE_SCALE_UNIFORM + : COMMIT_MOUSE_SCALE; + } +private: + static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned c) { + sp_select_context_get_type(); + switch (c % 2) { + case 0: return Glib::wrap(handles[3], true); + default: return Glib::wrap(handles[2], true); + } + } + Geom::Point _sc_center; + Geom::Point _sc_opposite; + unsigned _side; +}; + +/// Rotation handle for node transforms +class RotateHandle : public TransformHandle { +public: + RotateHandle(TransformHandleSet &th, unsigned corner) + : TransformHandle(th, corner_to_anchor(corner), _corner_to_pixbuf(corner)) + , _corner(corner) + {} +protected: + + virtual void startTransform() { + _rot_center = _th.rotationCenter(); + _rot_opposite = _th.bounds().corner(_corner + 2); + _last_angle = 0; + } + + virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) + { + Geom::Point rotc = held_shift(*event) ? _rot_opposite : _rot_center; + double angle = Geom::angle_between(_origin - rotc, new_pos - rotc); + if (held_control(*event)) { + angle = snap_angle(angle); + } + _last_angle = angle; + Geom::Matrix t = Geom::Translate(-rotc) + * Geom::Rotate(angle) + * Geom::Translate(rotc); + return t; + } + + virtual CommitEvent getCommitEvent() { return COMMIT_MOUSE_ROTATE; } + + virtual Glib::ustring _getTip(unsigned state) { + if (state_held_shift(state)) { + if (state_held_control(state)) { + return format_tip(C_("Transform handle tip", + "<b>Shift+Ctrl:</b> rotate around the opposite corner and snap " + "angle to %f° increments"), snap_increment_degrees()); + } + return C_("Transform handle tip", "<b>Shift:</b> rotate around the opposite corner"); + } + if (state_held_control(state)) { + return format_tip(C_("Transform handle tip", + "<b>Ctrl:</b> snap angle to %f° increments"), snap_increment_degrees()); + } + return C_("Transform handle tip", "<b>Rotation handle:</b> drag to rotate " + "the selection around the rotation center"); + } + + virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { + return format_tip(C_("Transform handle tip", "Rotate by %.2f°"), + _last_angle * 360.0); + } + + virtual bool _hasDragTips() { return true; } + +private: + static Glib::RefPtr<Gdk::Pixbuf> _corner_to_pixbuf(unsigned c) { + sp_select_context_get_type(); + switch (c % 4) { + case 0: return Glib::wrap(handles[10], true); + case 1: return Glib::wrap(handles[8], true); + case 2: return Glib::wrap(handles[6], true); + default: return Glib::wrap(handles[4], true); + } + } + Geom::Point _rot_center; + Geom::Point _rot_opposite; + unsigned _corner; + static double _last_angle; +}; +double RotateHandle::_last_angle = 0; + +class SkewHandle : public TransformHandle { +public: + SkewHandle(TransformHandleSet &th, unsigned side) + : TransformHandle(th, side_to_anchor(side), _side_to_pixbuf(side)) + , _side(side) + {} + +protected: + + virtual void startTransform() { + _skew_center = _th.rotationCenter(); + Geom::Rect b = _th.bounds(); + _skew_opposite = Geom::middle_point(b.corner(_side + 2), b.corner(_side + 3)); + _last_angle = 0; + _last_horizontal = _side % 2; + } + + virtual Geom::Matrix computeTransform(Geom::Point const &new_pos, GdkEventMotion *event) + { + Geom::Point scc = held_shift(*event) ? _skew_center : _skew_opposite; + // d1 and d2 are reversed with respect to ScaleSideHandle + Geom::Dim2 d1 = static_cast<Geom::Dim2>(_side % 2); + Geom::Dim2 d2 = static_cast<Geom::Dim2>((_side + 1) % 2); + Geom::Point proj, scale(1.0, 1.0); + + // Skew handles allow scaling up to integer multiples of the original size + // in the second direction; prevent explosions + // TODO should the scaling part be only active with Alt? + if (!Geom::are_near(_origin[d2], scc[d2])) { + scale[d2] = (new_pos - scc)[d2] / (_origin - scc)[d2]; + } + + if (scale[d2] < 1.0) { + scale[d2] = copysign(1.0, scale[d2]); + } else { + scale[d2] = floor(scale[d2]); + } + + // Calculate skew angle. The angle is calculated with regards to the point obtained + // by projecting the handle position on the relevant side of the bounding box. + // This avoids degeneracies when moving the skew angle over the rotation center + proj[d1] = new_pos[d1]; + proj[d2] = scc[d2] + (_origin[d2] - scc[d2]) * scale[d2]; + double angle = 0; + if (!Geom::are_near(proj[d2], scc[d2])) + angle = Geom::angle_between(_origin - scc, proj - scc); + if (held_control(*event)) angle = snap_angle(angle); + + // skew matrix has the from [[1, k],[0, 1]] for horizontal skew + // and [[1,0],[k,1]] for vertical skew. + Geom::Matrix skew = Geom::identity(); + // correct the sign of the tangent + skew[d2 + 1] = (d1 == Geom::X ? -1.0 : 1.0) * tan(angle); + + _last_angle = angle; + Geom::Matrix t = Geom::Translate(-scc) + * Geom::Scale(scale) * skew + * Geom::Translate(scc); + return t; + } + + virtual CommitEvent getCommitEvent() { + return _side % 2 + ? COMMIT_MOUSE_SKEW_Y + : COMMIT_MOUSE_SKEW_X; + } + + virtual Glib::ustring _getTip(unsigned state) { + if (state_held_shift(state)) { + if (state_held_control(state)) { + return format_tip(C_("Transform handle tip", + "<b>Shift+Ctrl:</b> skew about the rotation center with snapping " + "to %f° increments"), snap_increment_degrees()); + } + return C_("Transform handle tip", "<b>Shift:</b> skew about the rotation center"); + } + if (state_held_control(state)) { + return format_tip(C_("Transform handle tip", + "<b>Ctrl:</b> snap skew angle to %f° increments"), snap_increment_degrees()); + } + return C_("Transform handle tip", + "<b>Skew handle:</b> drag to skew (shear) selection about " + "the opposite handle"); + } + + virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { + if (_last_horizontal) { + return format_tip(C_("Transform handle tip", "Skew horizontally by %.2f°"), + _last_angle * 360.0); + } else { + return format_tip(C_("Transform handle tip", "Skew vertically by %.2f°"), + _last_angle * 360.0); + } + } + + virtual bool _hasDragTips() { return true; } + +private: + + static Glib::RefPtr<Gdk::Pixbuf> _side_to_pixbuf(unsigned s) { + sp_select_context_get_type(); + switch (s % 4) { + case 0: return Glib::wrap(handles[9], true); + case 1: return Glib::wrap(handles[7], true); + case 2: return Glib::wrap(handles[5], true); + default: return Glib::wrap(handles[11], true); + } + } + Geom::Point _skew_center; + Geom::Point _skew_opposite; + unsigned _side; + static bool _last_horizontal; + static double _last_angle; +}; +bool SkewHandle::_last_horizontal = false; +double SkewHandle::_last_angle = 0; + +class RotationCenter : public ControlPoint { +public: + RotationCenter(TransformHandleSet &th) + : ControlPoint(th._desktop, Geom::Point(), Gtk::ANCHOR_CENTER, _get_pixbuf(), + ¢er_cset, th._transform_handle_group) + , _th(th) + { + setVisible(false); + } + +protected: + + virtual Glib::ustring _getTip(unsigned /*state*/) { + return C_("Transform handle tip", + "<b>Rotation center:</b> drag to change the origin of transforms"); + } + +private: + + static Glib::RefPtr<Gdk::Pixbuf> _get_pixbuf() { + sp_select_context_get_type(); + return Glib::wrap(handles[12], true); + } + + TransformHandleSet &_th; +}; + +TransformHandleSet::TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group) + : Manipulator(d) + , _active(0) + , _transform_handle_group(th_group) + , _mode(MODE_SCALE) + , _in_transform(false) + , _visible(true) +{ + _trans_outline = static_cast<CtrlRect*>(sp_canvas_item_new(sp_desktop_controls(_desktop), + SP_TYPE_CTRLRECT, NULL)); + sp_canvas_item_hide(_trans_outline); + _trans_outline->setDashed(true); + + for (unsigned i = 0; i < 4; ++i) { + _scale_corners[i] = new ScaleCornerHandle(*this, i); + _scale_sides[i] = new ScaleSideHandle(*this, i); + _rot_corners[i] = new RotateHandle(*this, i); + _skew_sides[i] = new SkewHandle(*this, i); + } + _center = new RotationCenter(*this); + // when transforming, update rotation center position + signal_transform.connect(sigc::mem_fun(*_center, &RotationCenter::transform)); +} + +TransformHandleSet::~TransformHandleSet() +{ + for (unsigned i = 0; i < 17; ++i) { + delete _handles[i]; + } +} + +/** Sets the mode of transform handles (scale or rotate). */ +void TransformHandleSet::setMode(Mode m) +{ + _mode = m; + _updateVisibility(_visible); +} + +Geom::Rect TransformHandleSet::bounds() +{ + return Geom::Rect(*_scale_corners[0], *_scale_corners[2]); +} + +ControlPoint &TransformHandleSet::rotationCenter() +{ + return *_center; +} + +void TransformHandleSet::setVisible(bool v) +{ + if (_visible != v) { + _visible = v; + _updateVisibility(_visible); + } +} + +void TransformHandleSet::setBounds(Geom::Rect const &r, bool preserve_center) +{ + if (_in_transform) { + _trans_outline->setRectangle(r); + } else { + for (unsigned i = 0; i < 4; ++i) { + _scale_corners[i]->move(r.corner(i)); + _scale_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1))); + _rot_corners[i]->move(r.corner(i)); + _skew_sides[i]->move(Geom::middle_point(r.corner(i), r.corner(i+1))); + } + if (!preserve_center) _center->move(r.midpoint()); + if (_visible) _updateVisibility(true); + } +} + +bool TransformHandleSet::event(GdkEvent*) +{ + return false; +} + +void TransformHandleSet::_emitTransform(Geom::Matrix const &t) +{ + signal_transform.emit(t); + _center->transform(t); +} + +void TransformHandleSet::_setActiveHandle(ControlPoint *th) +{ + _active = th; + if (_in_transform) + throw std::logic_error("Transform initiated when another transform in progress"); + _in_transform = true; + // hide all handles except the active one + _updateVisibility(false); + sp_canvas_item_show(_trans_outline); +} + +void TransformHandleSet::_clearActiveHandle() +{ + // This can only be called from handles, so they had to be visible before _setActiveHandle + sp_canvas_item_hide(_trans_outline); + _active = 0; + _in_transform = false; + _updateVisibility(_visible); +} + +/** Update the visibility of transformation handles according to settings and the dimensions + * of the bounding box. It hides the handles that would have no effect or lead to + * discontinuities. Additionally, side handles for which there is no space are not shown. */ +void TransformHandleSet::_updateVisibility(bool v) +{ + if (v) { + Geom::Rect b = bounds(); + Geom::Point handle_size( + gdk_pixbuf_get_width(handles[0]) / _desktop->current_zoom(), + gdk_pixbuf_get_height(handles[0]) / _desktop->current_zoom()); + Geom::Point bp = b.dimensions(); + + // do not scale when the bounding rectangle has zero width or height + bool show_scale = (_mode == MODE_SCALE) && !Geom::are_near(b.minExtent(), 0); + // do not rotate if the bounding rectangle is degenerate + bool show_rotate = (_mode == MODE_ROTATE_SKEW) && !Geom::are_near(b.maxExtent(), 0); + bool show_scale_side[2], show_skew[2]; + + // show sides if: + // a) there is enough space between corner handles, or + // b) corner handles are not shown, but side handles make sense + // this affects horizontal and vertical scale handles; skew handles never + // make sense if rotate handles are not shown + for (unsigned i = 0; i < 2; ++i) { + Geom::Dim2 d = static_cast<Geom::Dim2>(i); + Geom::Dim2 otherd = static_cast<Geom::Dim2>((i+1)%2); + show_scale_side[i] = (_mode == MODE_SCALE); + show_scale_side[i] &= (show_scale ? bp[d] >= handle_size[d] + : !Geom::are_near(bp[otherd], 0)); + show_skew[i] = (show_rotate && bp[d] >= handle_size[d] + && !Geom::are_near(bp[otherd], 0)); + } + for (unsigned i = 0; i < 4; ++i) { + _scale_corners[i]->setVisible(show_scale); + _rot_corners[i]->setVisible(show_rotate); + _scale_sides[i]->setVisible(show_scale_side[i%2]); + _skew_sides[i]->setVisible(show_skew[i%2]); + } + // show rotation center if there is enough space (?) + _center->setVisible(show_rotate /*&& bp[Geom::X] > handle_size[Geom::X] + && bp[Geom::Y] > handle_size[Geom::Y]*/); + } else { + for (unsigned i = 0; i < 17; ++i) { + if (_handles[i] != _active) + _handles[i]->setVisible(false); + } + } + +} + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/tool/transform-handle-set.h b/src/ui/tool/transform-handle-set.h new file mode 100644 index 000000000..48ad3af51 --- /dev/null +++ b/src/ui/tool/transform-handle-set.h @@ -0,0 +1,96 @@ +/** @file + * Affine transform handles component + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UI_TOOL_TRANSFORM_HANDLE_SET_H +#define SEEN_UI_TOOL_TRANSFORM_HANDLE_SET_H + +#include <memory> +#include <gdk/gdk.h> +#include <2geom/forward.h> +#include "display/display-forward.h" +#include "ui/tool/commit-events.h" +#include "ui/tool/manipulator.h" + +class SPDesktop; +class CtrlRect; // this is not present in display-forward.h! +namespace Inkscape { +namespace UI { + +//class TransformHandle; +class RotateHandle; +class SkewHandle; +class ScaleCornerHandle; +class ScaleSideHandle; +class RotationCenter; + +class TransformHandleSet : public Manipulator { +public: + enum Mode { + MODE_SCALE, + MODE_ROTATE_SKEW + }; + + TransformHandleSet(SPDesktop *d, SPCanvasGroup *th_group); + virtual ~TransformHandleSet(); + virtual bool event(GdkEvent *); + + bool visible() { return _visible; } + Mode mode() { return _mode; } + Geom::Rect bounds(); + void setVisible(bool v); + void setMode(Mode); + void setBounds(Geom::Rect const &, bool preserve_center = false); + + bool transforming() { return _in_transform; } + ControlPoint &rotationCenter(); + + sigc::signal<void, Geom::Matrix const &> signal_transform; + sigc::signal<void, CommitEvent> signal_commit; +private: + void _emitTransform(Geom::Matrix const &); + void _setActiveHandle(ControlPoint *h); + void _clearActiveHandle(); + void _updateVisibility(bool v); + union { + ControlPoint *_handles[17]; + struct { + ScaleCornerHandle *_scale_corners[4]; + ScaleSideHandle *_scale_sides[4]; + RotateHandle *_rot_corners[4]; + SkewHandle *_skew_sides[4]; + RotationCenter *_center; + }; + }; + ControlPoint *_active; + SPCanvasGroup *_transform_handle_group; + CtrlRect *_trans_outline; + Mode _mode; + bool _in_transform; + bool _visible; + bool _rot_center_visible; + friend class TransformHandle; + friend class RotationCenter; +}; + +} // namespace UI +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/ui/uxmanager.cpp b/src/ui/uxmanager.cpp new file mode 100644 index 000000000..3c6f85b91 --- /dev/null +++ b/src/ui/uxmanager.cpp @@ -0,0 +1,174 @@ +/** \file + * Desktop widget implementation + */ +/* Authors: + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <algorithm> + +#include "uxmanager.h" +#include "util/ege-tags.h" +#include "widgets/toolbox.h" +#include "widgets/desktop-widget.h" +#include "preferences.h" + +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif // GDK_WINDOWING_X11 + +using std::map; +using std::vector; + + +gchar const* KDE_WINDOW_MANAGER_NAME = "KWin"; +gchar const* UNKOWN_WINDOW_MANAGER_NAME = "unknown"; + + +static vector<SPDesktop*> desktops; +static vector<SPDesktopWidget*> dtws; +static map<SPDesktop*, vector<GtkWidget*> > trackedBoxes; + + + +namespace Inkscape { +namespace UI { + +UXManager* instance = 0; + +UXManager* UXManager::getInstance() +{ + if (!instance) { + instance = new UXManager(); + } + return instance; +} + + +UXManager::UXManager() : + floatwindowIssues(false) +{ + ege::TagSet tags; + tags.setLang("en"); + + tags.addTag(ege::Tag("General")); + tags.addTag(ege::Tag("Icons")); + +#if defined(GDK_WINDOWING_X11) + char const* wmName = gdk_x11_screen_get_window_manager_name( gdk_screen_get_default() ); + //g_message("Window manager is [%s]", wmName); + + //if (g_ascii_strcasecmp( wmName, UNKOWN_WINDOW_MANAGER_NAME ) == 0) { + if (g_ascii_strcasecmp( wmName, KDE_WINDOW_MANAGER_NAME ) == 0) { + floatwindowIssues = true; + } +#elif defined(GDK_WINDOWING_WIN32) + floatwindowIssues = true; +#endif // GDK_WINDOWING_WIN32 +} + +UXManager::~UXManager() +{ +} + + +bool UXManager::isFloatWindowProblem() const +{ + return floatwindowIssues; +} + +void UXManager::setTask(SPDesktop* dt, gint val) +{ + for (vector<SPDesktopWidget*>::iterator it = dtws.begin(); it != dtws.end(); ++it) { + SPDesktopWidget* dtw = *it; + + gboolean notDone = Inkscape::Preferences::get()->getBool("/options/workarounds/dynamicnotdone", false); + + if (dtw->desktop == dt) { + switch (val) { + default: + case 0: + dtw->setToolboxPosition("ToolToolbar", GTK_POS_LEFT); + dtw->setToolboxPosition("CommandsToolbar", GTK_POS_TOP); + if (notDone) { + dtw->setToolboxPosition("AuxToolbar", GTK_POS_TOP); + } + dtw->setToolboxPosition("SnapToolbar", GTK_POS_TOP); + break; + case 1: + dtw->setToolboxPosition("ToolToolbar", GTK_POS_TOP); + dtw->setToolboxPosition("CommandsToolbar", GTK_POS_LEFT); + if (notDone) { + dtw->setToolboxPosition("AuxToolbar", GTK_POS_TOP); + } + dtw->setToolboxPosition("SnapToolbar", GTK_POS_RIGHT); + break; + case 2: + dtw->setToolboxPosition("ToolToolbar", GTK_POS_LEFT); + dtw->setToolboxPosition("CommandsToolbar", GTK_POS_RIGHT); + dtw->setToolboxPosition("SnapToolbar", GTK_POS_RIGHT); + if (notDone) { + dtw->setToolboxPosition("AuxToolbar", GTK_POS_RIGHT); + } + } + } + } +} + + +void UXManager::addTrack( SPDesktopWidget* dtw ) +{ + if (std::find(dtws.begin(), dtws.end(), dtw) == dtws.end()) { + dtws.push_back(dtw); + } +} + +void UXManager::delTrack( SPDesktopWidget* dtw ) +{ + vector<SPDesktopWidget*>::iterator iter = std::find(dtws.begin(), dtws.end(), dtw); + if (iter != dtws.end()) { + dtws.erase(iter); + } +} + +void UXManager::connectToDesktop( vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ) +{ +//static map<SPDesktop*, vector<GtkWidget*> > trackedBoxes; + + for (vector<GtkWidget*>::const_iterator it = toolboxes.begin(); it != toolboxes.end(); ++it ) { + GtkWidget* toolbox = *it; + + ToolboxFactory::setToolboxDesktop( toolbox, desktop ); + vector<GtkWidget*>& tracked = trackedBoxes[desktop]; + if (find(tracked.begin(), tracked.end(), toolbox) == tracked.end()) { + tracked.push_back(toolbox); + } + } + + if (std::find(desktops.begin(), desktops.end(), desktop) == desktops.end()) { + desktops.push_back(desktop); + } +} + + +} // namespace UI +} // namespace Inkscape + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/uxmanager.h b/src/ui/uxmanager.h new file mode 100644 index 000000000..aecda2b5e --- /dev/null +++ b/src/ui/uxmanager.h @@ -0,0 +1,65 @@ +#ifndef SEEN_UI_UXMANAGER_H +#define SEEN_UI_UXMANAGER_H +/* + * A simple interface for previewing representations. + * + * Authors: + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <glib.h> +#include <vector> + +extern "C" +{ + typedef struct _GObject GObject; + typedef struct _GtkWidget GtkWidget; +} + +class SPDesktop; + +struct SPDesktopWidget; + + +namespace Inkscape { +namespace UI { + +class UXManager +{ +public: + static UXManager* getInstance(); + virtual ~UXManager(); + + void addTrack( SPDesktopWidget* dtw ); + void delTrack( SPDesktopWidget* dtw ); + + void connectToDesktop( std::vector<GtkWidget *> const & toolboxes, SPDesktop *desktop ); + + void setTask(SPDesktop* dt, gint val); + + bool isFloatWindowProblem() const; + +private: + UXManager(); + + bool floatwindowIssues; +}; + +} // namespace UI +} // namespace Inkscape + +#endif // SEEN_UI_UXMANAGER_H +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/ui/view/edit-widget.cpp b/src/ui/view/edit-widget.cpp index d34b18771..770a9bf87 100644 --- a/src/ui/view/edit-widget.cpp +++ b/src/ui/view/edit-widget.cpp @@ -228,12 +228,6 @@ EditWidget::onDialogAlignAndDistribute() } void -EditWidget::onDialogSprayOptionClass() -{ - _dlg_mgr.showDialog("SprayOptionClass"); -} - -void EditWidget::onDialogDocumentProperties() { // manage (Inkscape::UI::Dialog::DocumentPreferences::create()); diff --git a/src/ui/view/edit-widget.h b/src/ui/view/edit-widget.h index 452641e80..2bb708305 100644 --- a/src/ui/view/edit-widget.h +++ b/src/ui/view/edit-widget.h @@ -70,7 +70,6 @@ public: void onDialogAbout(); void onDialogAlignAndDistribute(); - void onDialogSprayOptionClass(); void onDialogInkscapePreferences(); void onDialogDialog(); void onDialogDocumentProperties(); diff --git a/src/ui/widget/color-picker.cpp b/src/ui/widget/color-picker.cpp index d6ae50ae3..34cf1d5e3 100644 --- a/src/ui/widget/color-picker.cpp +++ b/src/ui/widget/color-picker.cpp @@ -116,12 +116,15 @@ ColorPicker::on_changed (guint32) void sp_color_picker_color_mod(SPColorSelector *csel, GObject *cp) { - if (_in_use) return; - else _in_use = true; + if (_in_use) { + return; + } else { + _in_use = true; + } SPColor color; - float alpha; - csel->base->getColorAlpha(color, &alpha); + float alpha = 0; + csel->base->getColorAlpha(color, alpha); guint32 rgba = color.toRGBA32( alpha ); ColorPicker *ptr = (ColorPicker *)(cp); diff --git a/src/ui/widget/labelled.h b/src/ui/widget/labelled.h index 3685944a4..5670af0b6 100644 --- a/src/ui/widget/labelled.h +++ b/src/ui/widget/labelled.h @@ -39,6 +39,8 @@ public: Gtk::Widget const *getWidget() const; Gtk::Label const *getLabel() const; + void setLabelText(const Glib::ustring &str) { _label->set_text(str); }; + protected: Gtk::Widget *_widget; Gtk::Label *_label; diff --git a/src/ui/widget/page-sizer.cpp b/src/ui/widget/page-sizer.cpp index 68f26792a..e604a24ec 100644 --- a/src/ui/widget/page-sizer.cpp +++ b/src/ui/widget/page-sizer.cpp @@ -229,6 +229,11 @@ PageSizer::PageSizer(Registry & _wr) _dimensionUnits( _("U_nits:"), "units", _wr ), _dimensionWidth( _("_Width:"), _("Width of paper"), "width", _dimensionUnits, _wr ), _dimensionHeight( _("_Height:"), _("Height of paper"), "height", _dimensionUnits, _wr ), + _marginTop( _("T_op margin:"), _("Top margin"), "fit-margin-top", _wr ), + _marginLeft( _("L_eft:"), _("Left margin"), "fit-margin-left", _wr), + _marginRight( _("Ri_ght:"), _("Right margin"), "fit-margin-right", _wr), + _marginBottom( _("Botto_m:"), _("Bottom margin"), "fit-margin-bottom", _wr), + _widgetRegistry(&_wr) { //# Set up the Paper Size combo box @@ -273,16 +278,11 @@ PageSizer::PageSizer(Registry & _wr) // _paperSizeListSelection->select(iter); - pack_start (_paperSizeListBox, true, true, 0); - _paperSizeListLabel.set_label(_("P_age size:")); - _paperSizeListLabel.set_use_underline(); - _paperSizeListBox.pack_start (_paperSizeListLabel, false, false, 0); - _paperSizeListLabel.set_mnemonic_widget (_paperSizeList); - _paperSizeListBox.pack_start (_paperSizeListScroller, true, true, 0); + pack_start (_paperSizeListScroller, true, true, 0); //## Set up orientation radio buttons pack_start (_orientationBox, false, false, 0); - _orientationLabel.set_label(_("Page orientation:")); + _orientationLabel.set_label(_("Orientation:")); _orientationBox.pack_start(_orientationLabel, false, false, 0); _landscapeButton.set_use_underline(); _landscapeButton.set_label(_("_Landscape")); @@ -299,19 +299,48 @@ PageSizer::PageSizer(Registry & _wr) //## Set up custom size frame _customFrame.set_label(_("Custom size")); pack_start (_customFrame, false, false, 0); - _customTable.resize(2, 2); - _customTable.set_border_width (4); - _customTable.set_row_spacings (4); - _customTable.set_col_spacings (4); - _customTable.attach(_dimensionWidth, 0,1,0,1); - _customTable.attach(_dimensionUnits, 1,2,0,1); - _customTable.attach(_dimensionHeight, 0,1,1,2); - _customTable.attach(_fitPageButton, 1,2,1,2); - _customFrame.add(_customTable); - + _customFrame.add(_customDimTable); + + _customDimTable.resize(3, 2); + _customDimTable.set_border_width(4); + _customDimTable.set_row_spacings(4); + _customDimTable.set_col_spacings(4); + _customDimTable.attach(_dimensionWidth, 0,1, 0,1); + _customDimTable.attach(_dimensionUnits, 1,2, 0,1); + _customDimTable.attach(_dimensionHeight, 0,1, 1,2); + _customDimTable.attach(_fitPageMarginExpander, 0,2, 2,3); + + //## Set up fit page expander + _fitPageMarginExpander.set_label(_("Resi_ze page to content...")); + _fitPageMarginExpander.set_use_underline(); + _fitPageMarginExpander.add(_marginTable); + + //## Set up margin settings + _marginTable.resize(4, 2); + _marginTable.set_border_width(4); + _marginTable.set_row_spacings(4); + _marginTable.set_col_spacings(4); + _marginTable.attach(_fitPageButtonAlign, 0,2, 0,1); + _marginTable.attach(_marginTopAlign, 0,2, 1,2); + _marginTable.attach(_marginLeftAlign, 0,1, 2,3); + _marginTable.attach(_marginRightAlign, 1,2, 2,3); + _marginTable.attach(_marginBottomAlign, 0,2, 3,4); + + _marginTopAlign.set(0.5, 0.5, 0.0, 1.0); + _marginTopAlign.add(_marginTop); + _marginLeftAlign.set(0.0, 0.5, 0.0, 1.0); + _marginLeftAlign.add(_marginLeft); + _marginRightAlign.set(1.0, 0.5, 0.0, 1.0); + _marginRightAlign.add(_marginRight); + _marginBottomAlign.set(0.5, 0.5, 0.0, 1.0); + _marginBottomAlign.add(_marginBottom); + + _fitPageButtonAlign.set(0.5, 0.5, 0.0, 1.0); + _fitPageButtonAlign.add(_fitPageButton); _fitPageButton.set_use_underline(); - _fitPageButton.set_label(_("_Fit page to selection")); + _fitPageButton.set_label(_("_Resize page to drawing or selection")); _tips.set_tip(_fitPageButton, _("Resize the page to fit the current selection, or the entire drawing if there is no selection")); + } @@ -343,7 +372,7 @@ PageSizer::init () /** * Set document dimensions (if not called by Doc prop's update()) and * set the PageSizer's widgets and text entries accordingly. If - * 'chageList' is true, then adjust the paperSizeList to show the closest + * 'changeList' is true, then adjust the paperSizeList to show the closest * standard page size. * * \param w, h given in px @@ -454,7 +483,7 @@ PageSizer::find_paper_size (double w, double h) const /** - * Tell the desktop to change the page size + * Tell the desktop to fit the page size to the selection or drawing. */ void PageSizer::fire_fit_canvas_to_selection_or_drawing() diff --git a/src/ui/widget/page-sizer.h b/src/ui/widget/page-sizer.h index f970afe44..718eb95b5 100644 --- a/src/ui/widget/page-sizer.h +++ b/src/ui/widget/page-sizer.h @@ -183,24 +183,38 @@ protected: Gtk::HBox _orientationBox; Gtk::Label _orientationLabel; Gtk::RadioButton _portraitButton; - Gtk::RadioButton _landscapeButton; + Gtk::RadioButton _landscapeButton; //callbacks void on_portrait(); void on_landscape(); sigc::connection _portrait_connection; - sigc::connection _landscape_connection; + sigc::connection _landscape_connection; //### Custom size frame Gtk::Frame _customFrame; - Gtk::Table _customTable; + Gtk::Table _customDimTable; RegisteredUnitMenu _dimensionUnits; RegisteredScalarUnit _dimensionWidth; - RegisteredScalarUnit _dimensionHeight; - Gtk::Button _fitPageButton; + RegisteredScalarUnit _dimensionHeight; + + //### Fit Page options + Gtk::Expander _fitPageMarginExpander; + Gtk::Table _marginTable; + Gtk::Alignment _marginTopAlign; + Gtk::Alignment _marginLeftAlign; + Gtk::Alignment _marginRightAlign; + Gtk::Alignment _marginBottomAlign; + RegisteredScalar _marginTop; + RegisteredScalar _marginLeft; + RegisteredScalar _marginRight; + RegisteredScalar _marginBottom; + Gtk::Alignment _fitPageButtonAlign; + Gtk::Button _fitPageButton; + //callback void on_value_changed(); sigc::connection _changedw_connection; - sigc::connection _changedh_connection; + sigc::connection _changedh_connection; Registry *_widgetRegistry; diff --git a/src/ui/widget/point.cpp b/src/ui/widget/point.cpp index 508a8d961..f27cfe8c6 100644 --- a/src/ui/widget/point.cpp +++ b/src/ui/widget/point.cpp @@ -194,7 +194,7 @@ Point::setRange(double min, double max) /** Sets the value of the spin button */ void -Point::setValue(Geom::Point & p) +Point::setValue(Geom::Point const & p) { xwidget.setValue(p[0]); ywidget.setValue(p[1]); diff --git a/src/ui/widget/point.h b/src/ui/widget/point.h index 57a46de76..94477d877 100644 --- a/src/ui/widget/point.h +++ b/src/ui/widget/point.h @@ -64,7 +64,7 @@ public: void setDigits(unsigned digits); void setIncrements(double step, double page); void setRange(double min, double max); - void setValue(Geom::Point & p); + void setValue(Geom::Point const & p); void update(); diff --git a/src/ui/widget/registered-widget.cpp b/src/ui/widget/registered-widget.cpp index 95ddec286..db31d08d3 100644 --- a/src/ui/widget/registered-widget.cpp +++ b/src/ui/widget/registered-widget.cpp @@ -587,6 +587,96 @@ RegisteredTransformedPoint::on_value_changed() } /*######################################### + * Registered TRANSFORMEDPOINT + */ + +RegisteredVector::~RegisteredVector() +{ + _value_x_changed_connection.disconnect(); + _value_y_changed_connection.disconnect(); +} + +RegisteredVector::RegisteredVector ( const Glib::ustring& label, const Glib::ustring& tip, + const Glib::ustring& key, Registry& wr, Inkscape::XML::Node* repr_in, + SPDocument* doc_in ) + : RegisteredWidget<Point> (label, tip), + _polar_coords(false) +{ + init_parent(key, wr, repr_in, doc_in); + + setRange (-1e6, 1e6); + setDigits (2); + setIncrements(0.1, 1.0); + _value_x_changed_connection = signal_x_value_changed().connect (sigc::mem_fun (*this, &RegisteredVector::on_value_changed)); + _value_y_changed_connection = signal_y_value_changed().connect (sigc::mem_fun (*this, &RegisteredVector::on_value_changed)); +} + +void +RegisteredVector::setValue(Geom::Point const & p) +{ + if (!_polar_coords) { + Point::setValue(p); + } else { + Geom::Point polar; + polar[Geom::X] = atan2(p) *180/M_PI; + polar[Geom::Y] = p.length(); + Point::setValue(polar); + } +} + +void +RegisteredVector::setValue(Geom::Point const & p, Geom::Point const & origin) +{ + RegisteredVector::setValue(p); + _origin = origin; +} + +/** + * Changes the widgets text to polar coordinates. The SVG output will still be a normal carthesian vector. + * Careful: when calling getValue(), the return value's X-coord will be the angle, Y-value will be the distance/length. + * After changing the coords type (polar/non-polar), the value has to be reset (setValue). + */ +void +RegisteredVector::setPolarCoords(bool polar_coords) +{ + _polar_coords = polar_coords; + if (polar_coords) { + xwidget.setLabelText("Angle:"); + ywidget.setLabelText("Distance:"); + } else { + xwidget.setLabelText("X:"); + ywidget.setLabelText("Y:"); + } +} + +void +RegisteredVector::on_value_changed() +{ + if (setProgrammatically()) { + clearProgrammatically(); + return; + } + + if (_wr->isUpdating()) + return; + + _wr->setUpdating (true); + + Geom::Point origin = _origin; + Geom::Point vector = getValue(); + if (_polar_coords) { + vector = Geom::Point::polar(vector[Geom::X]*M_PI/180, vector[Geom::Y]); + } + + Inkscape::SVGOStringStream os; + os << origin << " , " << vector; + + write_to_xml(os.str().c_str()); + + _wr->setUpdating (false); +} + +/*######################################### * Registered RANDOM */ diff --git a/src/ui/widget/registered-widget.h b/src/ui/widget/registered-widget.h index a5c61f68a..7aefbb90e 100644 --- a/src/ui/widget/registered-widget.h +++ b/src/ui/widget/registered-widget.h @@ -334,6 +334,31 @@ protected: }; +class RegisteredVector : public RegisteredWidget<Point> { +public: + virtual ~RegisteredVector(); + RegisteredVector (const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + Registry& wr, + Inkscape::XML::Node* repr_in = NULL, + SPDocument *doc_in = NULL ); + + // redefine setValue, because transform must be applied + void setValue(Geom::Point const & p); + void setValue(Geom::Point const & p, Geom::Point const & origin); + void setPolarCoords(bool polar_coords = true); + +protected: + sigc::connection _value_x_changed_connection; + sigc::connection _value_y_changed_connection; + void on_value_changed(); + + Geom::Point _origin; + bool _polar_coords; +}; + + class RegisteredRandom : public RegisteredWidget<Random> { public: virtual ~RegisteredRandom(); diff --git a/src/ui/widget/selected-style.cpp b/src/ui/widget/selected-style.cpp index e7b0188d8..a8f9f9c60 100644 --- a/src/ui/widget/selected-style.cpp +++ b/src/ui/widget/selected-style.cpp @@ -103,7 +103,7 @@ static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries); static Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop); SelectedStyle::SelectedStyle(bool /*layout*/) - : + : current_stroke_width(0), _desktop (NULL), @@ -955,13 +955,13 @@ SelectedStyle::update() _paintserver_id[i] += ")"; if (SP_IS_LINEARGRADIENT (server)) { - SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false); + SPGradient *vector = SP_GRADIENT(server)->getVector(); sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_l[i], vector); place->add(_gradient_box_l[i]); _tooltips.set_tip(*place, __lgradient[i]); _mode[i] = SS_LGRADIENT; } else if (SP_IS_RADIALGRADIENT (server)) { - SPGradient *vector = sp_gradient_get_vector(SP_GRADIENT(server), false); + SPGradient *vector = SP_GRADIENT(server)->getVector(); sp_gradient_image_set_gradient ((SPGradientImage *) _gradient_preview_r[i], vector); place->add(_gradient_box_r[i]); _tooltips.set_tip(*place, __rgradient[i]); @@ -1165,7 +1165,7 @@ RotateableSwatch::RotateableSwatch(SelectedStyle *parent, guint mode) : undokey("ssrot1"), cr(0), cr_set(false) - + { } @@ -1426,14 +1426,14 @@ RotateableStrokeWidth::do_release(double by, guint modifier) { Dialog::FillAndStroke *get_fill_and_stroke_panel(SPDesktop *desktop) { - if (Dialog::PanelDialogBase *panel_dialog = + if (Dialog::PanelDialogBase *panel_dialog = dynamic_cast<Dialog::PanelDialogBase *>(desktop->_dlg_mgr->getDialog("FillAndStroke"))) { try { - Dialog::FillAndStroke &fill_and_stroke = + Dialog::FillAndStroke &fill_and_stroke = dynamic_cast<Dialog::FillAndStroke &>(panel_dialog->getPanel()); return &fill_and_stroke; } catch (std::exception e) { } - } + } return 0; } diff --git a/src/unclump.cpp b/src/unclump.cpp index aebcfd908..85306698c 100644 --- a/src/unclump.cpp +++ b/src/unclump.cpp @@ -16,7 +16,7 @@ // Taking bbox of an item is an expensive operation, and we need to do it many times, so here we // cache the centers, widths, and heights of items -//FIXME: make a class with these cashes as members instead of globals +//FIXME: make a class with these cashes as members instead of globals std::map<const gchar *, Geom::Point> c_cache; std::map<const gchar *, Geom::Point> wh_cache; @@ -26,18 +26,18 @@ Center of bbox of item Geom::Point unclump_center (SPItem *item) { - std::map<const gchar *, Geom::Point>::iterator i = c_cache.find(SP_OBJECT_ID(item)); + std::map<const gchar *, Geom::Point>::iterator i = c_cache.find(item->getId()); if ( i != c_cache.end() ) { return i->second; } Geom::OptRect r = item->getBounds(sp_item_i2d_affine(item)); if (r) { - Geom::Point const c = r->midpoint(); - c_cache[SP_OBJECT_ID(item)] = c; - return c; + Geom::Point const c = r->midpoint(); + c_cache[item->getId()] = c; + return c; } else { - // FIXME + // FIXME return Geom::Point(0, 0); } } @@ -46,16 +46,16 @@ Geom::Point unclump_wh (SPItem *item) { Geom::Point wh; - std::map<const gchar *, Geom::Point>::iterator i = wh_cache.find(SP_OBJECT_ID(item)); + std::map<const gchar *, Geom::Point>::iterator i = wh_cache.find(item->getId()); if ( i != wh_cache.end() ) { wh = i->second; } else { Geom::OptRect r = item->getBounds(sp_item_i2d_affine(item)); - if (r) { + if (r) { wh = r->dimensions(); - wh_cache[SP_OBJECT_ID(item)] = wh; + wh_cache[item->getId()] = wh; } else { - wh = Geom::Point(0, 0); + wh = Geom::Point(0, 0); } } @@ -63,7 +63,7 @@ unclump_wh (SPItem *item) } /** -Distance between "edges" of item1 and item2. An item is considered to be an ellipse inscribed into its w/h, +Distance between "edges" of item1 and item2. An item is considered to be an ellipse inscribed into its w/h, so its radius (distance from center to edge) depends on the w/h and the angle towards the other item. May be negative if the edge of item1 is between the center and the edge of item2. */ @@ -287,7 +287,7 @@ unclump_push (SPItem *from, SPItem *what, double dist) Geom::Matrix move = Geom::Translate (by); - std::map<const gchar *, Geom::Point>::iterator i = c_cache.find(SP_OBJECT_ID(what)); + std::map<const gchar *, Geom::Point>::iterator i = c_cache.find(what->getId()); if ( i != c_cache.end() ) { i->second *= move; } @@ -310,7 +310,7 @@ unclump_pull (SPItem *to, SPItem *what, double dist) Geom::Matrix move = Geom::Translate (by); - std::map<const gchar *, Geom::Point>::iterator i = c_cache.find(SP_OBJECT_ID(what)); + std::map<const gchar *, Geom::Point>::iterator i = c_cache.find(what->getId()); if ( i != c_cache.end() ) { i->second *= move; } @@ -325,7 +325,7 @@ unclump_pull (SPItem *to, SPItem *what, double dist) /** Unclumps the items in \a items, reducing local unevenness in their distribution. Produces an effect similar to "engraver dots". The only distribution which is unchanged by unclumping is a hexagonal -grid. May be called repeatedly for stronger effect. +grid. May be called repeatedly for stronger effect. */ void unclump (GSList *items) @@ -333,7 +333,7 @@ unclump (GSList *items) c_cache.clear(); wh_cache.clear(); - for (GSList *i = items; i != NULL; i = i->next) { // for each original/clone x: + for (GSList *i = items; i != NULL; i = i->next) { // for each original/clone x: SPItem *item = SP_ITEM (i->data); GSList *nei = NULL; @@ -353,7 +353,7 @@ unclump (GSList *items) g_slist_free (rest); break; } - } + } if (g_slist_length (nei) >= 2) { double ave = unclump_average (item, nei); @@ -369,7 +369,7 @@ unclump (GSList *items) if (fabs (ave) < 1e6 && fabs (dist_closest) < 1e6 && fabs (dist_farest) < 1e6) { // otherwise the items are bogus // increase these coefficients to make unclumping more aggressive and less stable // the pull coefficient is a bit bigger to counteract the long-term expansion trend - unclump_push (closest, item, 0.3 * (ave - dist_closest)); + unclump_push (closest, item, 0.3 * (ave - dist_closest)); unclump_pull (farest, item, 0.35 * (dist_farest - ave)); } } diff --git a/src/util/Makefile_insert b/src/util/Makefile_insert index b76a4dcdb..d2f005531 100644 --- a/src/util/Makefile_insert +++ b/src/util/Makefile_insert @@ -1,24 +1,35 @@ ## Makefile.am fragment sourced by src/Makefile.am. ink_common_sources += \ + util/accumulators.h \ util/compose.hpp \ - util/ucompose.hpp \ + util/copy.h \ util/enums.h \ + util/ege-tags.h \ + util/ege-tags.cpp \ util/filter-list.h \ + util/find-if-before.h \ + util/find-last-if.h \ util/fixed_point.h \ util/format.h \ util/forward-pointer-iterator.h \ + util/function.h \ util/glib-list-iterators.h \ util/list.h \ util/list-container.h \ + util/list-copy.h \ + util/longest-common-suffix.h \ util/map-list.h \ util/mathfns.h \ + util/reference.h \ util/reverse-list.h \ util/share.h \ util/share.cpp \ util/tuple.h \ + util/ucompose.hpp \ util/units.cpp \ - util/units.h + util/units.h \ + util/unordered-containers.h # ###################### # ### CxxTest stuff #### diff --git a/src/util/accumulators.h b/src/util/accumulators.h new file mode 100644 index 000000000..c627786b1 --- /dev/null +++ b/src/util/accumulators.h @@ -0,0 +1,113 @@ +/** @file + * Frequently used accumulators for use with libsigc++ + */ +/* Authors: + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2009 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_UTIL_ACCUMULATORS_H +#define SEEN_UTIL_ACCUMULATORS_H + +#include <iterator> + +namespace Inkscape { +namespace Util { + +/** + * Accumulator which evaluates slots in reverse connection order. + * The slot that was connected last is evaluated first. + */ +struct Reverse { + typedef void result_type; + template <typename T_iterator> + result_type operator()(T_iterator first, T_iterator last) const { + while (first != last) *(--last); + } +}; + +/** + * Accumulator type for interruptible signals. Slots return a boolean value; emission + * is stopped when true is returned from a slot. + */ +struct Interruptible { + typedef bool result_type; + template <typename T_iterator> + result_type operator()(T_iterator first, T_iterator last) const { + for (; first != last; ++first) + if (*first) return true; + return false; + } +}; + +/** + * Same as Interruptible, but the slots are called in reverse order of connection, + * e.g. the slot that was connected last is evaluated first. + */ +struct ReverseInterruptible { + typedef bool result_type; + template <typename T_iterator> + result_type operator()(T_iterator first, T_iterator last) const { + while (first != last) { + if (*(--last)) return true; + } + return false; + } +}; + +/** + * The template parameter specifies how many slots from the beginning of the list + * should be evaluated after other slots. Useful for signals which invoke other signals + * once complete. Undefined results if the signal does not have at least @c num_chained + * slots before first emission. + * + * For example, if template param = 3, the execution order is as follows: + * @verbatim + 8. 1. 2. 3. 4. 5. 6. 7. + S1 S2 S3 S4 S5 S6 S7 S8 @endverbatim + */ +template <unsigned num_chained = 1> +struct Chained { + typedef void result_type; + template <typename T_iterator> + result_type operator()(T_iterator first, T_iterator last) const { + T_iterator save_first = first; + // ARGH, iterator_traits is not defined for slot iterators! + //std::advance(first, num_chained); + for (unsigned i = 0; i < num_chained && first != last; ++i) ++first; + for (; first != last; ++first) *first; + for (unsigned i = 0; i < num_chained && save_first != last; ++i, ++save_first) + *save_first; + } +}; + +/** + * Executes a logical OR on the results from slots. + */ +struct LogicalOr { + typedef bool result_type; + template <typename T_iterator> + result_type operator()(T_iterator first, T_iterator last) const { + bool ret = false; + for (; first != last; ++first) ret |= *first; + return ret; + } +}; + +} // namespace Util +} // namespace Inkscape + +#endif + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/traits/copy.h b/src/util/copy.h index 27038ff2d..27038ff2d 100644 --- a/src/traits/copy.h +++ b/src/util/copy.h diff --git a/src/util/ege-tags.cpp b/src/util/ege-tags.cpp new file mode 100644 index 000000000..5d33c85a3 --- /dev/null +++ b/src/util/ege-tags.cpp @@ -0,0 +1,178 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is EGE Tagging Support. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#if HAVE_LIBINTL_H +#include <libintl.h> +#endif // HAVE_LIBINTL_H + +#if !defined(_) +#define _(s) gettext(s) +#endif // !defined(_) + +#include <set> +#include <algorithm> +#include <functional> + +#include "ege-tags.h" + +#include <glib.h> + +namespace ege +{ + +Label::Label(std::string const& lang, std::string const& value) : + lang(lang), + value(value) +{ +} + +Label::~Label() +{ +} + +// ========================================================================= + +Tag::~Tag() +{ +} + +Tag::Tag(std::string const& key) : + key(key) +{ +} + +// ========================================================================= + +TagSet::TagSet() : + lang(), + tags(), + counts() +{ +} + +TagSet::~TagSet() +{ +} + +void TagSet::setLang(std::string const& lang) +{ + if (lang != this->lang) { + this->lang = lang; + } +} + + +struct sameLang : public std::binary_function<Label, Label, bool> { + bool operator()(Label const& x, Label const& y) const { return (x.lang == y.lang); } +}; + + +bool TagSet::addTag(Tag const& tag) +{ + bool present = false; + + for ( std::vector<Tag>::iterator it = tags.begin(); (it != tags.end()) && !present; ++it ) { + if (tag.key == it->key) { + present = true; + + for ( std::vector<Label>::const_iterator it2 = tag.labels.begin(); it2 != tag.labels.end(); ++it2 ) { + std::vector<Label>::iterator itOld = std::find_if( it->labels.begin(), it->labels.end(), std::bind2nd(sameLang(), *it2) ); + if (itOld != it->labels.end()) { + itOld->value = it2->value; + } else { + it->labels.push_back(*it2); + } + } + } + } + + if (!present) { + tags.push_back(tag); + counts[tag.key] = 0; + } + + return present; +} + + +std::vector<Tag> const& TagSet::getTags() +{ + return tags; +} + +int TagSet::getCount( std::string const& key ) +{ + int count = 0; + if ( counts.find(key) != counts.end() ) { + count = counts[key]; + } + return count; +} + +void TagSet::increment( std::string const& key ) +{ + if ( counts.find(key) != counts.end() ) { + counts[key]++; + } else { + Tag tag(key); + tags.push_back(tag); + counts[key] = 1; + } +} + +void TagSet::decrement( std::string const& key ) +{ + if ( counts.find(key) != counts.end() ) { + counts[key]--; + } +} + +} // namespace ege + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/util/ege-tags.h b/src/util/ege-tags.h new file mode 100644 index 000000000..eaba6a00a --- /dev/null +++ b/src/util/ege-tags.h @@ -0,0 +1,121 @@ +#ifndef SEEN_EGE_TAGS_H +#define SEEN_EGE_TAGS_H + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is EGE Tagging Support. + * + * The Initial Developer of the Original Code is + * Jon A. Cruz. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <string> +#include <vector> +#include <map> + +/* + * Implements base tagging of http://create.freedesktop.org/wiki/ResourceTagging . + */ + +// Note that this API is preliminary and subject to frequent change: + +namespace ege +{ + +class Label +{ +public: + Label(); + Label(std::string const& lang, std::string const& value); + ~Label(); + + std::string lang; + std::string value; +}; + +class Tag +{ +public: + Tag(); + Tag(std::string const& key); + ~Tag(); + + std::string key; + std::vector<Label> labels; +}; + + +/** + * Contains a set of tags with unique keys, and with locale support. + * + */ +class TagSet +{ +public: + TagSet(); + ~TagSet(); + + std::string const & getLang() const; + void setLang(std::string const& lang); + + /** + * Adds or updates a tag. + * + * @return true if a tag was updated, false if it was added. + */ + bool addTag(Tag const& tag); + std::vector<Tag> const& getTags(); + + int getCount( std::string const& key ); + void increment( std::string const& key ); + void decrement( std::string const& key ); + +private: + + std::string lang; + std::vector<Tag> tags; + std::map<std::string, int> counts; +}; + +} // namespace ege + + +#endif // SEEN_EGE_TAGS_H +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/util/filter-list.h b/src/util/filter-list.h index e00c33b08..50aba12fa 100644 --- a/src/util/filter-list.h +++ b/src/util/filter-list.h @@ -13,7 +13,7 @@ #define SEEN_INKSCAPE_UTIL_FILTER_LIST_H #include "util/list.h" -#include "traits/list-copy.h" +#include "util/list-copy.h" namespace Inkscape { diff --git a/src/algorithms/find-if-before.h b/src/util/find-if-before.h index 6a0f63be6..6a0f63be6 100644 --- a/src/algorithms/find-if-before.h +++ b/src/util/find-if-before.h diff --git a/src/algorithms/find-last-if.h b/src/util/find-last-if.h index 1ffd63b9c..1ffd63b9c 100644 --- a/src/algorithms/find-last-if.h +++ b/src/util/find-last-if.h diff --git a/src/util/fixed_point.h b/src/util/fixed_point.h index 6afec5e3c..05a73dab5 100644 --- a/src/util/fixed_point.h +++ b/src/util/fixed_point.h @@ -12,229 +12,7 @@ #ifndef SEEN_INKSCAPE_UTIL_FIXED_POINT_H #define SEEN_INKSCAPE_UTIL_FIXED_POINT_H -#include "traits/reference.h" -#include <math.h> -#include <algorithm> -#include <limits> - -namespace Inkscape { - -namespace Util { - -template <typename T, unsigned int precision> -class FixedPoint { -public: - FixedPoint() {} - FixedPoint(const FixedPoint& value) : v(value.v) {} - FixedPoint(char value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(unsigned char value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(short value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(unsigned short value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(int value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(unsigned int value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(double value) : v(static_cast<T>(floor(value*(1<<precision)))) {} - - FixedPoint& operator+=(FixedPoint val) { v += val.v; return *this; } - FixedPoint& operator-=(FixedPoint val) { v -= val.v; return *this; } - FixedPoint& operator*=(FixedPoint val) { - const unsigned int half_size = 8*sizeof(T)/2; - const T al = v&((1<<half_size)-1), bl = val.v&((1<<half_size)-1); - const T ah = v>>half_size, bh = val.v>>half_size; - v = static_cast<unsigned int>(al*bl)>>precision; - if ( half_size >= precision ) { - v += ((al*bh)+(ah*bl)+((ah*bh)<<half_size))<<(half_size-precision); - } else { - v += ((al*bh)+(ah*bl))>>(precision-half_size); - v += (ah*bh)<<(2*half_size-precision); - } - return *this; - } - - FixedPoint& operator*=(char val) { v *= val; return *this; } - FixedPoint& operator*=(unsigned char val) { v *= val; return *this; } - FixedPoint& operator*=(short val) { v *= val; return *this; } - FixedPoint& operator*=(unsigned short val) { v *= val; return *this; } - FixedPoint& operator*=(int val) { v *= val; return *this; } - FixedPoint& operator*=(unsigned int val) { v *= val; return *this; } - - FixedPoint operator+(FixedPoint val) const { FixedPoint r(*this); return r+=val; } - FixedPoint operator-(FixedPoint val) const { FixedPoint r(*this); return r-=val; } - FixedPoint operator*(FixedPoint val) const { FixedPoint r(*this); return r*=val; } - - FixedPoint operator*(char val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(unsigned char val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(short val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(unsigned short val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(int val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(unsigned int val) const { FixedPoint r(*this); return r*=val; } - - float operator*(float val) const { return static_cast<float>(*this)*val; } - double operator*(double val) const { return static_cast<double>(*this)*val; } - - operator char() const { return v>>precision; } - operator unsigned char() const { return v>>precision; } - operator short() const { return v>>precision; } - operator unsigned short() const { return v>>precision; } - operator int() const { return v>>precision; } - operator unsigned int() const { return v>>precision; } - - operator float() const { return ldexpf(v,-precision); } - operator double() const { return ldexp(v,-precision); } -private: - T v; -}; - -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(char a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(unsigned char a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(short a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(unsigned short a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(int a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(unsigned int a, FixedPoint<T,precision> b) { return b*=a; } - -template<typename T, unsigned int precision> float operator *(float a, FixedPoint<T,precision> b) { return b*a; } -template<typename T, unsigned int precision> double operator *(double a, FixedPoint<T,precision> b) { return b*a; } - -} - -} - -#endif -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : -/* - * Inkscape::Util::FixedPoint - fixed point type - * - * Authors: - * Jasper van de Gronde <th.v.d.gronde@hccnet.net> - * - * Copyright (C) 2006 Jasper van de Gronde - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#ifndef SEEN_INKSCAPE_UTIL_FIXED_POINT_H -#define SEEN_INKSCAPE_UTIL_FIXED_POINT_H - -#include "traits/reference.h" -#include <math.h> -#include <algorithm> -#include <limits> - -namespace Inkscape { - -namespace Util { - -template <typename T, unsigned int precision> -class FixedPoint { -public: - FixedPoint() {} - FixedPoint(const FixedPoint& value) : v(value.v) {} - FixedPoint(char value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(unsigned char value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(short value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(unsigned short value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(int value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(unsigned int value) : v(static_cast<T>(value)<<precision) {} - FixedPoint(double value) : v(static_cast<T>(floor(value*(1<<precision)))) {} - - FixedPoint& operator+=(FixedPoint val) { v += val.v; return *this; } - FixedPoint& operator-=(FixedPoint val) { v -= val.v; return *this; } - FixedPoint& operator*=(FixedPoint val) { - const unsigned int half_size = 8*sizeof(T)/2; - const T al = v&((1<<half_size)-1), bl = val.v&((1<<half_size)-1); - const T ah = v>>half_size, bh = val.v>>half_size; - v = static_cast<unsigned int>(al*bl)>>precision; - if ( half_size >= precision ) { - v += ((al*bh)+(ah*bl)+((ah*bh)<<half_size))<<(half_size-precision); - } else { - v += ((al*bh)+(ah*bl))>>(precision-half_size); - v += (ah*bh)<<(2*half_size-precision); - } - return *this; - } - - FixedPoint& operator*=(char val) { v *= val; return *this; } - FixedPoint& operator*=(unsigned char val) { v *= val; return *this; } - FixedPoint& operator*=(short val) { v *= val; return *this; } - FixedPoint& operator*=(unsigned short val) { v *= val; return *this; } - FixedPoint& operator*=(int val) { v *= val; return *this; } - FixedPoint& operator*=(unsigned int val) { v *= val; return *this; } - - FixedPoint operator+(FixedPoint val) const { FixedPoint r(*this); return r+=val; } - FixedPoint operator-(FixedPoint val) const { FixedPoint r(*this); return r-=val; } - FixedPoint operator*(FixedPoint val) const { FixedPoint r(*this); return r*=val; } - - FixedPoint operator*(char val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(unsigned char val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(short val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(unsigned short val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(int val) const { FixedPoint r(*this); return r*=val; } - FixedPoint operator*(unsigned int val) const { FixedPoint r(*this); return r*=val; } - - float operator*(float val) const { return static_cast<float>(*this)*val; } - double operator*(double val) const { return static_cast<double>(*this)*val; } - - operator char() const { return v>>precision; } - operator unsigned char() const { return v>>precision; } - operator short() const { return v>>precision; } - operator unsigned short() const { return v>>precision; } - operator int() const { return v>>precision; } - operator unsigned int() const { return v>>precision; } - - operator float() const { return ldexpf(v,-precision); } - operator double() const { return ldexp(v,-precision); } -private: - T v; -}; - -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(char a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(unsigned char a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(short a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(unsigned short a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(int a, FixedPoint<T,precision> b) { return b*=a; } -template<typename T, unsigned int precision> FixedPoint<T,precision> operator *(unsigned int a, FixedPoint<T,precision> b) { return b*=a; } - -template<typename T, unsigned int precision> float operator *(float a, FixedPoint<T,precision> b) { return b*a; } -template<typename T, unsigned int precision> double operator *(double a, FixedPoint<T,precision> b) { return b*a; } - -} - -} - -#endif -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : -/* - * Inkscape::Util::FixedPoint - fixed point type - * - * Authors: - * Jasper van de Gronde <th.v.d.gronde@hccnet.net> - * - * Copyright (C) 2006 Jasper van de Gronde - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#ifndef SEEN_INKSCAPE_UTIL_FIXED_POINT_H -#define SEEN_INKSCAPE_UTIL_FIXED_POINT_H - -#include "traits/reference.h" +#include "util/reference.h" #include <math.h> #include <algorithm> #include <limits> diff --git a/src/util/forward-pointer-iterator.h b/src/util/forward-pointer-iterator.h index 2c2345c81..1603fed27 100644 --- a/src/util/forward-pointer-iterator.h +++ b/src/util/forward-pointer-iterator.h @@ -15,7 +15,7 @@ #define SEEN_INKSCAPE_UTIL_FORWARD_POINTER_ITERATOR_H #include <iterator> -#include "traits/reference.h" +#include "util/reference.h" namespace Inkscape { diff --git a/src/traits/function.h b/src/util/function.h index d0dd5d87c..d0dd5d87c 100644 --- a/src/traits/function.h +++ b/src/util/function.h diff --git a/src/traits/list-copy.h b/src/util/list-copy.h index 9d99c4f29..811f93b77 100644 --- a/src/traits/list-copy.h +++ b/src/util/list-copy.h @@ -13,7 +13,7 @@ #define SEEN_INKSCAPE_TRAITS_LIST_COPY_H #include <iterator> -#include "traits/copy.h" +#include "util/copy.h" #include "util/list.h" namespace Inkscape { diff --git a/src/util/list.h b/src/util/list.h index 86a73711a..ebe3a9773 100644 --- a/src/util/list.h +++ b/src/util/list.h @@ -15,7 +15,7 @@ #include <cstddef> #include <iterator> #include "gc-managed.h" -#include "traits/reference.h" +#include "util/reference.h" namespace Inkscape { diff --git a/src/algorithms/longest-common-suffix.h b/src/util/longest-common-suffix.h index 04d2b179d..04d2b179d 100644 --- a/src/algorithms/longest-common-suffix.h +++ b/src/util/longest-common-suffix.h diff --git a/src/traits/reference.h b/src/util/reference.h index 199ae8929..199ae8929 100644 --- a/src/traits/reference.h +++ b/src/util/reference.h diff --git a/src/util/reverse-list.h b/src/util/reverse-list.h index 586f706c7..798b15701 100644 --- a/src/util/reverse-list.h +++ b/src/util/reverse-list.h @@ -13,7 +13,7 @@ #define SEEN_INKSCAPE_UTIL_REVERSE_LIST_H #include "util/list.h" -#include "traits/list-copy.h" +#include "util/list-copy.h" namespace Inkscape { diff --git a/src/util/tuple.h b/src/util/tuple.h index bf9338366..42266c8d2 100644 --- a/src/util/tuple.h +++ b/src/util/tuple.h @@ -12,7 +12,7 @@ #ifndef SEEN_INKSCAPE_UTIL_TUPLE_H #define SEEN_INKSCAPE_UTIL_TUPLE_H -#include "traits/reference.h" +#include "util/reference.h" namespace Inkscape { diff --git a/src/util/unordered-containers.h b/src/util/unordered-containers.h new file mode 100644 index 000000000..aaf771959 --- /dev/null +++ b/src/util/unordered-containers.h @@ -0,0 +1,78 @@ +/** @file + * @brief Compatibility wrapper for unordered containers. + */ +/* Authors: + * Jon A. Cruz <jon@joncruz.org> + * Krzysztof KosiÅ„ski <tweenk.pl@gmail.com> + * + * Copyright (C) 2010 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INK_UTIL_UNORDERED_CONTAINERS_H +#define SEEN_INK_UTIL_UNORDERED_CONTAINERS_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +#if defined(HAVE_TR1_UNORDERED_SET) + +# include <tr1/unordered_set> +# include <tr1/unordered_map> +# define INK_UNORDERED_SET std::tr1::unordered_set +# define INK_UNORDERED_MAP std::tr1::unordered_map +# define INK_HASH std::tr1::hash + +#elif defined(HAVE_BOOST_UNORDERED_SET) +# include <boost/unordered_set.hpp> +# include <boost/unordered_map.hpp> +# define INK_UNORDERED_SET boost::unordered_set +# define INK_UNORDERED_MAP boost::unordered_map +# define INK_HASH boost::hash + +#elif defined(HAVE_EXT_HASH_SET) + +# include <functional> +# include <ext/hash_set> +# include <ext/hash_map> +# define INK_UNORDERED_SET __gnu_cxx::hash_set +# define INK_UNORDERED_MAP __gnu_cxx::hash_map +# define INK_HASH __gnu_cxx::hash + +namespace __gnu_cxx { +// hash function for pointers +// TR1 and Boost have this defined by default, __gnu_cxx doesn't +template<typename T> +struct hash<T *> : public std::unary_function<T *, std::size_t> { + std::size_t operator()(T *p) const { + // Taken from Boost + std::size_t x = static_cast<std::size_t>(reinterpret_cast<std::ptrdiff_t>(p)); + return x + (x >> 3); + } +}; +} // namespace __gnu_cxx +#endif + +#else +/// Name (with namespace) of the unordered set template. +#define INK_UNORDERED_SET +/// Name (with namespace) of the unordered map template. +#define INK_UNORDERED_MAP +/// Name (with namespace) of the hash template. +#define INK_HASH + +#endif + +#endif // SEEN_SET_TYPES_H +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/verbs.cpp b/src/verbs.cpp index 6f86c3cce..dc1116953 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -16,6 +16,7 @@ * MenTaLguY <mental@rydia.net> * David Turner <novalis@gnu.org> * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl> * Copyright (C) (date unspecified) Authors @@ -49,7 +50,6 @@ #include "draw-context.h" #include "extension/effect.h" #include "file.h" -#include "gradient-context.h" #include "gradient-drag.h" #include "helper/action.h" #include "help.h" @@ -58,7 +58,6 @@ #include "layer-fns.h" #include "layer-manager.h" #include "message-stack.h" -#include "node-context.h" #include "path-chemistry.h" #include "preferences.h" #include "select-context.h" @@ -80,6 +79,7 @@ #include "ui/dialog/layers.h" #include "ui/dialog/swatches.h" #include "ui/icon-names.h" +#include "ui/tool/node-tool.h" //#ifdef WITH_INKBOARD //#include "jabber_whiteboard/session-manager.h" @@ -767,7 +767,7 @@ FileVerb::perform(SPAction *action, void *data, void */*pdata*/) Gtk::Window *parent = desktop->getToplevel(); g_assert(parent != NULL); - switch ((long) data) { + switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_FILE_NEW: sp_file_new_default(); break; @@ -833,7 +833,6 @@ EditVerb::perform(SPAction *action, void *data, void */*pdata*/) SPDesktop *dt = static_cast<SPDesktop*>(sp_action_get_view(action)); if (!dt) return; - SPEventContext *ec = dt->event_context; switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_EDIT_UNDO: @@ -846,7 +845,7 @@ EditVerb::perform(SPAction *action, void *data, void */*pdata*/) sp_selection_cut(dt); break; case SP_VERB_EDIT_COPY: - sp_selection_copy(); + sp_selection_copy(dt); break; case SP_VERB_EDIT_PASTE: sp_selection_paste(dt, false); @@ -918,63 +917,26 @@ EditVerb::perform(SPAction *action, void *data, void */*pdata*/) sp_edit_clear_all(dt); break; case SP_VERB_EDIT_SELECT_ALL: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->select_all_from_subpath(false); - } else { - sp_edit_select_all(dt); - } + SelectionHelper::selectAll(dt); break; case SP_VERB_EDIT_INVERT: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->select_all_from_subpath(true); - } else { - sp_edit_invert(dt); - } + SelectionHelper::invert(dt); break; case SP_VERB_EDIT_SELECT_ALL_IN_ALL_LAYERS: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->select_all(false); - } else { - sp_edit_select_all_in_all_layers(dt); - } + SelectionHelper::selectAllInAll(dt); break; case SP_VERB_EDIT_INVERT_IN_ALL_LAYERS: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->select_all(true); - } else { - sp_edit_invert_in_all_layers(dt); - } + SelectionHelper::invertAllInAll(dt); break; - case SP_VERB_EDIT_SELECT_NEXT: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->select_next(); - } else if (tools_isactive(dt, TOOLS_GRADIENT) - && ec->_grdrag->isNonEmpty()) { - sp_gradient_context_select_next (ec); - } else { - sp_selection_item_next(dt); - } + SelectionHelper::selectNext(dt); break; case SP_VERB_EDIT_SELECT_PREV: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->select_prev(); - } else if (tools_isactive(dt, TOOLS_GRADIENT) - && ec->_grdrag->isNonEmpty()) { - sp_gradient_context_select_prev (ec); - } else { - sp_selection_item_prev(dt); - } + SelectionHelper::selectPrev(dt); break; - case SP_VERB_EDIT_DESELECT: - if (tools_isactive(dt, TOOLS_NODES)) { - ec->shape_editor->deselect(); - } else { - sp_desktop_selection(dt)->clear(); - } + SelectionHelper::selectNone(dt); break; - case SP_VERB_EDIT_GUIDES_AROUND_PAGE: sp_guide_create_guides_around_page(dt); break; @@ -1086,7 +1048,7 @@ SelectionVerb::perform(SPAction *action, void *data, void */*pdata*/) sp_selected_path_simplify(dt); break; case SP_VERB_SELECTION_REVERSE: - sp_selected_path_reverse(dt); + SelectionHelper::reverse(dt); break; case SP_VERB_SELECTION_TRACE: inkscape_dialogs_unhide(); @@ -1117,7 +1079,7 @@ void LayerVerb::perform(SPAction *action, void *data, void */*pdata*/) { SPDesktop *dt = static_cast<SPDesktop*>(sp_action_get_view(action)); - unsigned int verb = reinterpret_cast<std::size_t>(data); + size_t verb = reinterpret_cast<std::size_t>(data); if ( !dt || !dt->currentLayer() ) { return; @@ -1365,41 +1327,12 @@ ObjectVerb::perform( SPAction *action, void *data, void */*pdata*/ ) flowtext_to_text(); break; case SP_VERB_OBJECT_FLIP_HORIZONTAL: - // When working with the node tool ... - if (tools_isactive(dt, TOOLS_NODES)) { - Inkscape::NodePath::Node *active_node = Inkscape::NodePath::Path::active_node; - - // ... and one of the nodes is currently mouseovered ... - if (active_node) { - - // ... flip the selected nodes about that node - ec->shape_editor->flip(Geom::X, active_node->pos); - } else { - - // ... or else about the center of their bounding box. - ec->shape_editor->flip(Geom::X); - } - - // When working with the selector tool, flip the selection about its rotation center - // (if it is visible) or about the center of the bounding box. - } else { - sp_selection_scale_relative(sel, center, Geom::Scale(-1.0, 1.0)); - } + sp_selection_scale_relative(sel, center, Geom::Scale(-1.0, 1.0)); sp_document_done(sp_desktop_document(dt), SP_VERB_OBJECT_FLIP_HORIZONTAL, _("Flip horizontally")); break; case SP_VERB_OBJECT_FLIP_VERTICAL: - // The behaviour is analogous to flipping horizontally - if (tools_isactive(dt, TOOLS_NODES)) { - Inkscape::NodePath::Node *active_node = Inkscape::NodePath::Path::active_node; - if (active_node) { - ec->shape_editor->flip(Geom::Y, active_node->pos); - } else { - ec->shape_editor->flip(Geom::Y); - } - } else { - sp_selection_scale_relative(sel, center, Geom::Scale(1.0, -1.0)); - } + sp_selection_scale_relative(sel, center, Geom::Scale(1.0, -1.0)); sp_document_done(sp_desktop_document(dt), SP_VERB_OBJECT_FLIP_VERTICAL, _("Flip vertically")); break; @@ -1635,7 +1568,7 @@ ZoomVerb::perform(SPAction *action, void *data, void */*pdata*/) gdouble zoom_inc = prefs->getDoubleLimited( "/options/zoomincrement/value", 1.414213562, 1.01, 10 ); - switch (GPOINTER_TO_INT(data)) { + switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_ZOOM_IN: { gint mul = 1 + gobble_key_events( @@ -1922,8 +1855,8 @@ TutorialVerb::perform(SPAction */*action*/, void *data, void */*pdata*/) { switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_TUTORIAL_BASIC: - /* TRANSLATORS: If you have translated the tutorial-basic.svg file to your language, - then translate this string as "tutorial-basic.LANG.svg" (where LANG is your language + /* TRANSLATORS: If you have translated the tutorial-basic.en.svgz file to your language, + then translate this string as "tutorial-basic.LANG.svgz" (where LANG is your language code); otherwise leave as "tutorial-basic.svg". */ sp_help_open_tutorial(NULL, (gpointer)_("tutorial-basic.svg")); break; @@ -1943,6 +1876,10 @@ TutorialVerb::perform(SPAction */*action*/, void *data, void */*pdata*/) // TRANSLATORS: See "tutorial-basic.svg" comment. sp_help_open_tutorial(NULL, (gpointer)_("tutorial-calligraphy.svg")); break; + case SP_VERB_TUTORIAL_INTERPOLATE: + // TRANSLATORS: See "tutorial-basic.svg" comment. + sp_help_open_tutorial(NULL, (gpointer)_("tutorial-interpolate.svg")); + break; case SP_VERB_TUTORIAL_DESIGN: // TRANSLATORS: See "tutorial-basic.svg" comment. sp_help_open_tutorial(NULL, (gpointer)_("tutorial-elements.svg")); @@ -2088,7 +2025,7 @@ EffectLastVerb::perform(SPAction *action, void *data, void */*pdata*/) if (effect == NULL) return; if (current_view == NULL) return; - switch ((long) data) { + switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_EFFECT_LAST_PREF: effect->prefs(current_view); break; @@ -2153,7 +2090,7 @@ FitCanvasVerb::perform(SPAction *action, void *data, void */*pdata*/) SPDocument *doc = sp_desktop_document(dt); if (!doc) return; - switch ((long) data) { + switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_FIT_CANVAS_TO_SELECTION: verb_fit_canvas_to_selection(dt); break; @@ -2222,7 +2159,7 @@ LockAndHideVerb::perform(SPAction *action, void *data, void */*pdata*/) SPDocument *doc = sp_desktop_document(dt); if (!doc) return; - switch ((long) data) { + switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_UNLOCK_ALL: unlock_all(dt); sp_document_done(doc, SP_VERB_UNLOCK_ALL, _("Unlock all objects in the current layer")); @@ -2734,6 +2671,8 @@ Verb *Verb::_base_verbs[] = { N_("Using bitmap tracing"), NULL/*"tutorial_tracing"*/), new TutorialVerb(SP_VERB_TUTORIAL_CALLIGRAPHY, "TutorialsCalligraphy", N_("Inkscape: _Calligraphy"), N_("Using the Calligraphy pen tool"), NULL), + new TutorialVerb(SP_VERB_TUTORIAL_INTERPOLATE, "TutorialsInterpolate", N_("Inkscape: _Interpolate"), + N_("Using the interpolate extension"), NULL/*"tutorial_interpolate"*/), new TutorialVerb(SP_VERB_TUTORIAL_DESIGN, "TutorialsDesign", N_("_Elements of Design"), N_("Principles of design in the tutorial form"), NULL/*"tutorial_design"*/), new TutorialVerb(SP_VERB_TUTORIAL_TIPS, "TutorialsTips", N_("_Tips and Tricks"), diff --git a/src/verbs.h b/src/verbs.h index d0abcdca2..7cc580f26 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -264,6 +264,7 @@ enum { SP_VERB_TUTORIAL_ADVANCED, SP_VERB_TUTORIAL_TRACING, SP_VERB_TUTORIAL_CALLIGRAPHY, + SP_VERB_TUTORIAL_INTERPOLATE, SP_VERB_TUTORIAL_DESIGN, SP_VERB_TUTORIAL_TIPS, /* Effects */ diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 45f013f16..9af325926 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -29,8 +29,9 @@ spw-utilities.cpp sp-xmlview-attr-list.cpp sp-xmlview-content.cpp sp-xmlview-tree.cpp +swatch-selector.cpp toolbox.cpp ) ADD_LIBRARY(widgets STATIC ${widgets_SRC}) TARGET_LINK_LIBRARIES(widgets -2geom ${INKSCAPE_LIBS})
\ No newline at end of file +2geom ${INKSCAPE_LIBS}) diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert index 08b2433ea..5d327d8a3 100644 --- a/src/widgets/Makefile_insert +++ b/src/widgets/Makefile_insert @@ -67,6 +67,8 @@ ink_common_sources += \ widgets/sp-xmlview-tree.h \ widgets/stroke-style.cpp \ widgets/stroke-style.h \ + widgets/swatch-selector.cpp \ + widgets/swatch-selector.h \ widgets/toolbox.cpp \ widgets/toolbox.h \ widgets/widget-sizes.h diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index b63992afe..ab440595f 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -48,25 +48,31 @@ #include "sp-image.h" #include "sp-item.h" #include "sp-namedview.h" -#include "toolbox.h" #include "ui/dialog/dialog-manager.h" #include "ui/dialog/swatches.h" #include "ui/icon-names.h" #include "ui/widget/dock.h" #include "ui/widget/layer-selector.h" #include "ui/widget/selected-style.h" -#include "widgets/button.h" -#include "widgets/ruler.h" -#include "widgets/spinbutton-events.h" -#include "widgets/spw-utilities.h" -#include "widgets/toolbox.h" -#include "widgets/widget-sizes.h" +#include "ui/uxmanager.h" + +// We're in the "widgets" directory, so no need to explicitly prefix these: +#include "button.h" +#include "ruler.h" +#include "spinbutton-events.h" +#include "spw-utilities.h" +#include "toolbox.h" +#include "widget-sizes.h" #if defined (SOLARIS) && (SOLARIS == 8) #include "round.h" using Inkscape::round; #endif + +using Inkscape::UI::UXManager; +using Inkscape::UI::ToolboxFactory; + #ifdef WITH_INKBOARD #endif @@ -85,7 +91,6 @@ enum { /* SPDesktopWidget */ static void sp_desktop_widget_class_init (SPDesktopWidgetClass *klass); -static void sp_desktop_widget_init (SPDesktopWidget *widget); static void sp_desktop_widget_destroy (GtkObject *object); static void sp_desktop_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation); @@ -97,7 +102,6 @@ static void sp_dtw_color_profile_event(EgeColorProfTracker *widget, SPDesktopWid static void cms_adjust_toggled( GtkWidget *button, gpointer data ); static void cms_adjust_set_sensitive( SPDesktopWidget *dtw, bool enabled ); static void sp_desktop_widget_adjustment_value_changed (GtkAdjustment *adj, SPDesktopWidget *dtw); -static void sp_desktop_widget_namedview_modified (SPObject *obj, guint flags, SPDesktopWidget *dtw); static gdouble sp_dtw_zoom_value_to_display (gdouble value); static gdouble sp_dtw_zoom_display_to_value (gdouble value); @@ -241,7 +245,7 @@ SPDesktopWidget::window_get_pointer() /** * Registers SPDesktopWidget class and returns its type number. */ -GType sp_desktop_widget_get_type(void) +GType SPDesktopWidget::getType(void) { static GtkType type = 0; if (!type) { @@ -254,7 +258,7 @@ GType sp_desktop_widget_get_type(void) 0, // class_data sizeof(SPDesktopWidget), 0, // n_preallocs - (GInstanceInitFunc)sp_desktop_widget_init, + (GInstanceInitFunc)SPDesktopWidget::init, 0 // value_table }; type = g_type_register_static(SP_TYPE_VIEW_WIDGET, "SPDesktopWidget", &info, static_cast<GTypeFlags>(0)); @@ -282,14 +286,12 @@ sp_desktop_widget_class_init (SPDesktopWidgetClass *klass) /** * Callback for SPDesktopWidget object initialization. */ -static void -sp_desktop_widget_init (SPDesktopWidget *dtw) +void SPDesktopWidget::init( SPDesktopWidget *dtw ) { GtkWidget *widget; GtkWidget *tbl; GtkWidget *canvas_tbl; - GtkWidget *hbox; GtkWidget *eventbox; GtkStyle *style; @@ -320,24 +322,26 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) gtk_box_pack_end( GTK_BOX( dtw->vbox ), GTK_WIDGET(dtw->panels->gobj()), FALSE, TRUE, 0 ); } - hbox = gtk_hbox_new (FALSE, 0); - gtk_box_pack_end (GTK_BOX (dtw->vbox), hbox, TRUE, TRUE, 0); - gtk_widget_show (hbox); + dtw->hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_end( GTK_BOX (dtw->vbox), dtw->hbox, TRUE, TRUE, 0 ); + gtk_widget_show(dtw->hbox); - dtw->aux_toolbox = sp_aux_toolbox_new (); + dtw->aux_toolbox = ToolboxFactory::createAuxToolbox(); gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->aux_toolbox, FALSE, TRUE, 0); - dtw->snap_toolbox = sp_snap_toolbox_new (); - gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->snap_toolbox, FALSE, TRUE, 0); + dtw->snap_toolbox = ToolboxFactory::createSnapToolbox(); + ToolboxFactory::setOrientation( dtw->snap_toolbox, GTK_ORIENTATION_VERTICAL ); + gtk_box_pack_end( GTK_BOX(dtw->hbox), dtw->snap_toolbox, FALSE, TRUE, 0 ); - dtw->commands_toolbox = sp_commands_toolbox_new (); + dtw->commands_toolbox = ToolboxFactory::createCommandsToolbox(); gtk_box_pack_end (GTK_BOX (dtw->vbox), dtw->commands_toolbox, FALSE, TRUE, 0); - dtw->tool_toolbox = sp_tool_toolbox_new (); - gtk_box_pack_start (GTK_BOX (hbox), dtw->tool_toolbox, FALSE, TRUE, 0); + dtw->tool_toolbox = ToolboxFactory::createToolToolbox(); + ToolboxFactory::setOrientation( dtw->tool_toolbox, GTK_ORIENTATION_VERTICAL ); + gtk_box_pack_start( GTK_BOX(dtw->hbox), dtw->tool_toolbox, FALSE, TRUE, 0 ); tbl = gtk_table_new (2, 3, FALSE); - gtk_box_pack_start (GTK_BOX (hbox), tbl, TRUE, TRUE, 1); + gtk_box_pack_start( GTK_BOX(dtw->hbox), tbl, TRUE, TRUE, 1 ); canvas_tbl = gtk_table_new (3, 3, FALSE); @@ -528,8 +532,10 @@ sp_desktop_widget_init (SPDesktopWidget *dtw) dtw->_tracker = ege_color_prof_tracker_new(GTK_WIDGET(dtw->layer_selector->gobj())); #if ENABLE_LCMS - { + bool fromDisplay = prefs->getBool( "/options/displayprofile/from_display"); + if ( fromDisplay ) { Glib::ustring id = Inkscape::colorprofile_get_display_id( 0, 0 ); + bool enabled = false; if ( dtw->canvas->cms_key ) { *(dtw->canvas->cms_key) = id; @@ -567,6 +573,8 @@ sp_desktop_widget_destroy (GtkObject *object) { SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (object); + UXManager::getInstance()->delTrack(dtw); + if (dtw->desktop) { if ( watcher ) { watcher->remove(dtw); @@ -707,23 +715,22 @@ sp_desktop_widget_realize (GtkWidget *widget) dtw->desktop->set_display_area (d.x0, d.y0, d.x1, d.y1, 10); - sp_desktop_widget_update_namedview(dtw); + dtw->updateNamedview(); } /* This is just to provide access to common functionality from sp_desktop_widget_realize() above as well as from SPDesktop::change_document() */ -void -sp_desktop_widget_update_namedview (SPDesktopWidget *dtw) { - g_return_if_fail(dtw); - - /* Listen on namedview modification */ +void SPDesktopWidget::updateNamedview() +{ + // Listen on namedview modification // originally (prior to the sigc++ conversion) the signal was simply // connected twice rather than disconnecting the first connection - dtw->modified_connection.disconnect(); - dtw->modified_connection = dtw->desktop->namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_desktop_widget_namedview_modified), dtw)); - sp_desktop_widget_namedview_modified (dtw->desktop->namedview, SP_OBJECT_MODIFIED_FLAG, dtw); + modified_connection.disconnect(); - dtw->updateTitle(SP_DOCUMENT_NAME (dtw->desktop->doc())); + modified_connection = desktop->namedview->connectModified(sigc::mem_fun(*this, &SPDesktopWidget::namedviewModified)); + namedviewModified(desktop->namedview, SP_OBJECT_MODIFIED_FLAG); + + updateTitle(SP_DOCUMENT_NAME (desktop->doc())); } /** @@ -790,6 +797,11 @@ void cms_adjust_toggled( GtkWidget */*button*/, gpointer data ) dtw->requestCanvasUpdate(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setBool("/options/displayprofile/enable", down); + if (down) { + dtw->setMessage (Inkscape::NORMAL_MESSAGE, _("Color-managed display is <b>enabled</b> in this window")); + } else { + dtw->setMessage (Inkscape::NORMAL_MESSAGE, _("Color-managed display is <b>disabled</b> in this window")); + } } #endif // ENABLE_LCMS } @@ -900,8 +912,8 @@ SPDesktopWidget::shutdown() GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("<span weight=\"bold\" size=\"larger\">The file \"%s\" was saved with a format (%s) that may cause data loss!</span>\n\n" - "Do you want to save this file as an Inkscape SVG?"), - SP_DOCUMENT_NAME(doc), + "Do you want to save this file as Inkscape SVG?"), + SP_DOCUMENT_NAME(doc)? SP_DOCUMENT_NAME(doc) : "Unnamed", SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE); // fix for bug 1767940: GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(GTK_MESSAGE_DIALOG(dialog)->label), GTK_CAN_FOCUS); @@ -1199,18 +1211,18 @@ sp_desktop_widget_fullscreen(SPDesktopWidget *dtw) /** * Hide whatever the user does not want to see in the window */ -void -sp_desktop_widget_layout (SPDesktopWidget *dtw) +void SPDesktopWidget::layoutWidgets() { + SPDesktopWidget *dtw = this; Glib::ustring pref_root; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (dtw->desktop->is_focusMode()) { - pref_root = "/focus/"; + pref_root = "/focus/"; } else if (dtw->desktop->is_fullscreen()) { - pref_root = "/fullscreen/"; + pref_root = "/fullscreen/"; } else { - pref_root = "/window/"; + pref_root = "/window/"; } #ifndef GDK_WINDOWING_QUARTZ @@ -1228,17 +1240,17 @@ sp_desktop_widget_layout (SPDesktopWidget *dtw) } if (!prefs->getBool(pref_root + "snaptoolbox/state", true)) { - gtk_widget_hide_all (dtw->snap_toolbox); - } else { - gtk_widget_show_all (dtw->snap_toolbox); - } + gtk_widget_hide_all (dtw->snap_toolbox); + } else { + gtk_widget_show_all (dtw->snap_toolbox); + } if (!prefs->getBool(pref_root + "toppanel/state", true)) { gtk_widget_hide_all (dtw->aux_toolbox); } else { // we cannot just show_all because that will show all tools' panels; // this is a function from toolbox.cpp that shows only the current tool's panel - show_aux_toolbox (dtw->aux_toolbox); + ToolboxFactory::showAuxToolbox(dtw->aux_toolbox); } if (!prefs->getBool(pref_root + "toolbox/state", true)) { @@ -1336,8 +1348,57 @@ SPDesktopWidget::isToolboxButtonActive (const gchar* id) return isActive; } -SPViewWidget * -sp_desktop_widget_new (SPNamedView *namedview) +void SPDesktopWidget::setToolboxPosition(Glib::ustring const& id, GtkPositionType pos) +{ + // Note - later on these won't be individual member variables. + GtkWidget* toolbox = 0; + if (id == "ToolToolbar") { + toolbox = tool_toolbox; + } else if (id == "AuxToolbar") { + toolbox = aux_toolbox; + } else if (id == "CommandsToolbar") { + toolbox = commands_toolbox; + } else if (id == "SnapToolbar") { + toolbox = snap_toolbox; + } + + + if (toolbox) { + switch(pos) { + case GTK_POS_TOP: + case GTK_POS_BOTTOM: + if ( gtk_widget_is_ancestor(toolbox, hbox) ) { + gtk_widget_reparent( toolbox, vbox ); + gtk_box_set_child_packing(GTK_BOX(vbox), toolbox, FALSE, TRUE, 0, GTK_PACK_START); + } + ToolboxFactory::setOrientation(toolbox, GTK_ORIENTATION_HORIZONTAL); + break; + case GTK_POS_LEFT: + case GTK_POS_RIGHT: + if ( !gtk_widget_is_ancestor(toolbox, hbox) ) { + gtk_widget_reparent( toolbox, hbox ); + gtk_box_set_child_packing(GTK_BOX(hbox), toolbox, FALSE, TRUE, 0, GTK_PACK_START); + if (pos == GTK_POS_LEFT) { + gtk_box_reorder_child( GTK_BOX(hbox), toolbox, 0 ); + } + } + ToolboxFactory::setOrientation(toolbox, GTK_ORIENTATION_VERTICAL); + break; + } + } +} + + +SPViewWidget *sp_desktop_widget_new( SPNamedView *namedview ) +{ + SPDesktopWidget* dtw = SPDesktopWidget::createInstance(namedview); + + UXManager::getInstance()->addTrack(dtw); + + return SP_VIEW_WIDGET(dtw); +} + +SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview) { SPDesktopWidget *dtw = (SPDesktopWidget*)g_object_new(SP_TYPE_DESKTOP_WIDGET, NULL); @@ -1362,7 +1423,7 @@ sp_desktop_widget_new (SPNamedView *namedview) sp_view_widget_set_view (SP_VIEW_WIDGET (dtw), dtw->desktop); /* Listen on namedview modification */ - dtw->modified_connection = namedview->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_desktop_widget_namedview_modified), dtw)); + dtw->modified_connection = namedview->connectModified(sigc::mem_fun(*dtw, &SPDesktopWidget::namedviewModified)); dtw->layer_selector->setDesktop(dtw->desktop); @@ -1372,16 +1433,18 @@ sp_desktop_widget_new (SPNamedView *namedview) gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0); #endif - sp_desktop_widget_layout (dtw); + dtw->layoutWidgets(); - sp_tool_toolbox_set_desktop (dtw->tool_toolbox, dtw->desktop); - sp_aux_toolbox_set_desktop (dtw->aux_toolbox, dtw->desktop); - sp_commands_toolbox_set_desktop (dtw->commands_toolbox, dtw->desktop); - sp_snap_toolbox_set_desktop (dtw->snap_toolbox, dtw->desktop); + std::vector<GtkWidget *> toolboxes; + toolboxes.push_back(dtw->tool_toolbox); + toolboxes.push_back(dtw->aux_toolbox); + toolboxes.push_back(dtw->commands_toolbox); + toolboxes.push_back(dtw->snap_toolbox); + UXManager::getInstance()->connectToDesktop( toolboxes, dtw->desktop ); dtw->panels->setDesktop( dtw->desktop ); - return SP_VIEW_WIDGET (dtw); + return dtw; } void @@ -1436,22 +1499,22 @@ sp_desktop_widget_update_vruler (SPDesktopWidget *dtw) } -static void -sp_desktop_widget_namedview_modified (SPObject *obj, guint flags, SPDesktopWidget *dtw) +void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) { SPNamedView *nv=SP_NAMEDVIEW(obj); + if (flags & SP_OBJECT_MODIFIED_FLAG) { - dtw->dt2r = 1.0 / nv->doc_units->unittobase; - dtw->ruler_origin = Geom::Point(0,0); //nv->gridorigin; Why was the grid origin used here? + this->dt2r = 1.0 / nv->doc_units->unittobase; + this->ruler_origin = Geom::Point(0,0); //nv->gridorigin; Why was the grid origin used here? - sp_ruler_set_metric (GTK_RULER (dtw->vruler), nv->getDefaultMetric()); - sp_ruler_set_metric (GTK_RULER (dtw->hruler), nv->getDefaultMetric()); + sp_ruler_set_metric(GTK_RULER (this->vruler), nv->getDefaultMetric()); + sp_ruler_set_metric(GTK_RULER (this->hruler), nv->getDefaultMetric()); - gtk_tooltips_set_tip (dtw->tt, dtw->hruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL); - gtk_tooltips_set_tip (dtw->tt, dtw->vruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL); + gtk_tooltips_set_tip(this->tt, this->hruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL); + gtk_tooltips_set_tip(this->tt, this->vruler_box, gettext(sp_unit_get_plural (nv->doc_units)), NULL); - sp_desktop_widget_update_rulers (dtw); - update_snap_toolbox(dtw->desktop, NULL, dtw->snap_toolbox); + sp_desktop_widget_update_rulers(this); + ToolboxFactory::updateSnapToolbox(this->desktop, 0, this->snap_toolbox); } } diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h index 04146cac6..33f2a6ae7 100644 --- a/src/widgets/desktop-widget.h +++ b/src/widgets/desktop-widget.h @@ -5,6 +5,7 @@ * SPDesktopWidget: handling Gtk events on a desktop. * * Authors: + * Jon A. Cruz <jon@joncruz.org> (c) 2010 * John Bintz <jcoswell@coswellproductions.org> (c) 2006 * Ralf Stephan <ralf@ark.in-berlin.de> (c) 2005, distrib. under GPL2 * ? -2004 @@ -26,28 +27,23 @@ typedef struct _EgeColorProfTracker EgeColorProfTracker; -#define SP_TYPE_DESKTOP_WIDGET (sp_desktop_widget_get_type ()) +#define SP_TYPE_DESKTOP_WIDGET SPDesktopWidget::getType() #define SP_DESKTOP_WIDGET(o) (GTK_CHECK_CAST ((o), SP_TYPE_DESKTOP_WIDGET, SPDesktopWidget)) #define SP_DESKTOP_WIDGET_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), SP_TYPE_DESKTOP_WIDGET, SPDesktopWidgetClass)) #define SP_IS_DESKTOP_WIDGET(o) (GTK_CHECK_TYPE ((o), SP_TYPE_DESKTOP_WIDGET)) #define SP_IS_DESKTOP_WIDGET_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_DESKTOP_WIDGET)) -GtkType sp_desktop_widget_get_type(); - void sp_desktop_widget_destroy (SPDesktopWidget* dtw); void sp_desktop_widget_show_decorations(SPDesktopWidget *dtw, gboolean show); void sp_desktop_widget_iconify(SPDesktopWidget *dtw); void sp_desktop_widget_maximize(SPDesktopWidget *dtw); void sp_desktop_widget_fullscreen(SPDesktopWidget *dtw); -void sp_desktop_widget_layout(SPDesktopWidget *dtw); void sp_desktop_widget_update_zoom(SPDesktopWidget *dtw); void sp_desktop_widget_update_rulers (SPDesktopWidget *dtw); void sp_desktop_widget_update_hruler (SPDesktopWidget *dtw); void sp_desktop_widget_update_vruler (SPDesktopWidget *dtw); -void sp_desktop_widget_update_namedview (SPDesktopWidget *dtw); - /* Show/hide rulers & scrollbars */ void sp_desktop_widget_toggle_rulers (SPDesktopWidget *dtw); void sp_desktop_widget_toggle_scrollbars (SPDesktopWidget *dtw); @@ -80,14 +76,14 @@ struct SPDesktopWidget { // The root vbox of the window layout. GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *menubar, *statusbar; Inkscape::UI::Dialogs::SwatchesPanel *panels; GtkWidget *hscrollbar, *vscrollbar, *vscrollbar_box; - GtkWidget *tool_toolbox, *aux_toolbox, *commands_toolbox, *snap_toolbox; - /* Rulers */ GtkWidget *hruler, *vruler; GtkWidget *hruler_box, *vruler_box; // eventboxes for setting tooltips @@ -129,8 +125,11 @@ struct SPDesktopWidget { { _dtw->updateTitle (uri); } virtual Gtk::Window* getWindow() { return _dtw->window; } - virtual void layout() - { sp_desktop_widget_layout (_dtw); } + + virtual void layout() { + _dtw->layoutWidgets(); + } + virtual void present() { _dtw->presentWindow(); } virtual void getGeometry (gint &x, gint &y, gint &w, gint &h) @@ -221,6 +220,7 @@ struct SPDesktopWidget { void setToolboxAdjustmentValue (gchar const * id, double value); void setToolboxSelectOneValue (gchar const * id, gint value); bool isToolboxButtonActive (gchar const *id); + void setToolboxPosition(Glib::ustring const& id, GtkPositionType pos); void setCoordinateStatus(Geom::Point p); void requestCanvasUpdate(); void requestCanvasUpdateAndWait(); @@ -231,6 +231,22 @@ struct SPDesktopWidget { Inkscape::UI::Widget::Dock* getDock(); + static GtkType getType(); + static SPDesktopWidget* createInstance(SPNamedView *namedview); + + void updateNamedview(); + +private: + GtkWidget *tool_toolbox; + GtkWidget *aux_toolbox; + GtkWidget *commands_toolbox,; + GtkWidget *snap_toolbox; + + static void init(SPDesktopWidget *widget); + void layoutWidgets(); + + void namedviewModified(SPObject *obj, guint flags); + }; /// The SPDesktopWidget vtable diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp index 5e9d30bcd..3f2018b91 100644 --- a/src/widgets/fill-style.cpp +++ b/src/widgets/fill-style.cpp @@ -58,12 +58,12 @@ static void sp_fill_style_widget_change_selection ( SPWidget *spw, static void sp_fill_style_widget_update (SPWidget *spw); -static void sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel, - SPPaintSelectorMode mode, - SPWidget *spw ); -static void sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel, - SPPaintSelectorFillRule mode, - SPWidget *spw ); +static void sp_fill_style_widget_paint_mode_changed( SPPaintSelector *psel, + SPPaintSelector::Mode mode, + SPWidget *spw ); +static void sp_fill_style_widget_fillrule_changed( SPPaintSelector *psel, + SPPaintSelector::FillRule mode, + SPWidget *spw ); static void sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw ); static void sp_fill_style_widget_paint_changed (SPPaintSelector *psel, SPWidget *spw ); @@ -124,8 +124,8 @@ static void sp_fill_style_widget_construct( SPWidget *spw, SPPaintSelector */*psel*/ ) { #ifdef SP_FS_VERBOSE - g_print ( "Fill style widget constructed: inkscape %p repr %p\n", - spw->inkscape, spw->repr ); + g_print ( "Fill style widget constructed: inkscape %p\n", + spw->inkscape ); #endif if (spw->inkscape) { sp_fill_style_widget_update (spw); @@ -143,6 +143,9 @@ sp_fill_style_widget_modify_selection( SPWidget *spw, SP_OBJECT_PARENT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG) ) { +#ifdef SP_FS_VERBOSE + g_message("sp_fill_style_widget_modify_selection()"); +#endif sp_fill_style_widget_update (spw); } } @@ -184,51 +187,52 @@ sp_fill_style_widget_update (SPWidget *spw) // create temporary style SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT); // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection - int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL); + int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL); switch (result) { case QUERY_STYLE_NOTHING: { /* No paint at all */ - sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY); + psel->setMode(SPPaintSelector::MODE_EMPTY); break; } case QUERY_STYLE_SINGLE: case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector - case QUERY_STYLE_MULTIPLE_SAME: + case QUERY_STYLE_MULTIPLE_SAME: { - SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true); - sp_paint_selector_set_mode (psel, pselmode); + SPPaintSelector::Mode pselmode = SPPaintSelector::getModeForStyle(*query, true); + psel->setMode(pselmode); - sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO? - SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD); + psel->setFillrule(query->fill_rule.computed == ART_WIND_RULE_NONZERO? + SPPaintSelector::FILLRULE_NONZERO : SPPaintSelector::FILLRULE_EVENODD); if (query->fill.set && query->fill.isColor()) { - sp_paint_selector_set_color_alpha (psel, &query->fill.value.color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value)); + psel->setColorAlpha(query->fill.value.color, SP_SCALE24_TO_FLOAT(query->fill_opacity.value)); } else if (query->fill.set && query->fill.isPaintserver()) { - SPPaintServer *server = SP_STYLE_FILL_SERVER (query); + SPPaintServer *server = query->getFillPaintServer(); - if (SP_IS_LINEARGRADIENT (server)) { - SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); - sp_paint_selector_set_gradient_linear (psel, vector); + 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); - sp_paint_selector_set_gradient_properties (psel, - SP_GRADIENT_UNITS (lg), - SP_GRADIENT_SPREAD (lg)); + psel->setGradientProperties( SP_GRADIENT_UNITS(lg), + SP_GRADIENT_SPREAD(lg) ); } else if (SP_IS_RADIALGRADIENT (server)) { - SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); - sp_paint_selector_set_gradient_radial (psel, vector); + SPGradient *vector = SP_GRADIENT(server)->getVector(); + psel->setGradientRadial( vector ); SPRadialGradient *rg = SP_RADIALGRADIENT (server); - sp_paint_selector_set_gradient_properties (psel, - SP_GRADIENT_UNITS (rg), - SP_GRADIENT_SPREAD (rg)); + psel->setGradientProperties( SP_GRADIENT_UNITS (rg), + SP_GRADIENT_SPREAD (rg) ); } else if (SP_IS_PATTERN (server)) { SPPattern *pat = pattern_getroot (SP_PATTERN (server)); - sp_update_pattern_list (psel, pat); + psel->updatePatternList( pat ); } } break; @@ -236,7 +240,7 @@ sp_fill_style_widget_update (SPWidget *spw) case QUERY_STYLE_MULTIPLE_DIFFERENT: { - sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE); + psel->setMode(SPPaintSelector::MODE_MULTIPLE); break; } } @@ -249,23 +253,26 @@ sp_fill_style_widget_update (SPWidget *spw) static void -sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel, - SPPaintSelectorMode /*mode*/, - SPWidget *spw ) +sp_fill_style_widget_paint_mode_changed( SPPaintSelector *psel, + SPPaintSelector::Mode /*mode*/, + SPWidget *spw ) { if (g_object_get_data (G_OBJECT (spw), "update")) return; +#ifdef SP_FS_VERBOSE + g_message("sp_fill_style_widget_paint_mode_changed(psel:%p, mode, spw:%p)", psel, spw); +#endif + /* TODO: Does this work? */ /* TODO: Not really, here we have to get old color back from object */ /* Instead of relying on paint widget having meaningful colors set */ sp_fill_style_widget_paint_changed (psel, spw); } -static void -sp_fill_style_widget_fillrule_changed ( SPPaintSelector */*psel*/, - SPPaintSelectorFillRule mode, - SPWidget *spw ) +static void sp_fill_style_widget_fillrule_changed( SPPaintSelector */*psel*/, + SPPaintSelector::FillRule mode, + SPWidget *spw ) { if (g_object_get_data (G_OBJECT (spw), "update")) return; @@ -273,13 +280,13 @@ sp_fill_style_widget_fillrule_changed ( SPPaintSelector */*psel*/, SPDesktop *desktop = SP_ACTIVE_DESKTOP; SPCSSAttr *css = sp_repr_css_attr_new (); - sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero"); + sp_repr_css_set_property(css, "fill-rule", mode == SPPaintSelector::FILLRULE_EVENODD? "evenodd":"nonzero"); sp_desktop_set_style (desktop, css); sp_repr_css_attr_unref (css); - sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE, + sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE, _("Change fill rule")); } @@ -305,8 +312,8 @@ sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw) } if (g_object_get_data (G_OBJECT (spw), "local")) { - // previous local flag not cleared yet; - // this means dragged events come too fast, so we better skip this one to speed up display + // previous local flag not cleared yet; + // this means dragged events come too fast, so we better skip this one to speed up display // (it's safe to do this in any case) return; } @@ -315,11 +322,11 @@ sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw) switch (psel->mode) { - case SP_PAINT_SELECTOR_MODE_COLOR_RGB: - case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + case SPPaintSelector::MODE_COLOR_RGB: + case SPPaintSelector::MODE_COLOR_CMYK: { - sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity"); - sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE, + psel->setFlatColor( SP_ACTIVE_DESKTOP, "fill", "fill-opacity" ); + sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE, _("Set fill color")); g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection break; @@ -346,6 +353,9 @@ static void sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, SPWidget *spw ) { +#ifdef SP_FS_VERBOSE + g_message("sp_fill_style_widget_paint_changed(psel:%p, spw:%p)", psel, spw); +#endif if (g_object_get_data (G_OBJECT (spw), "update")) { return; } @@ -362,17 +372,17 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, switch (psel->mode) { - case SP_PAINT_SELECTOR_MODE_EMPTY: + case SPPaintSelector::MODE_EMPTY: // This should not happen. g_warning ( "file %s: line %d: Paint %d should not emit 'changed'", __FILE__, __LINE__, psel->mode); break; - case SP_PAINT_SELECTOR_MODE_MULTIPLE: + case SPPaintSelector::MODE_MULTIPLE: // This happens when you switch multiple objects with different gradients to flat color; // nothing to do here. break; - case SP_PAINT_SELECTOR_MODE_NONE: + case SPPaintSelector::MODE_NONE: { SPCSSAttr *css = sp_repr_css_attr_new (); sp_repr_css_set_property (css, "fill", "none"); @@ -381,18 +391,18 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, sp_repr_css_attr_unref (css); - sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, + sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, _("Remove fill")); break; } - case SP_PAINT_SELECTOR_MODE_COLOR_RGB: - case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + case SPPaintSelector::MODE_COLOR_RGB: + case SPPaintSelector::MODE_COLOR_CMYK: { // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(desktop), 0); - sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity"); + psel->setFlatColor( desktop, "fill", "fill-opacity" ); sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE, _("Set fill color")); // resume interruptibility @@ -407,10 +417,11 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, break; } - case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR: - case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL: + case SPPaintSelector::MODE_GRADIENT_LINEAR: + case SPPaintSelector::MODE_GRADIENT_RADIAL: + case SPPaintSelector::MODE_SWATCH: if (items) { - SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR + SPGradientType const gradient_type = ( psel->mode != SPPaintSelector::MODE_GRADIENT_RADIAL ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL ); @@ -418,7 +429,7 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property(css, "fill-opacity", "1.0"); - SPGradient *vector = sp_paint_selector_get_gradient_vector(psel); + SPGradient *vector = psel->getGradientVector(); if (!vector) { /* No vector in paint selector should mean that we just changed mode */ @@ -456,22 +467,22 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style"); SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true); - sp_gradient_selector_attrs_to_gradient (gr, psel); + psel->pushAttrsToGradient( gr ); } } sp_repr_css_attr_unref (css); - sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, + sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, _("Set gradient on fill")); } break; - case SP_PAINT_SELECTOR_MODE_PATTERN: + case SPPaintSelector::MODE_PATTERN: if (items) { - SPPattern *pattern = sp_paint_selector_get_pattern (psel); + SPPattern *pattern = psel->getPattern(); if (!pattern) { /* No Pattern in paint selector should mean that we just @@ -509,14 +520,14 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, } // end if - sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, + sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, _("Set pattern on fill")); } // end if break; - case SP_PAINT_SELECTOR_MODE_UNSET: + case SPPaintSelector::MODE_UNSET: if (items) { SPCSSAttr *css = sp_repr_css_attr_new (); sp_repr_css_unset_property (css, "fill"); @@ -524,7 +535,7 @@ sp_fill_style_widget_paint_changed ( SPPaintSelector *psel, sp_desktop_set_style (desktop, css); sp_repr_css_attr_unref (css); - sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, + sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, _("Unset fill")); } break; diff --git a/src/widgets/gradient-selector.cpp b/src/widgets/gradient-selector.cpp index f24a6781b..bc2413730 100644 --- a/src/widgets/gradient-selector.cpp +++ b/src/widgets/gradient-selector.cpp @@ -4,9 +4,11 @@ * Authors: * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2010 Jon A. Cruz * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -118,9 +120,9 @@ sp_gradient_selector_class_init (SPGradientSelectorClass *klass) static void sp_gradient_selector_init (SPGradientSelector *sel) { - GtkWidget *hb, *l, *m, *mi; + GtkWidget *hb, *m, *mi; - sel->mode = SP_GRADIENT_SELECTOR_MODE_LINEAR; + sel->mode = SPGradientSelector::MODE_LINEAR; sel->gradientUnits = SP_GRADIENT_UNITS_USERSPACEONUSE; sel->gradientSpread = SP_GRADIENT_SPREAD_PAD; @@ -180,9 +182,9 @@ sp_gradient_selector_init (SPGradientSelector *sel) gtk_option_menu_set_menu (GTK_OPTION_MENU (sel->spread), m); - l = gtk_label_new (_("Repeat:")); - gtk_widget_show (l); - gtk_box_pack_end (GTK_BOX (hb), l, FALSE, FALSE, 4); + sel->spreadLbl = gtk_label_new (_("Repeat:")); + gtk_widget_show(sel->spreadLbl); + gtk_box_pack_end(GTK_BOX(hb), sel->spreadLbl, FALSE, FALSE, 4); } static void @@ -206,77 +208,78 @@ sp_gradient_selector_new (void) return (GtkWidget *) sel; } -void -sp_gradient_selector_set_mode (SPGradientSelector *sel, guint mode) +void SPGradientSelector::setMode(SelectorMode mode) { - g_return_if_fail (sel != NULL); - g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel)); - - sel->mode = mode; + if (mode != this->mode) { + this->mode = mode; + if (mode == MODE_SWATCH) { + if (spread) { + GtkWidget *parent = gtk_widget_get_parent(spread); + if (parent) { + gtk_container_remove(GTK_CONTAINER(parent), spread); + spread = 0; + } + } + if (spreadLbl) { + GtkWidget *parent = gtk_widget_get_parent(spreadLbl); + if (parent) { + gtk_container_remove(GTK_CONTAINER(parent), spreadLbl); + spreadLbl = 0; + } + } + + SPGradientVectorSelector* vs = SP_GRADIENT_VECTOR_SELECTOR(vectors); + vs->setSwatched(); + } + } } -void -sp_gradient_selector_set_units (SPGradientSelector *sel, guint units) +void SPGradientSelector::setUnits(SPGradientUnits units) { - g_return_if_fail (sel != NULL); - g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel)); - - sel->gradientUnits = (SPGradientUnits)units; + gradientUnits = units; } -void -sp_gradient_selector_set_spread (SPGradientSelector *sel, guint spread) +void SPGradientSelector::setSpread(SPGradientSpread spread) { - g_return_if_fail (sel != NULL); - g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel)); + gradientSpread = spread; - sel->gradientSpread = (SPGradientSpread)spread; - - gtk_option_menu_set_history (GTK_OPTION_MENU (sel->spread), sel->gradientSpread); + gtk_option_menu_set_history(GTK_OPTION_MENU(this->spread), gradientSpread); } -SPGradientUnits -sp_gradient_selector_get_units (SPGradientSelector *sel) +SPGradientUnits SPGradientSelector::getUnits() { - return (SPGradientUnits) sel->gradientUnits; + return gradientUnits; } -SPGradientSpread -sp_gradient_selector_get_spread (SPGradientSelector *sel) +SPGradientSpread SPGradientSelector::getSpread() { - return (SPGradientSpread) sel->gradientSpread; + return gradientSpread; } -void -sp_gradient_selector_set_vector (SPGradientSelector *sel, SPDocument *doc, SPGradient *vector) +void SPGradientSelector::setVector(SPDocument *doc, SPGradient *vector) { - g_return_if_fail (sel != NULL); - g_return_if_fail (SP_IS_GRADIENT_SELECTOR (sel)); - g_return_if_fail (!vector || SP_IS_GRADIENT (vector)); - g_return_if_fail (!vector || (SP_OBJECT_DOCUMENT (vector) == doc)); + g_return_if_fail(!vector || SP_IS_GRADIENT(vector)); + g_return_if_fail(!vector || (SP_OBJECT_DOCUMENT(vector) == doc)); - if (vector && !SP_GRADIENT_HAS_STOPS (vector)) + if (vector && !SP_GRADIENT_HAS_STOPS(vector)) { return; + } - sp_gradient_vector_selector_set_gradient (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors), doc, vector); + sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(vectors), doc, vector); if (vector) { - gtk_widget_set_sensitive (sel->edit, TRUE); - gtk_widget_set_sensitive (sel->add, TRUE); + gtk_widget_set_sensitive(edit, TRUE); + gtk_widget_set_sensitive(add, TRUE); } else { - gtk_widget_set_sensitive (sel->edit, FALSE); - gtk_widget_set_sensitive (sel->add, (doc != NULL)); + gtk_widget_set_sensitive(edit, FALSE); + gtk_widget_set_sensitive(add, (doc != NULL)); } } -SPGradient * -sp_gradient_selector_get_vector (SPGradientSelector *sel) +SPGradient *SPGradientSelector::getVector() { - if (sel == NULL || !SP_IS_GRADIENT_SELECTOR (sel)) - return NULL; - /* fixme: */ - return SP_GRADIENT_VECTOR_SELECTOR (sel->vectors)->gr; + return SP_GRADIENT_VECTOR_SELECTOR(vectors)->gr; } static void @@ -287,7 +290,7 @@ sp_gradient_selector_vector_set (SPGradientVectorSelector */*gvs*/, SPGradient * if (!blocked) { blocked = TRUE; gr = sp_gradient_ensure_vector_normalized (gr); - sp_gradient_selector_set_vector (sel, (gr) ? SP_OBJECT_DOCUMENT (gr) : NULL, gr); + sel->setVector((gr) ? SP_OBJECT_DOCUMENT (gr) : 0, gr); g_signal_emit (G_OBJECT (sel), signals[CHANGED], 0, gr); blocked = FALSE; } diff --git a/src/widgets/gradient-selector.h b/src/widgets/gradient-selector.h index e68dfecfc..25f561a6e 100644 --- a/src/widgets/gradient-selector.h +++ b/src/widgets/gradient-selector.h @@ -6,16 +6,21 @@ * * Authors: * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2010 Jon A. Cruz * * Released under GNU GPL, read the file 'COPYING' for more information */ #include <glib.h> +#include <gtk/gtkvbox.h> +#include "sp-gradient.h" #include "sp-gradient-spread.h" #include "sp-gradient-units.h" + class SPGradient; #define SP_TYPE_GRADIENT_SELECTOR (sp_gradient_selector_get_type ()) @@ -24,32 +29,43 @@ class SPGradient; #define SP_IS_GRADIENT_SELECTOR(o) (GTK_CHECK_TYPE ((o), SP_TYPE_GRADIENT_SELECTOR)) #define SP_IS_GRADIENT_SELECTOR_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), SP_TYPE_GRADIENT_SELECTOR)) -#include <libnr/nr-forward.h> -#include <gtk/gtkvbox.h> -#include "../forward.h" -#include <sp-gradient.h> -enum { - SP_GRADIENT_SELECTOR_MODE_LINEAR, - SP_GRADIENT_SELECTOR_MODE_RADIAL -}; struct SPGradientSelector { GtkVBox vbox; - guint mode : 1; + enum SelectorMode { + MODE_LINEAR, + MODE_RADIAL, + MODE_SWATCH + }; + + SelectorMode mode; - SPGradientUnits gradientUnits : 1; - SPGradientSpread gradientSpread : 2; + SPGradientUnits gradientUnits; + SPGradientSpread gradientSpread; /* Vector selector */ GtkWidget *vectors; + /* Editing buttons */ - GtkWidget *edit, *add; + GtkWidget *edit; + GtkWidget *add; + /* Position widget */ GtkWidget *position; + /* Spread selector */ GtkWidget *spread; + GtkWidget *spreadLbl; + + void setMode(SelectorMode mode); + void setUnits(SPGradientUnits units); + void setSpread(SPGradientSpread spread); + void setVector(SPDocument *doc, SPGradient *vector); + SPGradientUnits getUnits(); + SPGradientSpread getSpread(); + SPGradient *getVector(); }; struct SPGradientSelectorClass { @@ -65,17 +81,8 @@ GType sp_gradient_selector_get_type(void); GtkWidget *sp_gradient_selector_new (void); -void sp_gradient_selector_set_mode (SPGradientSelector *sel, guint mode); -void sp_gradient_selector_set_units (SPGradientSelector *sel, guint units); -void sp_gradient_selector_set_spread (SPGradientSelector *sel, guint spread); -void sp_gradient_selector_set_vector (SPGradientSelector *sel, SPDocument *doc, SPGradient *vector); void sp_gradient_selector_set_bbox (SPGradientSelector *sel, gdouble x0, gdouble y0, gdouble x1, gdouble y1); -SPGradientUnits sp_gradient_selector_get_units (SPGradientSelector *sel); -SPGradientSpread sp_gradient_selector_get_spread (SPGradientSelector *sel); - -SPGradient *sp_gradient_selector_get_vector (SPGradientSelector *sel); - #endif // SEEN_GRADIENT_SELECTOR_H diff --git a/src/widgets/gradient-toolbar.cpp b/src/widgets/gradient-toolbar.cpp index ddd9fd96a..077e038e7 100644 --- a/src/widgets/gradient-toolbar.cpp +++ b/src/widgets/gradient-toolbar.cpp @@ -164,8 +164,7 @@ gr_prepare_label (SPObject *obj) return g_strdup_printf ("<small>%s</small>", id); } -GtkWidget * -gr_vector_list (SPDesktop *desktop, bool selection_empty, SPGradient *gr_selected, bool gr_multi) +GtkWidget *gr_vector_list(SPDesktop *desktop, bool selection_empty, SPGradient *gr_selected, bool gr_multi) { SPDocument *document = sp_desktop_document (desktop); @@ -175,7 +174,8 @@ gr_vector_list (SPDesktop *desktop, bool selection_empty, SPGradient *gr_selecte GSList *gl = NULL; const GSList *gradients = sp_document_get_resource_list (document, "gradient"); for (const GSList *i = gradients; i != NULL; i = i->next) { - if (SP_GRADIENT_HAS_STOPS (i->data)) { + SPGradient *grad = SP_GRADIENT(i->data); + if (SP_GRADIENT_HAS_STOPS(grad) && !grad->isSolid()) { gl = g_slist_prepend (gl, i->data); } } @@ -185,6 +185,7 @@ gr_vector_list (SPDesktop *desktop, bool selection_empty, SPGradient *gr_selecte guint idx = 0; if (!gl) { + // The document has no gradients GtkWidget *l = gtk_label_new(""); gtk_label_set_markup (GTK_LABEL(l), _("<small>No gradients</small>")); GtkWidget *i = gtk_menu_item_new (); @@ -194,6 +195,7 @@ gr_vector_list (SPDesktop *desktop, bool selection_empty, SPGradient *gr_selecte gtk_menu_append (GTK_MENU (m), i); gtk_widget_set_sensitive (om, FALSE); } else if (selection_empty) { + // Document has gradients, but nothing is currently selected. GtkWidget *l = gtk_label_new(""); gtk_label_set_markup (GTK_LABEL(l), _("<small>Nothing selected</small>")); GtkWidget *i = gtk_menu_item_new (); @@ -268,29 +270,37 @@ gr_vector_list (SPDesktop *desktop, bool selection_empty, SPGradient *gr_selecte } -void -gr_read_selection (Inkscape::Selection *selection, GrDrag *drag, SPGradient **gr_selected, bool *gr_multi, SPGradientSpread *spr_selected, bool *spr_multi) +void gr_read_selection( Inkscape::Selection *selection, + GrDrag *drag, + SPGradient *&gr_selected, + bool &gr_multi, + SPGradientSpread &spr_selected, + bool &spr_multi ) { if (drag && drag->selected) { // GRADIENTFIXME: make this work for more than one selected dragger? - GrDragger *dragger = (GrDragger*) drag->selected->data; - for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger - GrDraggable *draggable = (GrDraggable *) i->data; - SPGradient *gradient = sp_item_gradient_get_vector (draggable->item, draggable->fill_or_stroke); - SPGradientSpread spread = sp_item_gradient_get_spread (draggable->item, draggable->fill_or_stroke); + GrDragger *dragger = static_cast<GrDragger*>(drag->selected->data); + for (GSList const* i = dragger->draggables; i; i = i->next) { // for all draggables of dragger + GrDraggable *draggable = static_cast<GrDraggable *>(i->data); + SPGradient *gradient = sp_item_gradient_get_vector(draggable->item, draggable->fill_or_stroke); + SPGradientSpread spread = sp_item_gradient_get_spread(draggable->item, draggable->fill_or_stroke); + + if (gradient && gradient->isSolid()) { + gradient = 0; + } - if (gradient != *gr_selected) { - if (*gr_selected != NULL) { - *gr_multi = true; + if (gradient && (gradient != gr_selected)) { + if (gr_selected) { + gr_multi = true; } else { - *gr_selected = gradient; + gr_selected = gradient; } } - if (spread != *spr_selected) { - if (*spr_selected != INT_MAX) { - *spr_multi = true; + if (spread != spr_selected) { + if (spr_selected != INT_MAX) { + spr_multi = true; } else { - *spr_selected = spread; + spr_selected = spread; } } } @@ -298,48 +308,59 @@ gr_read_selection (Inkscape::Selection *selection, GrDrag *drag, SPGradient **gr } // If no selected dragger, read desktop selection - for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { + for (GSList const* i = selection->itemList(); i; i = i->next) { SPItem *item = SP_ITEM(i->data); SPStyle *style = SP_OBJECT_STYLE (item); if (style && (style->fill.isPaintserver())) { SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (item); - if (SP_IS_GRADIENT (server)) { - SPGradient *gradient = sp_gradient_get_vector (SP_GRADIENT (server), false); + if (SP_IS_GRADIENT(server)) { + SPGradient *gradient = SP_GRADIENT(server)->getVector(); SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server)); - if (gradient != *gr_selected) { - if (*gr_selected != NULL) { - *gr_multi = true; + + if (gradient && gradient->isSolid()) { + gradient = 0; + } + + if (gradient && (gradient != gr_selected)) { + if (gr_selected) { + gr_multi = true; } else { - *gr_selected = gradient; + gr_selected = gradient; } } - if (spread != *spr_selected) { - if (*spr_selected != INT_MAX) { - *spr_multi = true; + if (spread != spr_selected) { + if (spr_selected != INT_MAX) { + spr_multi = true; } else { - *spr_selected = spread; + spr_selected = spread; } } } } if (style && (style->stroke.isPaintserver())) { SPObject *server = SP_OBJECT_STYLE_STROKE_SERVER (item); - if (SP_IS_GRADIENT (server)) { - SPGradient *gradient = sp_gradient_get_vector (SP_GRADIENT (server), false); + if (SP_IS_GRADIENT(server)) { + SPGradient *gradient = SP_GRADIENT(server)->getVector(); SPGradientSpread spread = sp_gradient_get_spread (SP_GRADIENT (server)); - if (gradient != *gr_selected) { - if (*gr_selected != NULL) { - *gr_multi = true; + + if (gradient && gradient->isSolid()) { + gradient = 0; + + } + + if (gradient && (gradient != gr_selected)) { + if (gr_selected) { + gr_multi = true; } else { - *gr_selected = gradient; + gr_selected = gradient; } } - if (spread != *spr_selected) { - if (*spr_selected != INT_MAX) { - *spr_multi = true; + if (spread != spr_selected) { + if (spr_selected != INT_MAX) { + spr_multi = true; } else { - *spr_selected = spread; + spr_selected = spread; } } } @@ -347,41 +368,41 @@ gr_read_selection (Inkscape::Selection *selection, GrDrag *drag, SPGradient **gr } } -static void -gr_tb_selection_changed (Inkscape::Selection *, gpointer data) +static void gr_tb_selection_changed(Inkscape::Selection * /*selection*/, gpointer data) { - GtkWidget *widget = (GtkWidget *) data; - - SPDesktop *desktop = (SPDesktop *) g_object_get_data (G_OBJECT(widget), "desktop"); - if (!desktop) - return; + GtkWidget *widget = GTK_WIDGET(data); + + SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(G_OBJECT(widget), "desktop")); + if (desktop) { + Inkscape::Selection *selection = sp_desktop_selection(desktop); // take from desktop, not from args + if (selection) { + SPEventContext *ev = sp_desktop_event_context(desktop); + + GtkWidget *om = (GtkWidget *) g_object_get_data(G_OBJECT(widget), "menu"); + if (om) { + gtk_widget_destroy(om); + om = 0; + } - Inkscape::Selection *selection = sp_desktop_selection (desktop); // take from desktop, not from args - if (!selection) - return; + SPGradient *gr_selected = 0; + bool gr_multi = false; - SPEventContext *ev = sp_desktop_event_context (desktop); + SPGradientSpread spr_selected = static_cast<SPGradientSpread>(INT_MAX); // meaning undefined + bool spr_multi = false; - GtkWidget *om = (GtkWidget *) g_object_get_data (G_OBJECT (widget), "menu"); - if (om) gtk_widget_destroy (om); + gr_read_selection(selection, ev ? ev->get_drag() : 0, gr_selected, gr_multi, spr_selected, spr_multi); - SPGradient *gr_selected = NULL; - bool gr_multi = false; + om = gr_vector_list(desktop, selection->isEmpty(), gr_selected, gr_multi); + g_object_set_data(G_OBJECT(widget), "menu", om); - SPGradientSpread spr_selected = (SPGradientSpread) INT_MAX; // meaning undefined - bool spr_multi = false; + GtkWidget *buttons = (GtkWidget *) g_object_get_data(G_OBJECT(widget), "buttons"); + gtk_widget_set_sensitive(buttons, (gr_selected && !gr_multi)); - gr_read_selection (selection, ev? ev->get_drag() : NULL, &gr_selected, &gr_multi, &spr_selected, &spr_multi); - - om = gr_vector_list (desktop, selection->isEmpty(), gr_selected, gr_multi); - g_object_set_data (G_OBJECT (widget), "menu", om); + gtk_box_pack_start(GTK_BOX(widget), om, TRUE, TRUE, 0); - GtkWidget *buttons = (GtkWidget *) g_object_get_data (G_OBJECT(widget), "buttons"); - gtk_widget_set_sensitive (buttons, (gr_selected && !gr_multi)); - - gtk_box_pack_start (GTK_BOX (widget), om, TRUE, TRUE, 0); - - gtk_widget_show_all (widget); + gtk_widget_show_all(widget); + } + } } static void @@ -431,8 +452,7 @@ gr_edit (GtkWidget */*button*/, GtkWidget *widget) } } -GtkWidget * -gr_change_widget (SPDesktop *desktop) +GtkWidget * gr_change_widget(SPDesktop *desktop) { Inkscape::Selection *selection = sp_desktop_selection (desktop); SPDocument *document = sp_desktop_document (desktop); @@ -446,7 +466,7 @@ gr_change_widget (SPDesktop *desktop) GtkTooltips *tt = gtk_tooltips_new(); - gr_read_selection (selection, ev? ev->get_drag() : NULL, &gr_selected, &gr_multi, &spr_selected, &spr_multi); + gr_read_selection (selection, ev? ev->get_drag() : 0, gr_selected, gr_multi, spr_selected, spr_multi); GtkWidget *widget = gtk_hbox_new(FALSE, FALSE); gtk_object_set_data(GTK_OBJECT(widget), "dtw", desktop->canvas); diff --git a/src/widgets/gradient-vector.cpp b/src/widgets/gradient-vector.cpp index c884604a2..7bfe27310 100644 --- a/src/widgets/gradient-vector.cpp +++ b/src/widgets/gradient-vector.cpp @@ -5,12 +5,14 @@ * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> * MenTaLguY <mental@rydia.net> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. * Copyright (C) 2004 Monash University * Copyright (C) 2004 David Turner * Copyright (C) 2006 MenTaLguY + * Copyright (C) 2010 Jon A. Cruz * * Released under GNU GPL, read the file 'COPYING' for more information * @@ -46,16 +48,16 @@ enum { LAST_SIGNAL }; -static void sp_gradient_vector_selector_class_init (SPGradientVectorSelectorClass *klass); -static void sp_gradient_vector_selector_init (SPGradientVectorSelector *gvs); -static void sp_gradient_vector_selector_destroy (GtkObject *object); +static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass); +static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs); +static void sp_gradient_vector_selector_destroy(GtkObject *object); -static void sp_gvs_gradient_release (SPObject *obj, SPGradientVectorSelector *gvs); -static void sp_gvs_defs_release (SPObject *defs, SPGradientVectorSelector *gvs); -static void sp_gvs_defs_modified (SPObject *defs, guint flags, SPGradientVectorSelector *gvs); +static void sp_gvs_gradient_release(SPObject *obj, SPGradientVectorSelector *gvs); +static void sp_gvs_defs_release(SPObject *defs, SPGradientVectorSelector *gvs); +static void sp_gvs_defs_modified(SPObject *defs, guint flags, SPGradientVectorSelector *gvs); -static void sp_gvs_rebuild_gui_full (SPGradientVectorSelector *gvs); -static void sp_gvs_gradient_activate (GtkMenuItem *mi, SPGradientVectorSelector *gvs); +static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs); +static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector *gvs); static GtkVBoxClass *parent_class; static guint signals[LAST_SIGNAL] = {0}; @@ -74,36 +76,35 @@ GType sp_gradient_vector_selector_get_type(void) sizeof(SPGradientVectorSelectorClass), NULL, /* base_init */ NULL, /* base_finalize */ - (GClassInitFunc) sp_gradient_vector_selector_class_init, + reinterpret_cast<GClassInitFunc>(sp_gradient_vector_selector_class_init), NULL, /* class_finalize */ NULL, /* class_data */ sizeof(SPGradientVectorSelector), 0, /* n_preallocs */ - (GInstanceInitFunc) sp_gradient_vector_selector_init, + reinterpret_cast<GInstanceInitFunc>(sp_gradient_vector_selector_init), 0, /* value_table */ }; type = g_type_register_static( GTK_TYPE_VBOX, "SPGradientVectorSelector", &info, - static_cast< GTypeFlags > (0) ); + static_cast< GTypeFlags >(0) ); } return type; } -static void -sp_gradient_vector_selector_class_init (SPGradientVectorSelectorClass *klass) +static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass) { GtkObjectClass *object_class; - object_class = GTK_OBJECT_CLASS (klass); + object_class = GTK_OBJECT_CLASS(klass); - parent_class = (GtkVBoxClass*)gtk_type_class (GTK_TYPE_VBOX); + parent_class = static_cast<GtkVBoxClass*>(gtk_type_class(GTK_TYPE_VBOX)); - signals[VECTOR_SET] = gtk_signal_new ("vector_set", + signals[VECTOR_SET] = gtk_signal_new( "vector_set", GTK_RUN_LAST, GTK_CLASS_TYPE(object_class), - GTK_SIGNAL_OFFSET (SPGradientVectorSelectorClass, vector_set), + GTK_SIGNAL_OFFSET(SPGradientVectorSelectorClass, vector_set), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); @@ -111,11 +112,12 @@ sp_gradient_vector_selector_class_init (SPGradientVectorSelectorClass *klass) object_class->destroy = sp_gradient_vector_selector_destroy; } -static void -sp_gradient_vector_selector_init (SPGradientVectorSelector *gvs) +static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs) { gvs->idlabel = TRUE; + gvs->swatched = false; + gvs->doc = NULL; gvs->gr = NULL; @@ -123,17 +125,14 @@ sp_gradient_vector_selector_init (SPGradientVectorSelector *gvs) new (&gvs->defs_release_connection) sigc::connection(); new (&gvs->defs_modified_connection) sigc::connection(); - gvs->menu = gtk_option_menu_new (); - gtk_widget_show (gvs->menu); - gtk_box_pack_start (GTK_BOX (gvs), gvs->menu, TRUE, TRUE, 0); + gvs->menu = gtk_option_menu_new(); + gtk_widget_show(gvs->menu); + gtk_box_pack_start(GTK_BOX(gvs), gvs->menu, TRUE, TRUE, 0); } -static void -sp_gradient_vector_selector_destroy (GtkObject *object) +static void sp_gradient_vector_selector_destroy(GtkObject *object) { - SPGradientVectorSelector *gvs; - - gvs = SP_GRADIENT_VECTOR_SELECTOR (object); + SPGradientVectorSelector *gvs = SP_GRADIENT_VECTOR_SELECTOR(object); if (gvs->gr) { gvs->gradient_release_connection.disconnect(); @@ -150,40 +149,39 @@ sp_gradient_vector_selector_destroy (GtkObject *object) gvs->defs_release_connection.~connection(); gvs->defs_modified_connection.~connection(); - if (((GtkObjectClass *) (parent_class))->destroy) - (* ((GtkObjectClass *) (parent_class))->destroy) (object); + if ((reinterpret_cast<GtkObjectClass *>(parent_class))->destroy) { + (* (reinterpret_cast<GtkObjectClass *>(parent_class))->destroy) (object); + } } -GtkWidget * -sp_gradient_vector_selector_new (SPDocument *doc, SPGradient *gr) +GtkWidget *sp_gradient_vector_selector_new(SPDocument *doc, SPGradient *gr) { GtkWidget *gvs; - g_return_val_if_fail (!gr || SP_IS_GRADIENT (gr), NULL); - g_return_val_if_fail (!gr || (SP_OBJECT_DOCUMENT (gr) == doc), NULL); + g_return_val_if_fail(!gr || SP_IS_GRADIENT(gr), NULL); + g_return_val_if_fail(!gr || (SP_OBJECT_DOCUMENT(gr) == doc), NULL); - gvs = (GtkWidget*)gtk_type_new (SP_TYPE_GRADIENT_VECTOR_SELECTOR); + gvs = static_cast<GtkWidget*>(gtk_type_new(SP_TYPE_GRADIENT_VECTOR_SELECTOR)); if (doc) { - sp_gradient_vector_selector_set_gradient (SP_GRADIENT_VECTOR_SELECTOR (gvs), doc, gr); + sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(gvs), doc, gr); } else { - sp_gvs_rebuild_gui_full (SP_GRADIENT_VECTOR_SELECTOR (gvs)); + sp_gvs_rebuild_gui_full(SP_GRADIENT_VECTOR_SELECTOR(gvs)); } return gvs; } -void -sp_gradient_vector_selector_set_gradient (SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr) +void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr) { static gboolean suppress = FALSE; - g_return_if_fail (gvs != NULL); - g_return_if_fail (SP_IS_GRADIENT_VECTOR_SELECTOR (gvs)); - g_return_if_fail (!gr || (doc != NULL)); - g_return_if_fail (!gr || SP_IS_GRADIENT (gr)); - g_return_if_fail (!gr || (SP_OBJECT_DOCUMENT (gr) == doc)); - g_return_if_fail (!gr || SP_GRADIENT_HAS_STOPS (gr)); + g_return_if_fail(gvs != NULL); + g_return_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs)); + g_return_if_fail(!gr || (doc != NULL)); + g_return_if_fail(!gr || SP_IS_GRADIENT(gr)); + g_return_if_fail(!gr || (SP_OBJECT_DOCUMENT(gr) == doc)); + g_return_if_fail(!gr || SP_GRADIENT_HAS_STOPS(gr)); if (doc != gvs->doc) { /* Disconnect signals */ @@ -196,7 +194,8 @@ sp_gradient_vector_selector_set_gradient (SPGradientVectorSelector *gvs, SPDocum gvs->defs_modified_connection.disconnect(); gvs->doc = NULL; } - /* Connect signals */ + + // Connect signals if (doc) { gvs->defs_release_connection = SP_DOCUMENT_DEFS(doc)->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_defs_release), gvs)); gvs->defs_modified_connection = SP_DOCUMENT_DEFS(doc)->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gvs_defs_modified), gvs)); @@ -206,145 +205,144 @@ sp_gradient_vector_selector_set_gradient (SPGradientVectorSelector *gvs, SPDocum } gvs->doc = doc; gvs->gr = gr; - sp_gvs_rebuild_gui_full (gvs); - if (!suppress) g_signal_emit (G_OBJECT (gvs), signals[VECTOR_SET], 0, gr); + sp_gvs_rebuild_gui_full(gvs); + if (!suppress) g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr); } else if (gr != gvs->gr) { - /* Harder case - keep document, rebuild menus and stuff */ - /* fixme: (Lauris) */ + // Harder case - keep document, rebuild menus and stuff + // fixme: (Lauris) suppress = TRUE; - sp_gradient_vector_selector_set_gradient (gvs, NULL, NULL); - sp_gradient_vector_selector_set_gradient (gvs, doc, gr); + sp_gradient_vector_selector_set_gradient(gvs, NULL, NULL); + sp_gradient_vector_selector_set_gradient(gvs, doc, gr); suppress = FALSE; - g_signal_emit (G_OBJECT (gvs), signals[VECTOR_SET], 0, gr); + g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr); } /* The case of setting NULL -> NULL is not very interesting */ } -SPDocument * -sp_gradient_vector_selector_get_document (SPGradientVectorSelector *gvs) +SPDocument *sp_gradient_vector_selector_get_document(SPGradientVectorSelector *gvs) { - g_return_val_if_fail (gvs != NULL, NULL); - g_return_val_if_fail (SP_IS_GRADIENT_VECTOR_SELECTOR (gvs), NULL); + g_return_val_if_fail(gvs != NULL, NULL); + g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL); return gvs->doc; } -SPGradient * -sp_gradient_vector_selector_get_gradient (SPGradientVectorSelector *gvs) +SPGradient *sp_gradient_vector_selector_get_gradient(SPGradientVectorSelector *gvs) { - g_return_val_if_fail (gvs != NULL, NULL); - g_return_val_if_fail (SP_IS_GRADIENT_VECTOR_SELECTOR (gvs), NULL); + g_return_val_if_fail(gvs != NULL, NULL); + g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL); return gvs->gr; } -static void -sp_gvs_rebuild_gui_full (SPGradientVectorSelector *gvs) +static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs) { /* Clear old menu, if there is any */ - if (gtk_option_menu_get_menu (GTK_OPTION_MENU (gvs->menu))) { - gtk_option_menu_remove_menu (GTK_OPTION_MENU (gvs->menu)); + if (gtk_option_menu_get_menu(GTK_OPTION_MENU(gvs->menu))) { + gtk_option_menu_remove_menu(GTK_OPTION_MENU(gvs->menu)); } /* Create new menu widget */ - GtkWidget *m = gtk_menu_new (); - gtk_widget_show (m); + GtkWidget *m = gtk_menu_new(); + gtk_widget_show(m); /* Pick up all gradients with vectors */ GSList *gl = NULL; if (gvs->gr) { - const GSList *gradients = sp_document_get_resource_list (SP_OBJECT_DOCUMENT (gvs->gr), "gradient"); - for (const GSList *l = gradients; l != NULL; l = l->next) { - if (SP_GRADIENT_HAS_STOPS (l->data)) { - gl = g_slist_prepend (gl, l->data); + const GSList *gradients = sp_document_get_resource_list(SP_OBJECT_DOCUMENT(gvs->gr), "gradient"); + for (const GSList *curr = gradients; curr; curr = curr->next) { + SPGradient* grad = SP_GRADIENT(curr->data); + if (SP_GRADIENT_HAS_STOPS(grad) && (grad->isSwatch() == gvs->swatched)) { + gl = g_slist_prepend(gl, curr->data); } } } - gl = g_slist_reverse (gl); + gl = g_slist_reverse(gl); gint pos = 0; gint idx = 0; if (!gvs->doc) { GtkWidget *i; - i = gtk_menu_item_new_with_label (_("No document selected")); - gtk_widget_show (i); - gtk_menu_append (GTK_MENU (m), i); - gtk_widget_set_sensitive (gvs->menu, FALSE); + i = gtk_menu_item_new_with_label(_("No document selected")); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + gtk_widget_set_sensitive(gvs->menu, FALSE); } else if (!gl) { GtkWidget *i; - i = gtk_menu_item_new_with_label (_("No gradients in document")); - gtk_widget_show (i); - gtk_menu_append (GTK_MENU (m), i); - gtk_widget_set_sensitive (gvs->menu, FALSE); + i = gtk_menu_item_new_with_label(_("No gradients in document")); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + gtk_widget_set_sensitive(gvs->menu, FALSE); } else if (!gvs->gr) { GtkWidget *i; - i = gtk_menu_item_new_with_label (_("No gradient selected")); - gtk_widget_show (i); - gtk_menu_append (GTK_MENU (m), i); - gtk_widget_set_sensitive (gvs->menu, FALSE); + i = gtk_menu_item_new_with_label(_("No gradient selected")); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + gtk_widget_set_sensitive(gvs->menu, FALSE); } else { while (gl) { SPGradient *gr; GtkWidget *i, *w; - gr = SP_GRADIENT (gl->data); - gl = g_slist_remove (gl, gr); + gr = SP_GRADIENT(gl->data); + gl = g_slist_remove(gl, gr); /* We have to know: */ /* Gradient destroy */ /* Gradient name change */ - i = gtk_menu_item_new (); - gtk_widget_show (i); - g_object_set_data (G_OBJECT (i), "gradient", gr); - g_signal_connect (G_OBJECT (i), "activate", G_CALLBACK (sp_gvs_gradient_activate), gvs); + i = gtk_menu_item_new(); + gtk_widget_show(i); + g_object_set_data(G_OBJECT(i), "gradient", gr); + g_signal_connect(G_OBJECT(i), "activate", G_CALLBACK(sp_gvs_gradient_activate), gvs); - w = sp_gradient_image_new (gr); - gtk_widget_show (w); + w = sp_gradient_image_new(gr); + gtk_widget_show(w); if (gvs->idlabel) { GtkWidget *hb, *l; - hb = gtk_hbox_new (FALSE, 4); - gtk_widget_show (hb); - l = gtk_label_new (SP_OBJECT_ID (gr)); - gtk_widget_show (l); - gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); - gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (hb), w, FALSE, FALSE, 0); + hb = gtk_hbox_new(FALSE, 4); + gtk_widget_show(hb); + l = gtk_label_new(gr->getId()); + gtk_widget_show(l); + gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5); + gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hb), w, FALSE, FALSE, 0); w = hb; } - gtk_container_add (GTK_CONTAINER (i), w); + gtk_container_add(GTK_CONTAINER(i), w); - gtk_menu_append (GTK_MENU (m), i); + gtk_menu_append(GTK_MENU(m), i); - if (gr == gvs->gr) pos = idx; + if (gr == gvs->gr) { + pos = idx; + } idx += 1; } - gtk_widget_set_sensitive (gvs->menu, TRUE); + gtk_widget_set_sensitive(gvs->menu, TRUE); } - gtk_option_menu_set_menu (GTK_OPTION_MENU (gvs->menu), m); + gtk_option_menu_set_menu(GTK_OPTION_MENU(gvs->menu), m); /* Set history */ - gtk_option_menu_set_history (GTK_OPTION_MENU (gvs->menu), pos); + gtk_option_menu_set_history(GTK_OPTION_MENU(gvs->menu), pos); } -static void -sp_gvs_gradient_activate (GtkMenuItem *mi, SPGradientVectorSelector *gvs) +static void sp_gvs_gradient_activate(GtkMenuItem *mi, SPGradientVectorSelector *gvs) { SPGradient *gr, *norm; - gr = (SPGradient*)g_object_get_data (G_OBJECT (mi), "gradient"); + gr = (SPGradient*)g_object_get_data(G_OBJECT(mi), "gradient"); /* Hmmm... bad things may happen here, if actual gradient is something new */ /* Namely - menuitems etc. will be fucked up */ /* Hmmm - probably we can just re-set it as menuitem data (Lauris) */ - //g_print ("SPGradientVectorSelector: gradient %s activated\n", SP_OBJECT_ID (gr)); + //g_print("SPGradientVectorSelector: gradient %s activated\n", SP_OBJECT_ID(gr)); - norm = sp_gradient_ensure_vector_normalized (gr); + norm = sp_gradient_ensure_vector_normalized(gr); if (norm != gr) { - //g_print ("SPGradientVectorSelector: become %s after normalization\n", SP_OBJECT_ID (norm)); + //g_print("SPGradientVectorSelector: become %s after normalization\n", SP_OBJECT_ID(norm)); /* But be careful that we do not have gradient saved anywhere else */ - g_object_set_data (G_OBJECT (mi), "gradient", norm); + g_object_set_data(G_OBJECT(mi), "gradient", norm); } /* fixme: Really we would want to use _set_vector */ @@ -359,19 +357,18 @@ sp_gvs_gradient_activate (GtkMenuItem *mi, SPGradientVectorSelector *gvs) gvs->gr = norm; } - g_signal_emit (G_OBJECT (gvs), signals[VECTOR_SET], 0, norm); + g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, norm); if (norm != gr) { /* We do extra undo push here */ /* If handler has already done it, it is just NOP */ // FIXME: looks like this is never a valid undo step, consider removing this - sp_document_done (SP_OBJECT_DOCUMENT (norm), SP_VERB_CONTEXT_GRADIENT, + sp_document_done(SP_OBJECT_DOCUMENT(norm), SP_VERB_CONTEXT_GRADIENT, /* TODO: annotate */ "gradient-vector.cpp:350"); } } -static void -sp_gvs_gradient_release (SPObject */*obj*/, SPGradientVectorSelector *gvs) +static void sp_gvs_gradient_release(SPObject */*obj*/, SPGradientVectorSelector *gvs) { /* Disconnect gradient */ if (gvs->gr) { @@ -380,11 +377,10 @@ sp_gvs_gradient_release (SPObject */*obj*/, SPGradientVectorSelector *gvs) } /* Rebuild GUI */ - sp_gvs_rebuild_gui_full (gvs); + sp_gvs_rebuild_gui_full(gvs); } -static void -sp_gvs_defs_release (SPObject */*defs*/, SPGradientVectorSelector *gvs) +static void sp_gvs_defs_release(SPObject */*defs*/, SPGradientVectorSelector *gvs) { gvs->doc = NULL; @@ -398,15 +394,20 @@ sp_gvs_defs_release (SPObject */*defs*/, SPGradientVectorSelector *gvs) } /* Rebuild GUI */ - sp_gvs_rebuild_gui_full (gvs); + sp_gvs_rebuild_gui_full(gvs); } -static void -sp_gvs_defs_modified (SPObject */*defs*/, guint /*flags*/, SPGradientVectorSelector *gvs) +static void sp_gvs_defs_modified(SPObject */*defs*/, guint /*flags*/, SPGradientVectorSelector *gvs) { /* fixme: We probably have to check some flags here (Lauris) */ - sp_gvs_rebuild_gui_full (gvs); + sp_gvs_rebuild_gui_full(gvs); +} + +void SPGradientVectorSelector::setSwatched() +{ + swatched = true; + sp_gvs_rebuild_gui_full(this); } /*################################################################## @@ -422,27 +423,27 @@ sp_gvs_defs_modified (SPObject */*defs*/, guint /*flags*/, SPGradientVectorSelec #define PAD 4 -static GtkWidget *sp_gradient_vector_widget_new (SPGradient *gradient, SPStop *stop); +static GtkWidget *sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *stop); -static void sp_gradient_vector_widget_load_gradient (GtkWidget *widget, SPGradient *gradient); -static gint sp_gradient_vector_dialog_delete (GtkWidget *widget, GdkEvent *event, GtkWidget *dialog); -static void sp_gradient_vector_dialog_destroy (GtkObject *object, gpointer data); +static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient); +static gint sp_gradient_vector_dialog_delete(GtkWidget *widget, GdkEvent *event, GtkWidget *dialog); +static void sp_gradient_vector_dialog_destroy(GtkObject *object, gpointer data); -static void sp_gradient_vector_widget_destroy (GtkObject *object, gpointer data); -static void sp_gradient_vector_gradient_release (SPObject *obj, GtkWidget *widget); -static void sp_gradient_vector_gradient_modified (SPObject *obj, guint flags, GtkWidget *widget); -static void sp_gradient_vector_color_dragged (SPColorSelector *csel, GtkObject *object); -static void sp_gradient_vector_color_changed (SPColorSelector *csel, GtkObject *object); +static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer data); +static void sp_gradient_vector_gradient_release(SPObject *obj, GtkWidget *widget); +static void sp_gradient_vector_gradient_modified(SPObject *obj, guint flags, GtkWidget *widget); +static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *object); +static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object); static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop); static gboolean blocked = FALSE; -static void grad_edit_dia_stop_added_or_removed (Inkscape::XML::Node */*repr*/, Inkscape::XML::Node */*child*/, Inkscape::XML::Node */*ref*/, gpointer data) +static void grad_edit_dia_stop_added_or_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node */*child*/, Inkscape::XML::Node */*ref*/, gpointer data) { GtkWidget *vb = GTK_WIDGET(data); - GtkWidget *mnu = (GtkWidget *)g_object_get_data (G_OBJECT(vb), "stopmenu"); - SPGradient *gradient = (SPGradient *)g_object_get_data (G_OBJECT(vb), "gradient"); - update_stop_list (mnu, gradient, NULL); + GtkWidget *mnu = (GtkWidget *)g_object_get_data(G_OBJECT(vb), "stopmenu"); + SPGradient *gradient = (SPGradient *)g_object_get_data(G_OBJECT(vb), "gradient"); + update_stop_list(mnu, gradient, NULL); } //FIXME!!! We must also listen to attr changes on all children (i.e. stops) too, @@ -457,14 +458,13 @@ static Inkscape::XML::NodeEventVector grad_edit_dia_repr_events = NULL /* order_changed */ }; -static void -verify_grad(SPGradient *gradient) +static void verify_grad(SPGradient *gradient) { int i = 0; SPStop *stop = NULL; /* count stops */ for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { - if (SP_IS_STOP (ochild)) { + if (SP_IS_STOP(ochild)) { i++; stop = SP_STOP(ochild); } @@ -475,7 +475,7 @@ verify_grad(SPGradient *gradient) if (i < 1) { gchar c[64]; - sp_svg_write_color (c, sizeof(c), 0x00000000); + sp_svg_write_color(c, sizeof(c), 0x00000000); Inkscape::CSSOStringStream os; os << "stop-color:" << c << ";stop-opacity:" << 1.0 << ";"; @@ -485,32 +485,31 @@ verify_grad(SPGradient *gradient) child = xml_doc->createElement("svg:stop"); sp_repr_set_css_double(child, "offset", 0.0); child->setAttribute("style", os.str().c_str()); - SP_OBJECT_REPR (gradient)->addChild(child, NULL); + SP_OBJECT_REPR(gradient)->addChild(child, NULL); Inkscape::GC::release(child); child = xml_doc->createElement("svg:stop"); sp_repr_set_css_double(child, "offset", 1.0); child->setAttribute("style", os.str().c_str()); - SP_OBJECT_REPR (gradient)->addChild(child, NULL); + SP_OBJECT_REPR(gradient)->addChild(child, NULL); Inkscape::GC::release(child); } if (i < 2) { sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", 0.0); Inkscape::XML::Node *child = SP_OBJECT_REPR(stop)->duplicate(SP_OBJECT_REPR(gradient)->document()); sp_repr_set_css_double(child, "offset", 1.0); - SP_OBJECT_REPR(gradient)->addChild(child, SP_OBJECT_REPR (stop)); + SP_OBJECT_REPR(gradient)->addChild(child, SP_OBJECT_REPR(stop)); Inkscape::GC::release(child); } } -static void -select_stop_in_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) +static void select_stop_in_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) { int i = 0; for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { - if (SP_IS_STOP (ochild)) { - if (SP_OBJECT (ochild) == SP_OBJECT(new_stop)) { - gtk_option_menu_set_history (GTK_OPTION_MENU (mnu), i); + if (SP_IS_STOP(ochild)) { + if (SP_OBJECT(ochild) == SP_OBJECT(new_stop)) { + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), i); break; } i++; @@ -518,104 +517,103 @@ select_stop_in_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) } } -static void -update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) +static void update_stop_list( GtkWidget *mnu, SPGradient *gradient, SPStop *new_stop) { - if (!SP_IS_GRADIENT (gradient)) + if (!SP_IS_GRADIENT(gradient)) { return; + } blocked = TRUE; /* Clear old menu, if there is any */ - if (gtk_option_menu_get_menu (GTK_OPTION_MENU (mnu))) { - gtk_option_menu_remove_menu (GTK_OPTION_MENU (mnu)); + if (gtk_option_menu_get_menu(GTK_OPTION_MENU(mnu))) { + gtk_option_menu_remove_menu(GTK_OPTION_MENU(mnu)); } /* Create new menu widget */ - GtkWidget *m = gtk_menu_new (); - gtk_widget_show (m); + GtkWidget *m = gtk_menu_new(); + gtk_widget_show(m); GSList *sl = NULL; if (gradient->has_stops) { - for ( SPObject *ochild = sp_object_first_child (SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { - if (SP_IS_STOP (ochild)) { - sl = g_slist_append (sl, ochild); + for ( SPObject *ochild = sp_object_first_child(SP_OBJECT(gradient)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { + if (SP_IS_STOP(ochild)) { + sl = g_slist_append(sl, ochild); } } } if (!sl) { - GtkWidget *i = gtk_menu_item_new_with_label (_("No stops in gradient")); - gtk_widget_show (i); - gtk_menu_append (GTK_MENU (m), i); - gtk_widget_set_sensitive (mnu, FALSE); + GtkWidget *i = gtk_menu_item_new_with_label(_("No stops in gradient")); + gtk_widget_show(i); + gtk_menu_append(GTK_MENU(m), i); + gtk_widget_set_sensitive(mnu, FALSE); } else { for (; sl != NULL; sl = sl->next){ SPStop *stop; GtkWidget *i; if (SP_IS_STOP(sl->data)){ - stop = SP_STOP (sl->data); - i = gtk_menu_item_new (); - gtk_widget_show (i); - g_object_set_data (G_OBJECT (i), "stop", stop); - GtkWidget *hb = gtk_hbox_new (FALSE, 4); + stop = SP_STOP(sl->data); + i = gtk_menu_item_new(); + gtk_widget_show(i); + g_object_set_data(G_OBJECT(i), "stop", stop); + GtkWidget *hb = gtk_hbox_new(FALSE, 4); GtkWidget *cpv = sp_color_preview_new(sp_stop_get_rgba32(stop)); - gtk_widget_show (cpv); - gtk_container_add ( GTK_CONTAINER (hb), cpv ); - g_object_set_data ( G_OBJECT (i), "preview", cpv ); + gtk_widget_show(cpv); + gtk_container_add( GTK_CONTAINER(hb), cpv ); + g_object_set_data( G_OBJECT(i), "preview", cpv ); Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) sl->data); - GtkWidget *l = gtk_label_new (repr->attribute("id")); - gtk_widget_show (l); - gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); - gtk_box_pack_start (GTK_BOX (hb), l, TRUE, TRUE, 0); - gtk_widget_show (hb); - gtk_container_add (GTK_CONTAINER (i), hb); - gtk_menu_append (GTK_MENU (m), i); + GtkWidget *l = gtk_label_new(repr->attribute("id")); + gtk_widget_show(l); + gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5); + gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, 0); + gtk_widget_show(hb); + gtk_container_add(GTK_CONTAINER(i), hb); + gtk_menu_append(GTK_MENU(m), i); } } - gtk_widget_set_sensitive (mnu, TRUE); + gtk_widget_set_sensitive(mnu, TRUE); } - gtk_option_menu_set_menu (GTK_OPTION_MENU (mnu), m); + gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu), m); /* Set history */ if (new_stop == NULL) { - gtk_option_menu_set_history (GTK_OPTION_MENU (mnu), 0); + gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); } else { - select_stop_in_list (mnu, gradient, new_stop); + select_stop_in_list(mnu, gradient, new_stop); } blocked = FALSE; } -/*user selected existing stop from list*/ -static void -sp_grad_edit_select (GtkOptionMenu *mnu, GtkWidget *tbl) +// user selected existing stop from list +static void sp_grad_edit_select(GtkOptionMenu *mnu, GtkWidget *tbl) { - SPGradient *gradient = (SPGradient *)g_object_get_data (G_OBJECT(tbl), "gradient"); - - GObject *item = G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))); - SPStop *stop = SP_STOP (g_object_get_data (item, "stop")); - if (!stop) return; + GObject *item = G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))); + SPStop *stop = SP_STOP(g_object_get_data(item, "stop")); + if (!stop) { + return; + } blocked = TRUE; - SPColorSelector *csel = (SPColorSelector*)g_object_get_data (G_OBJECT (tbl), "cselector"); + SPColorSelector *csel = (SPColorSelector*)g_object_get_data(G_OBJECT(tbl), "cselector"); guint32 const c = sp_stop_get_rgba32(stop); - csel->base->setAlpha(SP_RGBA32_A_F (c)); - SPColor color( SP_RGBA32_R_F (c), SP_RGBA32_G_F (c), SP_RGBA32_B_F (c) ); + csel->base->setAlpha(SP_RGBA32_A_F(c)); + SPColor color( SP_RGBA32_R_F(c), SP_RGBA32_G_F(c), SP_RGBA32_B_F(c) ); // set its color, from the stored array csel->base->setColor( color ); - GtkWidget *offspin = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "offspn")); - GtkWidget *offslide =GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "offslide")); + GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offspn")); + GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offslide")); - GtkAdjustment *adj = (GtkAdjustment*)gtk_object_get_data (GTK_OBJECT (tbl), "offset"); + GtkAdjustment *adj = static_cast<GtkAdjustment*>(gtk_object_get_data(GTK_OBJECT(tbl), "offset")); bool isEndStop = false; SPStop *prev = NULL; - prev = sp_prev_stop(stop, gradient); + prev = stop->getPrevStop(); if (prev != NULL ) { adj->lower = prev->offset; } else { @@ -624,7 +622,7 @@ sp_grad_edit_select (GtkOptionMenu *mnu, GtkWidget *tbl) } SPStop *next = NULL; - next = sp_next_stop(stop); + next = stop->getNextStop(); if (next != NULL ) { adj->upper = next->offset; } else { @@ -634,16 +632,16 @@ sp_grad_edit_select (GtkOptionMenu *mnu, GtkWidget *tbl) //fixme: does this work on all possible input gradients? if (!isEndStop) { - gtk_widget_set_sensitive (offslide, TRUE); - gtk_widget_set_sensitive (GTK_WIDGET (offspin), TRUE); + gtk_widget_set_sensitive(offslide, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE); } else { - gtk_widget_set_sensitive (offslide, FALSE); - gtk_widget_set_sensitive (GTK_WIDGET (offspin), FALSE); + gtk_widget_set_sensitive(offslide, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(offspin), FALSE); } - gtk_adjustment_set_value (adj, stop->offset); + gtk_adjustment_set_value(adj, stop->offset); - gtk_adjustment_changed (adj); + gtk_adjustment_changed(adj); blocked = FALSE; } @@ -651,57 +649,58 @@ sp_grad_edit_select (GtkOptionMenu *mnu, GtkWidget *tbl) -static void -offadjustmentChanged( GtkAdjustment *adjustment, GtkWidget *vb) +static void offadjustmentChanged( GtkAdjustment *adjustment, GtkWidget *vb) { - if (blocked) + if (blocked) { return; + } blocked = TRUE; - GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(vb), "stopmenu"); - if (!g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")) return; - SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")); + GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(vb), "stopmenu")); + if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")) { + return; + } + SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); stop->offset = adjustment->value; sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", stop->offset); - sp_document_done (SP_OBJECT_DOCUMENT (stop), SP_VERB_CONTEXT_GRADIENT, + sp_document_done(SP_OBJECT_DOCUMENT(stop), SP_VERB_CONTEXT_GRADIENT, _("Change gradient stop offset")); blocked = FALSE; } -guint32 -sp_average_color (guint32 c1, guint32 c2, gdouble p = 0.5) +guint32 sp_average_color(guint32 c1, guint32 c2, gdouble p = 0.5) { - guint32 r = (guint32) (SP_RGBA32_R_U (c1) * p + SP_RGBA32_R_U (c2) * (1 - p)); - guint32 g = (guint32) (SP_RGBA32_G_U (c1) * p + SP_RGBA32_G_U (c2) * (1 - p)); - guint32 b = (guint32) (SP_RGBA32_B_U (c1) * p + SP_RGBA32_B_U (c2) * (1 - p)); - guint32 a = (guint32) (SP_RGBA32_A_U (c1) * p + SP_RGBA32_A_U (c2) * (1 - p)); + guint32 r = (guint32) (SP_RGBA32_R_U(c1) * p + SP_RGBA32_R_U(c2) * (1 - p)); + guint32 g = (guint32) (SP_RGBA32_G_U(c1) * p + SP_RGBA32_G_U(c2) * (1 - p)); + guint32 b = (guint32) (SP_RGBA32_B_U(c1) * p + SP_RGBA32_B_U(c2) * (1 - p)); + guint32 a = (guint32) (SP_RGBA32_A_U(c1) * p + SP_RGBA32_A_U(c2) * (1 - p)); - return SP_RGBA32_U_COMPOSE (r, g, b, a); + return SP_RGBA32_U_COMPOSE(r, g, b, a); } -static void -sp_grd_ed_add_stop (GtkWidget */*widget*/, GtkWidget *vb) +static void sp_grd_ed_add_stop(GtkWidget */*widget*/, GtkWidget *vb) { - SPGradient *gradient = (SPGradient *) g_object_get_data (G_OBJECT(vb), "gradient"); - verify_grad (gradient); - GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(vb), "stopmenu"); + SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient")); + verify_grad(gradient); + GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(vb), "stopmenu")); - SPStop *stop = (SPStop *) g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop"); + SPStop *stop = static_cast<SPStop *>(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); - if (stop == NULL) + if (stop == NULL) { return; + } Inkscape::XML::Node *new_stop_repr = NULL; - SPStop *next = sp_next_stop (stop); + SPStop *next = stop->getNextStop(); if (next == NULL) { - SPStop *prev = sp_prev_stop (stop, gradient); + SPStop *prev = stop->getPrevStop(); if (prev != NULL) { next = stop; stop = prev; @@ -713,8 +712,8 @@ sp_grd_ed_add_stop (GtkWidget */*widget*/, GtkWidget *vb) SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(stop)); } else { next = stop; - new_stop_repr = SP_OBJECT_REPR(sp_prev_stop(stop, gradient))->duplicate(SP_OBJECT_REPR(gradient)->document()); - SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(sp_prev_stop(stop, gradient))); + new_stop_repr = SP_OBJECT_REPR(stop->getPrevStop())->duplicate(SP_OBJECT_REPR(gradient)->document()); + SP_OBJECT_REPR(gradient)->addChild(new_stop_repr, SP_OBJECT_REPR(stop->getPrevStop())); } SPStop *newstop = (SPStop *) SP_OBJECT_DOCUMENT(gradient)->getObjectByRepr(new_stop_repr); @@ -723,186 +722,184 @@ sp_grd_ed_add_stop (GtkWidget */*widget*/, GtkWidget *vb) guint32 const c1 = sp_stop_get_rgba32(stop); guint32 const c2 = sp_stop_get_rgba32(next); - guint32 cnew = sp_average_color (c1, c2); + guint32 cnew = sp_average_color(c1, c2); Inkscape::CSSOStringStream os; gchar c[64]; - sp_svg_write_color (c, sizeof(c), cnew); - gdouble opacity = (gdouble) SP_RGBA32_A_F (cnew); + sp_svg_write_color(c, sizeof(c), cnew); + gdouble opacity = static_cast<gdouble>(SP_RGBA32_A_F(cnew)); os << "stop-color:" << c << ";stop-opacity:" << opacity <<";"; SP_OBJECT_REPR (newstop)->setAttribute("style", os.str().c_str()); sp_repr_set_css_double( SP_OBJECT_REPR(newstop), "offset", (double)newstop->offset); - sp_gradient_vector_widget_load_gradient (vb, gradient); + sp_gradient_vector_widget_load_gradient(vb, gradient); Inkscape::GC::release(new_stop_repr); update_stop_list(GTK_WIDGET(mnu), gradient, newstop); - GtkWidget *offspin = GTK_WIDGET (g_object_get_data (G_OBJECT (vb), "offspn")); - GtkWidget *offslide =GTK_WIDGET (g_object_get_data (G_OBJECT (vb), "offslide")); - gtk_widget_set_sensitive (offslide, TRUE); - gtk_widget_set_sensitive (GTK_WIDGET (offspin), TRUE); - sp_document_done (SP_OBJECT_DOCUMENT (gradient), SP_VERB_CONTEXT_GRADIENT, + GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offspn")); + GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offslide")); + gtk_widget_set_sensitive(offslide, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE); + sp_document_done(SP_OBJECT_DOCUMENT(gradient), SP_VERB_CONTEXT_GRADIENT, _("Add gradient stop")); } -static void -sp_grd_ed_del_stop (GtkWidget */*widget*/, GtkWidget *vb) +static void sp_grd_ed_del_stop(GtkWidget */*widget*/, GtkWidget *vb) { - SPGradient *gradient = (SPGradient *)g_object_get_data (G_OBJECT(vb), "gradient"); + SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient")); - GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(vb), "stopmenu"); - if (!g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")) return; - SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")); + GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(vb), "stopmenu")); + if (!g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")) return; + SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); if (gradient->vector.stops.size() > 2) { // 2 is the minimum // if we delete first or last stop, move the next/previous to the edge if (stop->offset == 0) { - SPStop *next = sp_next_stop (stop); + SPStop *next = stop->getNextStop(); if (next) { next->offset = 0; - sp_repr_set_css_double (SP_OBJECT_REPR (next), "offset", 0); + sp_repr_set_css_double(SP_OBJECT_REPR(next), "offset", 0); } } else if (stop->offset == 1) { - SPStop *prev = sp_prev_stop (stop, gradient); + SPStop *prev = stop->getPrevStop(); if (prev) { prev->offset = 1; - sp_repr_set_css_double (SP_OBJECT_REPR (prev), "offset", 1); + sp_repr_set_css_double(SP_OBJECT_REPR(prev), "offset", 1); } } SP_OBJECT_REPR(gradient)->removeChild(SP_OBJECT_REPR(stop)); - sp_gradient_vector_widget_load_gradient (vb, gradient); + sp_gradient_vector_widget_load_gradient(vb, gradient); update_stop_list(GTK_WIDGET(mnu), gradient, NULL); - sp_document_done (SP_OBJECT_DOCUMENT (gradient), SP_VERB_CONTEXT_GRADIENT, + sp_document_done(SP_OBJECT_DOCUMENT(gradient), SP_VERB_CONTEXT_GRADIENT, _("Delete gradient stop")); } } -static GtkWidget * -sp_gradient_vector_widget_new (SPGradient *gradient, SPStop *select_stop) +static GtkWidget * sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *select_stop) { GtkWidget *vb, *w, *f, *csel; - g_return_val_if_fail (!gradient || SP_IS_GRADIENT (gradient), NULL); + g_return_val_if_fail(!gradient || SP_IS_GRADIENT(gradient), NULL); - vb = gtk_vbox_new (FALSE, PAD); - g_signal_connect (G_OBJECT (vb), "destroy", G_CALLBACK (sp_gradient_vector_widget_destroy), NULL); + vb = gtk_vbox_new(FALSE, PAD); + g_signal_connect(G_OBJECT(vb), "destroy", G_CALLBACK(sp_gradient_vector_widget_destroy), NULL); - w = sp_gradient_image_new (gradient); - g_object_set_data (G_OBJECT (vb), "preview", w); - gtk_widget_show (w); - gtk_box_pack_start (GTK_BOX (vb), w, TRUE, TRUE, PAD); + w = sp_gradient_image_new(gradient); + g_object_set_data(G_OBJECT(vb), "preview", w); + gtk_widget_show(w); + gtk_box_pack_start(GTK_BOX(vb), w, TRUE, TRUE, PAD); - sp_repr_add_listener (SP_OBJECT_REPR(gradient), &grad_edit_dia_repr_events, vb); - GtkTooltips *tt = gtk_tooltips_new (); + sp_repr_add_listener(SP_OBJECT_REPR(gradient), &grad_edit_dia_repr_events, vb); + GtkTooltips *tt = gtk_tooltips_new(); /* Stop list */ - GtkWidget *mnu = gtk_option_menu_new (); + GtkWidget *mnu = gtk_option_menu_new(); /* Create new menu widget */ - update_stop_list (GTK_WIDGET(mnu), gradient, NULL); - gtk_signal_connect (GTK_OBJECT (mnu), "changed", GTK_SIGNAL_FUNC (sp_grad_edit_select), vb); - gtk_widget_show (mnu); - gtk_object_set_data (GTK_OBJECT (vb), "stopmenu", mnu); - gtk_box_pack_start (GTK_BOX (vb), mnu, FALSE, FALSE, 0); + update_stop_list(GTK_WIDGET(mnu), gradient, NULL); + gtk_signal_connect(GTK_OBJECT(mnu), "changed", GTK_SIGNAL_FUNC(sp_grad_edit_select), vb); + gtk_widget_show(mnu); + gtk_object_set_data(GTK_OBJECT(vb), "stopmenu", mnu); + gtk_box_pack_start(GTK_BOX(vb), mnu, FALSE, FALSE, 0); /* Add and Remove buttons */ - GtkWidget *hb = gtk_hbox_new (FALSE, 1); + GtkWidget *hb = gtk_hbox_new(FALSE, 1); // TRANSLATORS: "Stop" means: a "phase" of a gradient - GtkWidget *b = gtk_button_new_with_label (_("Add stop")); - gtk_widget_show (b); - gtk_container_add (GTK_CONTAINER (hb), b); - gtk_tooltips_set_tip (tt, b, _("Add another control stop to gradient"), NULL); - gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (sp_grd_ed_add_stop), vb); - b = gtk_button_new_with_label (_("Delete stop")); - gtk_widget_show (b); - gtk_container_add (GTK_CONTAINER (hb), b); - gtk_tooltips_set_tip (tt, b, _("Delete current control stop from gradient"), NULL); - gtk_signal_connect (GTK_OBJECT (b), "clicked", GTK_SIGNAL_FUNC (sp_grd_ed_del_stop), vb); + GtkWidget *b = gtk_button_new_with_label(_("Add stop")); + gtk_widget_show(b); + gtk_container_add(GTK_CONTAINER(hb), b); + gtk_tooltips_set_tip(tt, b, _("Add another control stop to gradient"), NULL); + gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_grd_ed_add_stop), vb); + b = gtk_button_new_with_label(_("Delete stop")); + gtk_widget_show(b); + gtk_container_add(GTK_CONTAINER(hb), b); + gtk_tooltips_set_tip(tt, b, _("Delete current control stop from gradient"), NULL); + gtk_signal_connect(GTK_OBJECT(b), "clicked", GTK_SIGNAL_FUNC(sp_grd_ed_del_stop), vb); - gtk_widget_show (hb); - gtk_box_pack_start (GTK_BOX (vb),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); + gtk_widget_show(hb); + gtk_box_pack_start(GTK_BOX(vb),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); /* Offset Slider and stuff */ - hb = gtk_hbox_new (FALSE, 0); + hb = gtk_hbox_new(FALSE, 0); /* Label */ - GtkWidget *l = gtk_label_new (_("Offset:")); - gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5); - gtk_box_pack_start (GTK_BOX (hb),l, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); - gtk_widget_show (l); + GtkWidget *l = gtk_label_new(_("Offset:")); + gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5); + gtk_box_pack_start(GTK_BOX(hb),l, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); + gtk_widget_show(l); /* Adjustment */ GtkAdjustment *Offset_adj = NULL; - Offset_adj= (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 1.0, 0.01, 0.01, 0.0); - gtk_object_set_data (GTK_OBJECT (vb), "offset", Offset_adj); - GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu (GTK_OPTION_MENU(mnu))); - SPStop *stop = SP_STOP (g_object_get_data (G_OBJECT (gtk_menu_get_active (m)), "stop")); - gtk_adjustment_set_value (Offset_adj, stop->offset); + Offset_adj= (GtkAdjustment *) gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.01, 0.0); + gtk_object_set_data(GTK_OBJECT(vb), "offset", Offset_adj); + GtkMenu *m = GTK_MENU(gtk_option_menu_get_menu(GTK_OPTION_MENU(mnu))); + SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), "stop")); + gtk_adjustment_set_value(Offset_adj, stop->offset); /* Slider */ GtkWidget *slider = gtk_hscale_new(Offset_adj); gtk_scale_set_draw_value( GTK_SCALE(slider), FALSE ); - gtk_widget_show (slider); - gtk_box_pack_start (GTK_BOX (hb),slider, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); - gtk_object_set_data (GTK_OBJECT (vb), "offslide", slider); + gtk_widget_show(slider); + gtk_box_pack_start(GTK_BOX(hb),slider, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); + gtk_object_set_data(GTK_OBJECT(vb), "offslide", slider); /* Spinbutton */ - GtkWidget *sbtn = gtk_spin_button_new (GTK_ADJUSTMENT (Offset_adj), 0.01, 2); - sp_dialog_defocus_on_enter (sbtn); - gtk_widget_show (sbtn); - gtk_box_pack_start (GTK_BOX (hb),sbtn, FALSE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); - gtk_object_set_data (GTK_OBJECT (vb), "offspn", sbtn); + GtkWidget *sbtn = gtk_spin_button_new(GTK_ADJUSTMENT(Offset_adj), 0.01, 2); + sp_dialog_defocus_on_enter(sbtn); + gtk_widget_show(sbtn); + gtk_box_pack_start(GTK_BOX(hb),sbtn, FALSE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); + gtk_object_set_data(GTK_OBJECT(vb), "offspn", sbtn); if (stop->offset>0 && stop->offset<1) { - gtk_widget_set_sensitive (slider, TRUE); - gtk_widget_set_sensitive (GTK_WIDGET (sbtn), TRUE); + gtk_widget_set_sensitive(slider, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(sbtn), TRUE); } else { - gtk_widget_set_sensitive (slider, FALSE); - gtk_widget_set_sensitive (GTK_WIDGET (sbtn), FALSE); + gtk_widget_set_sensitive(slider, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(sbtn), FALSE); } /* Signals */ - gtk_signal_connect (GTK_OBJECT (Offset_adj), "value_changed", - GTK_SIGNAL_FUNC (offadjustmentChanged), vb); + gtk_signal_connect(GTK_OBJECT(Offset_adj), "value_changed", + GTK_SIGNAL_FUNC(offadjustmentChanged), vb); - // gtk_signal_connect (GTK_OBJECT (slider), "changed", GTK_SIGNAL_FUNC (offsliderChanged), vb); - gtk_widget_show (hb); - gtk_box_pack_start (GTK_BOX (vb), hb, FALSE, FALSE, PAD); + // gtk_signal_connect(GTK_OBJECT(slider), "changed", GTK_SIGNAL_FUNC(offsliderChanged), vb); + gtk_widget_show(hb); + gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, PAD); // TRANSLATORS: "Stop" means: a "phase" of a gradient - f = gtk_frame_new (_("Stop Color")); - gtk_widget_show (f); - gtk_box_pack_start (GTK_BOX (vb), f, TRUE, TRUE, PAD); - csel = (GtkWidget*)sp_color_selector_new (SP_TYPE_COLOR_NOTEBOOK); - g_object_set_data (G_OBJECT (vb), "cselector", csel); - gtk_widget_show (csel); - gtk_container_add (GTK_CONTAINER (f), csel); - g_signal_connect (G_OBJECT (csel), "dragged", G_CALLBACK (sp_gradient_vector_color_dragged), vb); - g_signal_connect (G_OBJECT (csel), "changed", G_CALLBACK (sp_gradient_vector_color_changed), vb); - - gtk_widget_show (vb); - - sp_gradient_vector_widget_load_gradient (vb, gradient); - - if (select_stop) - select_stop_in_list (GTK_WIDGET(mnu), gradient, select_stop); + f = gtk_frame_new(_("Stop Color")); + gtk_widget_show(f); + gtk_box_pack_start(GTK_BOX(vb), f, TRUE, TRUE, PAD); + csel = static_cast<GtkWidget*>(sp_color_selector_new(SP_TYPE_COLOR_NOTEBOOK)); + g_object_set_data(G_OBJECT(vb), "cselector", csel); + gtk_widget_show(csel); + gtk_container_add(GTK_CONTAINER(f), csel); + g_signal_connect(G_OBJECT(csel), "dragged", G_CALLBACK(sp_gradient_vector_color_dragged), vb); + g_signal_connect(G_OBJECT(csel), "changed", G_CALLBACK(sp_gradient_vector_color_changed), vb); + + gtk_widget_show(vb); + + sp_gradient_vector_widget_load_gradient(vb, gradient); + + if (select_stop) { + select_stop_in_list(GTK_WIDGET(mnu), gradient, select_stop); + } return vb; } -GtkWidget * -sp_gradient_vector_editor_new (SPGradient *gradient, SPStop *stop) +GtkWidget * sp_gradient_vector_editor_new(SPGradient *gradient, SPStop *stop) { GtkWidget *wid; if (dlg == NULL) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - dlg = sp_window_new (_("Gradient editor"), TRUE); + dlg = sp_window_new(_("Gradient editor"), TRUE); if (x == -1000 || y == -1000) { x = prefs->getInt(prefs_path + "x", -1000); y = prefs->getInt(prefs_path + "y", -1000); @@ -912,32 +909,39 @@ sp_gradient_vector_editor_new (SPGradient *gradient, SPStop *stop) h = prefs->getInt(prefs_path + "h", 0); } - if (x<0) x=0; - if (y<0) y=0; + if (x<0) { + x=0; + } + if (y<0) { + y=0; + } - if (x != 0 || y != 0) - gtk_window_move ((GtkWindow *) dlg, x, y); - else + if (x != 0 || y != 0) { + gtk_window_move(reinterpret_cast<GtkWindow *>(dlg), x, y); + } else { gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); - if (w && h) gtk_window_resize ((GtkWindow *) dlg, w, h); - sp_transientize (dlg); + } + if (w && h) { + gtk_window_resize(reinterpret_cast<GtkWindow *>(dlg), w, h); + } + sp_transientize(dlg); wd.win = dlg; wd.stop = 0; - g_signal_connect (G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd); - gtk_signal_connect (GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg); - gtk_signal_connect (GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_gradient_vector_dialog_destroy), dlg); - gtk_signal_connect (GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_gradient_vector_dialog_delete), dlg); - g_signal_connect (G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_gradient_vector_dialog_delete), dlg); - g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg ); - g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg ); - - gtk_container_set_border_width (GTK_CONTAINER (dlg), PAD); - - wid = (GtkWidget*)sp_gradient_vector_widget_new (gradient, stop); - g_object_set_data (G_OBJECT (dlg), "gradient-vector-widget", wid); + g_signal_connect(G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_transientize_callback), &wd); + gtk_signal_connect(GTK_OBJECT(dlg), "event", GTK_SIGNAL_FUNC(sp_dialog_event_handler), dlg); + gtk_signal_connect(GTK_OBJECT(dlg), "destroy", G_CALLBACK(sp_gradient_vector_dialog_destroy), dlg); + gtk_signal_connect(GTK_OBJECT(dlg), "delete_event", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg); + g_signal_connect(G_OBJECT(INKSCAPE), "shut_down", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg); + g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_hide", G_CALLBACK(sp_dialog_hide), dlg ); + g_signal_connect( G_OBJECT(INKSCAPE), "dialogs_unhide", G_CALLBACK(sp_dialog_unhide), dlg ); + + gtk_container_set_border_width(GTK_CONTAINER(dlg), PAD); + + wid = static_cast<GtkWidget*>(sp_gradient_vector_widget_new(gradient, stop)); + g_object_set_data(G_OBJECT(dlg), "gradient-vector-widget", wid); /* Connect signals */ - gtk_widget_show (wid); - gtk_container_add (GTK_CONTAINER (dlg), wid); + gtk_widget_show(wid); + gtk_container_add(GTK_CONTAINER(dlg), wid); } else { // FIXME: temp fix for 0.38 // Simply load_gradient into the editor does not work for multi-stop gradients, @@ -946,29 +950,28 @@ sp_gradient_vector_editor_new (SPGradient *gradient, SPStop *stop) // and call sp_gradient_vector_editor_new again, so it creates the window anew. GdkEventAny event; - GtkWidget *widget = (GtkWidget *) dlg; + GtkWidget *widget = static_cast<GtkWidget *>(dlg); event.type = GDK_DELETE; event.window = widget->window; event.send_event = TRUE; - g_object_ref (G_OBJECT (event.window)); - gtk_main_do_event ((GdkEvent*)&event); - g_object_unref (G_OBJECT (event.window)); + g_object_ref(G_OBJECT(event.window)); + gtk_main_do_event(reinterpret_cast<GdkEvent*>(&event)); + g_object_unref(G_OBJECT(event.window)); - g_assert (dlg == NULL); - sp_gradient_vector_editor_new (gradient, stop); + g_assert(dlg == NULL); + sp_gradient_vector_editor_new(gradient, stop); } return dlg; } -static void -sp_gradient_vector_widget_load_gradient (GtkWidget *widget, SPGradient *gradient) +static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient) { blocked = TRUE; SPGradient *old; - old = (SPGradient*)g_object_get_data (G_OBJECT (widget), "gradient"); + old = (SPGradient*)g_object_get_data(G_OBJECT(widget), "gradient"); if (old != gradient) { sigc::connection *release_connection; @@ -982,7 +985,7 @@ sp_gradient_vector_widget_load_gradient (GtkWidget *widget, SPGradient *gradient g_assert( modified_connection != NULL ); release_connection->disconnect(); modified_connection->disconnect(); - sp_signal_disconnect_by_data (old, widget); + sp_signal_disconnect_by_data(old, widget); } if (gradient) { @@ -1009,62 +1012,64 @@ sp_gradient_vector_widget_load_gradient (GtkWidget *widget, SPGradient *gradient g_object_set_data(G_OBJECT(widget), "gradient_modified_connection", modified_connection); } - g_object_set_data (G_OBJECT (widget), "gradient", gradient); + g_object_set_data(G_OBJECT(widget), "gradient", gradient); if (gradient) { - gtk_widget_set_sensitive (widget, TRUE); + gtk_widget_set_sensitive(widget, TRUE); - sp_gradient_ensure_vector (gradient); + sp_gradient_ensure_vector(gradient); - GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(widget), "stopmenu"); - SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")); + GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(widget), "stopmenu")); + SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); guint32 const c = sp_stop_get_rgba32(stop); /// get the color selector - SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data (G_OBJECT (widget), "cselector")); + SPColorSelector *csel = SP_COLOR_SELECTOR(g_object_get_data(G_OBJECT(widget), "cselector")); // set alpha - csel->base->setAlpha(SP_RGBA32_A_F (c)); - SPColor color( SP_RGBA32_R_F (c), SP_RGBA32_G_F (c), SP_RGBA32_B_F (c) ); + csel->base->setAlpha(SP_RGBA32_A_F(c)); + SPColor color( SP_RGBA32_R_F(c), SP_RGBA32_G_F(c), SP_RGBA32_B_F(c) ); // set color csel->base->setColor( color ); /* Fill preview */ GtkWidget *w = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "preview")); - sp_gradient_image_set_gradient (SP_GRADIENT_IMAGE (w), gradient); + sp_gradient_image_set_gradient(SP_GRADIENT_IMAGE(w), gradient); - update_stop_list (GTK_WIDGET(mnu), gradient, NULL); + update_stop_list(GTK_WIDGET(mnu), gradient, NULL); // Once the user edits a gradient, it stops being auto-collectable if (SP_OBJECT_REPR(gradient)->attribute("inkscape:collect")) { - SPDocument *document = SP_OBJECT_DOCUMENT (gradient); + SPDocument *document = SP_OBJECT_DOCUMENT(gradient); bool saved = sp_document_get_undo_sensitive(document); - sp_document_set_undo_sensitive (document, false); + sp_document_set_undo_sensitive(document, false); SP_OBJECT_REPR(gradient)->setAttribute("inkscape:collect", NULL); - sp_document_set_undo_sensitive (document, saved); + sp_document_set_undo_sensitive(document, saved); } } else { // no gradient, disable everything - gtk_widget_set_sensitive (widget, FALSE); + gtk_widget_set_sensitive(widget, FALSE); } blocked = FALSE; } -static void -sp_gradient_vector_dialog_destroy (GtkObject */*object*/, gpointer /*data*/) +static void sp_gradient_vector_dialog_destroy(GtkObject */*object*/, gpointer /*data*/) { - sp_signal_disconnect_by_data (INKSCAPE, dlg); + sp_signal_disconnect_by_data(INKSCAPE, dlg); wd.win = dlg = NULL; wd.stop = 0; } -static gboolean -sp_gradient_vector_dialog_delete (GtkWidget */*widget*/, GdkEvent */*event*/, GtkWidget */*dialog*/) +static gboolean sp_gradient_vector_dialog_delete(GtkWidget */*widget*/, GdkEvent */*event*/, GtkWidget */*dialog*/) { - gtk_window_get_position ((GtkWindow *) dlg, &x, &y); - gtk_window_get_size ((GtkWindow *) dlg, &w, &h); + gtk_window_get_position(GTK_WINDOW(dlg), &x, &y); + gtk_window_get_size(GTK_WINDOW(dlg), &w, &h); - if (x<0) x=0; - if (y<0) y=0; + if (x<0) { + x=0; + } + if (y<0) { + y=0; + } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); prefs->setInt(prefs_path + "x", x); @@ -1077,12 +1082,11 @@ sp_gradient_vector_dialog_delete (GtkWidget */*widget*/, GdkEvent */*event*/, Gt /* Widget destroy handler */ -static void -sp_gradient_vector_widget_destroy (GtkObject *object, gpointer /*data*/) +static void sp_gradient_vector_widget_destroy(GtkObject *object, gpointer /*data*/) { GObject *gradient; - gradient = (GObject*)g_object_get_data (G_OBJECT (object), "gradient"); + gradient = (GObject*)g_object_get_data(G_OBJECT(object), "gradient"); sigc::connection *release_connection = (sigc::connection *)g_object_get_data(G_OBJECT(object), "gradient_release_connection"); sigc::connection *modified_connection = (sigc::connection *)g_object_get_data(G_OBJECT(object), "gradient_modified_connection"); @@ -1092,27 +1096,25 @@ sp_gradient_vector_widget_destroy (GtkObject *object, gpointer /*data*/) g_assert( modified_connection != NULL ); release_connection->disconnect(); modified_connection->disconnect(); - sp_signal_disconnect_by_data (gradient, object); + sp_signal_disconnect_by_data(gradient, object); } if (gradient && SP_OBJECT_REPR(gradient)) { - sp_repr_remove_listener_by_data (SP_OBJECT_REPR(gradient), object); + sp_repr_remove_listener_by_data(SP_OBJECT_REPR(gradient), object); } } -static void -sp_gradient_vector_gradient_release (SPObject */*object*/, GtkWidget *widget) +static void sp_gradient_vector_gradient_release(SPObject */*object*/, GtkWidget *widget) { - sp_gradient_vector_widget_load_gradient (widget, NULL); + sp_gradient_vector_widget_load_gradient(widget, NULL); } -static void -sp_gradient_vector_gradient_modified (SPObject *object, guint /*flags*/, GtkWidget *widget) +static void sp_gradient_vector_gradient_modified(SPObject *object, guint /*flags*/, GtkWidget *widget) { SPGradient *gradient=SP_GRADIENT(object); if (!blocked) { blocked = TRUE; - sp_gradient_vector_widget_load_gradient (widget, gradient); + sp_gradient_vector_widget_load_gradient(widget, gradient); blocked = FALSE; } } @@ -1121,79 +1123,86 @@ static void sp_gradient_vector_color_dragged(SPColorSelector *csel, GtkObject *o { SPGradient *gradient, *ngr; - if (blocked) return; + if (blocked) { + return; + } - gradient = (SPGradient*)g_object_get_data (G_OBJECT (object), "gradient"); - if (!gradient) return; + gradient = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(object), "gradient")); + if (!gradient) { + return; + } blocked = TRUE; - ngr = sp_gradient_ensure_vector_normalized (gradient); + ngr = sp_gradient_ensure_vector_normalized(gradient); if (ngr != gradient) { /* Our master gradient has changed */ - sp_gradient_vector_widget_load_gradient (GTK_WIDGET (object), ngr); + sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } - sp_gradient_ensure_vector (ngr); + sp_gradient_ensure_vector(ngr); - GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(object), "stopmenu"); - SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")); + GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(object), "stopmenu")); + SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); - csel->base->getColorAlpha(stop->specified_color, &stop->opacity); + csel->base->getColorAlpha(stop->specified_color, stop->opacity); stop->currentColor = false; blocked = FALSE; } -static void -sp_gradient_vector_color_changed (SPColorSelector *csel, GtkObject *object) +static void sp_gradient_vector_color_changed(SPColorSelector *csel, GtkObject *object) { SPColor color; float alpha; guint32 rgb; - if (blocked) return; + if (blocked) { + return; + } - SPGradient *gradient = (SPGradient*)g_object_get_data (G_OBJECT (object), "gradient"); - if (!gradient) return; + SPGradient *gradient = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(object), "gradient")); + if (!gradient) { + return; + } blocked = TRUE; - SPGradient *ngr = sp_gradient_ensure_vector_normalized (gradient); + SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); if (ngr != gradient) { /* Our master gradient has changed */ - sp_gradient_vector_widget_load_gradient (GTK_WIDGET (object), ngr); + sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); } - sp_gradient_ensure_vector (ngr); + sp_gradient_ensure_vector(ngr); /* Set start parameters */ /* We rely on normalized vector, i.e. stops HAVE to exist */ - g_return_if_fail (sp_first_stop(ngr) != NULL); + g_return_if_fail(ngr->getFirstStop() != NULL); - GtkOptionMenu *mnu = (GtkOptionMenu *)g_object_get_data (G_OBJECT(object), "stopmenu"); - SPStop *stop = SP_STOP(g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "stop")); + GtkOptionMenu *mnu = static_cast<GtkOptionMenu *>(g_object_get_data(G_OBJECT(object), "stopmenu")); + SPStop *stop = SP_STOP(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "stop")); - csel = (SPColorSelector*)g_object_get_data (G_OBJECT (object), "cselector"); - csel->base->getColorAlpha( color, &alpha ); + csel = static_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(object), "cselector")); + csel->base->getColorAlpha( color, alpha ); rgb = color.toRGBA32( 0x00 ); - sp_repr_set_css_double (SP_OBJECT_REPR (stop), "offset", stop->offset); + sp_repr_set_css_double(SP_OBJECT_REPR(stop), "offset", stop->offset); Inkscape::CSSOStringStream os; gchar c[64]; - sp_svg_write_color (c, sizeof(c), rgb); - os << "stop-color:" << c << ";stop-opacity:" << (gdouble) alpha <<";"; - SP_OBJECT_REPR (stop)->setAttribute("style", os.str().c_str()); - // g_snprintf (c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, (gdouble) alpha); - //SP_OBJECT_REPR (stop)->setAttribute("style", c); + sp_svg_write_color(c, sizeof(c), rgb); + os << "stop-color:" << c << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; + SP_OBJECT_REPR(stop)->setAttribute("style", os.str().c_str()); + // g_snprintf(c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, static_cast<gdouble>(alpha)); + //SP_OBJECT_REPR(stop)->setAttribute("style", c); - sp_document_done (SP_OBJECT_DOCUMENT (ngr), SP_VERB_CONTEXT_GRADIENT, + sp_document_done(SP_OBJECT_DOCUMENT(ngr), SP_VERB_CONTEXT_GRADIENT, _("Change gradient stop color")); blocked = FALSE; - SPColorPreview *cpv = (SPColorPreview *)g_object_get_data (G_OBJECT(gtk_menu_get_active (GTK_MENU(gtk_option_menu_get_menu (mnu)))), "preview"); + SPColorPreview *cpv = static_cast<SPColorPreview *>(g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(mnu)))), "preview")); sp_color_preview_set_rgba32(cpv, sp_stop_get_rgba32(stop)); } diff --git a/src/widgets/gradient-vector.h b/src/widgets/gradient-vector.h index ea1f5159f..ceca9158c 100644 --- a/src/widgets/gradient-vector.h +++ b/src/widgets/gradient-vector.h @@ -6,7 +6,9 @@ * * Author: * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> * + * Copyright (C) 2010 Jon A. Cruz * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. * @@ -31,6 +33,8 @@ struct SPGradientVectorSelector { guint idlabel : 1; + bool swatched; + SPDocument *doc; SPGradient *gr; @@ -40,6 +44,9 @@ struct SPGradientVectorSelector { sigc::connection gradient_release_connection; sigc::connection defs_release_connection; sigc::connection defs_modified_connection; + + + void setSwatched(); }; struct SPGradientVectorSelectorClass { diff --git a/src/widgets/icon.cpp b/src/widgets/icon.cpp index 743502d27..5d91d3532 100644 --- a/src/widgets/icon.cpp +++ b/src/widgets/icon.cpp @@ -1115,6 +1115,26 @@ static guchar *load_svg_pixels(gchar const *name, /* Create ArenaItem and set transform */ unsigned visionkey = sp_item_display_key_new(1); /* fixme: Memory manage root if needed (Lauris) */ + // This needs to be fixed indeed; this leads to a memory leak of a few megabytes these days + // because shapes are being rendered which are not being freed + // Valgrind output: + /*==7014== 1,548,344 bytes in 599 blocks are possibly lost in loss record 20,361 of 20,362 + ==7014== at 0x4A05974: operator new(unsigned long) (vg_replace_malloc.c:220) + ==7014== by 0x4F1015: __gnu_cxx::new_allocator<Shape::point_data>::allocate(unsigned long, void const*) (new_allocator.h:89) + ==7014== by 0x4F02AC: std::_Vector_base<Shape::point_data, std::allocator<Shape::point_data> >::_M_allocate(unsigned long) (stl_vector.h:140) + ==7014== by 0xCF62D7: std::vector<Shape::point_data, std::allocator<Shape::point_data> >::_M_fill_insert(__gnu_cxx::__normal_iterator<Shape::point_data*, std::vector<Shape::point_data, std::allocator<Shape::point_data> > >, unsigned long, Shape::point_data const&) (vector.tcc:414) + ==7014== by 0xCF4D45: std::vector<Shape::point_data, std::allocator<Shape::point_data> >::insert(__gnu_cxx::__normal_iterator<Shape::point_data*, std::vector<Shape::point_data, std::allocator<Shape::point_data> > >, unsigned long, Shape::point_data const&) (stl_vector.h:851) + ==7014== by 0xCF3DCD: std::vector<Shape::point_data, std::allocator<Shape::point_data> >::resize(unsigned long, Shape::point_data) (stl_vector.h:557) + ==7014== by 0xCEA771: Shape::AddPoint(Geom::Point) (Shape.cpp:326) + ==7014== by 0xD0F413: Shape::ConvertToShape(Shape*, fill_typ, bool) (ShapeSweep.cpp:257) + ==7014== by 0x5ECD4F: nr_arena_shape_update_stroke(NRArenaShape*, NRGC*, NRRectL*) (nr-arena-shape.cpp:651) + ==7014== by 0x5EE0DA: nr_arena_shape_render(_cairo*, NRArenaItem*, NRRectL*, NRPixBlock*, unsigned int) (nr-arena-shape.cpp:862) + ==7014== by 0x5E72FB: nr_arena_item_invoke_render(_cairo*, NRArenaItem*, NRRectL const*, NRPixBlock*, unsigned int) (nr-arena-item.cpp:578) + ==7014== by 0x5E9DDE: nr_arena_group_render(_cairo*, NRArenaItem*, NRRectL*, NRPixBlock*, unsigned int) (nr-arena-group.cpp:228) + ==7014== by 0x5E72FB: nr_arena_item_invoke_render(_cairo*, NRArenaItem*, NRRectL const*, NRPixBlock*, unsigned int) (nr-arena-item.cpp:578) + ==7014== by 0x5E9DDE: nr_arena_group_render(_cairo*, NRArenaItem*, NRRectL*, NRPixBlock*, unsigned int) (nr-arena-group.cpp:228) + ==7014== by 0x5E72FB: nr_arena_item_invoke_render(_cairo*, NRArenaItem*, NRRectL const*, NRPixBlock*, unsigned int) (nr-arena-item.cpp:578) + */ root = sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT(doc)), arena, visionkey, SP_ITEM_SHOW_DISPLAY ); diff --git a/src/widgets/paint-selector.cpp b/src/widgets/paint-selector.cpp index a101b9eeb..288764177 100644 --- a/src/widgets/paint-selector.cpp +++ b/src/widgets/paint-selector.cpp @@ -1,13 +1,16 @@ -#define __SP_PAINT_SELECTOR_C__ - /** \file * SPPaintSelector: Generic paint selector widget. */ /* - * Copyright (C) Lauris Kaplinski 2002 + * Authors: + * Lauris Kaplinski * bulia byak <buliabyak@users.sf.net> * John Cliff <simarilius@yahoo.com> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) Lauris Kaplinski 2002 + * Copyright (C) 2010 Authors */ #define noSP_PS_VERBOSE @@ -49,6 +52,7 @@ #include "io/sys.h" #include "helper/stock-items.h" #include "ui/icon-names.h" +#include "widgets/swatch-selector.h" #include "paint-selector.h" @@ -56,6 +60,9 @@ #include "svg/svg-icc-color.h" #endif // SP_PS_VERBOSE + +using Inkscape::Widgets::SwatchSelector; + enum { MODE_CHANGED, GRABBED, @@ -70,16 +77,17 @@ static void sp_paint_selector_class_init(SPPaintSelectorClass *klass); static void sp_paint_selector_init(SPPaintSelector *slider); static void sp_paint_selector_destroy(GtkObject *object); -static GtkWidget *sp_paint_selector_style_button_add(SPPaintSelector *psel, gchar const *px, SPPaintSelectorMode mode, GtkTooltips *tt, gchar const *tip); +static GtkWidget *sp_paint_selector_style_button_add(SPPaintSelector *psel, gchar const *px, SPPaintSelector::Mode mode, GtkTooltips *tt, gchar const *tip); static void sp_paint_selector_style_button_toggled(GtkToggleButton *tb, SPPaintSelector *psel); static void sp_paint_selector_fillrule_toggled(GtkToggleButton *tb, SPPaintSelector *psel); static void sp_paint_selector_set_mode_empty(SPPaintSelector *psel); static void sp_paint_selector_set_mode_multiple(SPPaintSelector *psel); static void sp_paint_selector_set_mode_none(SPPaintSelector *psel); -static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelectorMode mode); -static void sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelectorMode mode); -static void sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelectorMode mode); +static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelector::Mode mode); +static void sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelector::Mode mode); +static void sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelector::Mode mode); +static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSelector::Mode mode); static void sp_paint_selector_set_mode_unset(SPPaintSelector *psel); @@ -88,6 +96,48 @@ static void sp_paint_selector_set_style_buttons(SPPaintSelector *psel, GtkWidget static GtkVBoxClass *parent_class; static guint psel_signals[LAST_SIGNAL] = {0}; +#ifdef SP_PS_VERBOSE +static gchar const* modeStrings[] = { + "MODE_EMPTY", + "MODE_MULTIPLE", + "MODE_NONE", + "MODE_COLOR_RGB", + "MODE_COLOR_CMYK", + "MODE_GRADIENT_LINEAR", + "MODE_GRADIENT_RADIAL", + "MODE_PATTERN", + "MODE_SWATCH", + "MODE_UNSET", + ".", + ".", + ".", +}; +#endif + + +static bool isPaintModeGradient( SPPaintSelector::Mode mode ) +{ + bool isGrad = (mode == SPPaintSelector::MODE_GRADIENT_LINEAR) || + (mode == SPPaintSelector::MODE_GRADIENT_RADIAL) || + (mode == SPPaintSelector::MODE_SWATCH); + + return isGrad; +} + +static SPGradientSelector *getGradientFromData(SPPaintSelector const *psel) +{ + SPGradientSelector *grad = 0; + if (psel->mode == SPPaintSelector::MODE_SWATCH) { + SwatchSelector *swatchsel = static_cast<SwatchSelector*>(g_object_get_data(G_OBJECT(psel->selector), "swatch-selector")); + if (swatchsel) { + grad = swatchsel->getGradientSelector(); + } + } else { + grad = reinterpret_cast<SPGradientSelector*>(gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector")); + } + return grad; +} + GType sp_paint_selector_get_type(void) { static GtkType type = 0; @@ -168,7 +218,7 @@ sp_paint_selector_init(SPPaintSelector *psel) { GtkTooltips *tt = gtk_tooltips_new(); - psel->mode = (SPPaintSelectorMode)-1; // huh? do you mean 0xff? -- I think this means "not in the enum" + psel->mode = static_cast<SPPaintSelector::Mode>(-1); // huh? do you mean 0xff? -- I think this means "not in the enum" /* Paint style button box */ psel->style = gtk_hbox_new(FALSE, 0); @@ -178,17 +228,19 @@ sp_paint_selector_init(SPPaintSelector *psel) /* Buttons */ psel->none = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_NONE, - SP_PAINT_SELECTOR_MODE_NONE, tt, _("No paint")); + SPPaintSelector::MODE_NONE, tt, _("No paint")); psel->solid = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_SOLID, - SP_PAINT_SELECTOR_MODE_COLOR_RGB, tt, _("Flat color")); + SPPaintSelector::MODE_COLOR_RGB, tt, _("Flat color")); psel->gradient = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_GRADIENT_LINEAR, - SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR, tt, _("Linear gradient")); + SPPaintSelector::MODE_GRADIENT_LINEAR, tt, _("Linear gradient")); psel->radial = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_GRADIENT_RADIAL, - SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL, tt, _("Radial gradient")); + SPPaintSelector::MODE_GRADIENT_RADIAL, tt, _("Radial gradient")); psel->pattern = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_PATTERN, - SP_PAINT_SELECTOR_MODE_PATTERN, tt, _("Pattern")); + SPPaintSelector::MODE_PATTERN, tt, _("Pattern")); + psel->swatch = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_SWATCH, + SPPaintSelector::MODE_SWATCH, tt, _("Swatch")); psel->unset = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON_PAINT_UNKNOWN, - SP_PAINT_SELECTOR_MODE_UNSET, tt, _("Unset paint (make it undefined so it can be inherited)")); + SPPaintSelector::MODE_UNSET, tt, _("Unset paint (make it undefined so it can be inherited)")); /* Fillrule */ { @@ -201,7 +253,7 @@ sp_paint_selector_init(SPPaintSelector *psel) gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(psel->evenodd), FALSE); // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty gtk_tooltips_set_tip(tt, psel->evenodd, _("Any path self-intersections or subpaths create holes in the fill (fill-rule: evenodd)"), NULL); - gtk_object_set_data(GTK_OBJECT(psel->evenodd), "mode", GUINT_TO_POINTER(SP_PAINT_SELECTOR_FILLRULE_EVENODD)); + gtk_object_set_data(GTK_OBJECT(psel->evenodd), "mode", GUINT_TO_POINTER(SPPaintSelector::FILLRULE_EVENODD)); w = sp_icon_new(Inkscape::ICON_SIZE_DECORATION, INKSCAPE_ICON_FILL_RULE_EVEN_ODD); gtk_container_add(GTK_CONTAINER(psel->evenodd), w); gtk_box_pack_start(GTK_BOX(psel->fillrulebox), psel->evenodd, FALSE, FALSE, 0); @@ -212,7 +264,7 @@ sp_paint_selector_init(SPPaintSelector *psel) gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(psel->nonzero), FALSE); // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty gtk_tooltips_set_tip(tt, psel->nonzero, _("Fill is solid unless a subpath is counterdirectional (fill-rule: nonzero)"), NULL); - gtk_object_set_data(GTK_OBJECT(psel->nonzero), "mode", GUINT_TO_POINTER(SP_PAINT_SELECTOR_FILLRULE_NONZERO)); + gtk_object_set_data(GTK_OBJECT(psel->nonzero), "mode", GUINT_TO_POINTER(SPPaintSelector::FILLRULE_NONZERO)); w = sp_icon_new(Inkscape::ICON_SIZE_DECORATION, INKSCAPE_ICON_FILL_RULE_NONZERO); gtk_container_add(GTK_CONTAINER(psel->nonzero), w); gtk_box_pack_start(GTK_BOX(psel->fillrulebox), psel->nonzero, FALSE, FALSE, 0); @@ -242,10 +294,9 @@ sp_paint_selector_destroy(GtkObject *object) (* ((GtkObjectClass *) parent_class)->destroy)(object); } -static GtkWidget * -sp_paint_selector_style_button_add(SPPaintSelector *psel, - gchar const *pixmap, SPPaintSelectorMode mode, - GtkTooltips *tt, gchar const *tip) +static GtkWidget *sp_paint_selector_style_button_add(SPPaintSelector *psel, + gchar const *pixmap, SPPaintSelector::Mode mode, + GtkTooltips *tt, gchar const *tip) { GtkWidget *b, *w; @@ -274,7 +325,7 @@ static void sp_paint_selector_style_button_toggled(GtkToggleButton *tb, SPPaintSelector *psel) { if (!psel->update && gtk_toggle_button_get_active(tb)) { - sp_paint_selector_set_mode(psel, (SPPaintSelectorMode)GPOINTER_TO_UINT(gtk_object_get_data(GTK_OBJECT(tb), "mode"))); + psel->setMode(static_cast<SPPaintSelector::Mode>(GPOINTER_TO_UINT(gtk_object_get_data(GTK_OBJECT(tb), "mode")))); } } @@ -282,7 +333,7 @@ static void sp_paint_selector_fillrule_toggled(GtkToggleButton *tb, SPPaintSelector *psel) { if (!psel->update && gtk_toggle_button_get_active(tb)) { - SPPaintSelectorFillRule fr = (SPPaintSelectorFillRule)GPOINTER_TO_UINT(gtk_object_get_data(GTK_OBJECT(tb), "mode")); + SPPaintSelector::FillRule fr = static_cast<SPPaintSelector::FillRule>(GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(tb), "mode"))); gtk_signal_emit(GTK_OBJECT(psel), psel_signals[FILLRULE_CHANGED], fr); } } @@ -304,11 +355,9 @@ sp_paint_selector_show_fillrule(SPPaintSelector *psel, bool is_fill) GtkWidget * sp_paint_selector_new(bool is_fill) { - SPPaintSelector *psel; + SPPaintSelector *psel = static_cast<SPPaintSelector*>(gtk_type_new(SP_TYPE_PAINT_SELECTOR)); - psel = (SPPaintSelector*)gtk_type_new(SP_TYPE_PAINT_SELECTOR); - - sp_paint_selector_set_mode(psel, SP_PAINT_SELECTOR_MODE_MULTIPLE); + psel->setMode(SPPaintSelector::MODE_MULTIPLE); // This silliness is here because I don't know how to pass a parameter to the // GtkObject "constructor" (sp_paint_selector_init). Remove it when paint_selector @@ -318,63 +367,64 @@ sp_paint_selector_new(bool is_fill) return GTK_WIDGET(psel); } -void -sp_paint_selector_set_mode(SPPaintSelector *psel, SPPaintSelectorMode mode) +void SPPaintSelector::setMode(Mode mode) { - if (psel->mode != mode) { - psel->update = TRUE; + if (this->mode != mode) { + update = TRUE; #ifdef SP_PS_VERBOSE - g_print("Mode change %d -> %d\n", psel->mode, mode); + g_print("Mode change %d -> %d %s -> %s\n", this->mode, mode, modeStrings[this->mode], modeStrings[mode]); #endif switch (mode) { - case SP_PAINT_SELECTOR_MODE_EMPTY: - sp_paint_selector_set_mode_empty(psel); + case MODE_EMPTY: + sp_paint_selector_set_mode_empty(this); break; - case SP_PAINT_SELECTOR_MODE_MULTIPLE: - sp_paint_selector_set_mode_multiple(psel); + case MODE_MULTIPLE: + sp_paint_selector_set_mode_multiple(this); break; - case SP_PAINT_SELECTOR_MODE_NONE: - sp_paint_selector_set_mode_none(psel); + case MODE_NONE: + sp_paint_selector_set_mode_none(this); break; - case SP_PAINT_SELECTOR_MODE_COLOR_RGB: - case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: - sp_paint_selector_set_mode_color(psel, mode); + case MODE_COLOR_RGB: + case MODE_COLOR_CMYK: + sp_paint_selector_set_mode_color(this, mode); break; - case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR: - case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL: - sp_paint_selector_set_mode_gradient(psel, mode); + case MODE_GRADIENT_LINEAR: + case MODE_GRADIENT_RADIAL: + sp_paint_selector_set_mode_gradient(this, mode); break; - case SP_PAINT_SELECTOR_MODE_PATTERN: - sp_paint_selector_set_mode_pattern(psel, mode); + case MODE_PATTERN: + sp_paint_selector_set_mode_pattern(this, mode); break; - case SP_PAINT_SELECTOR_MODE_UNSET: - sp_paint_selector_set_mode_unset(psel); + case MODE_SWATCH: + sp_paint_selector_set_mode_swatch(this, mode); + break; + case MODE_UNSET: + sp_paint_selector_set_mode_unset(this); break; default: g_warning("file %s: line %d: Unknown paint mode %d", __FILE__, __LINE__, mode); break; } - psel->mode = mode; - gtk_signal_emit(GTK_OBJECT(psel), psel_signals[MODE_CHANGED], psel->mode); - psel->update = FALSE; + this->mode = mode; + gtk_signal_emit(GTK_OBJECT(this), psel_signals[MODE_CHANGED], this->mode); + update = FALSE; } } -void -sp_paint_selector_set_fillrule(SPPaintSelector *psel, SPPaintSelectorFillRule fillrule) +void SPPaintSelector::setFillrule(FillRule fillrule) { - if (psel->fillrulebox) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->evenodd), (fillrule == SP_PAINT_SELECTOR_FILLRULE_EVENODD)); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->nonzero), (fillrule == SP_PAINT_SELECTOR_FILLRULE_NONZERO)); + if (fillrulebox) { + // TODO this flips widgets but does not use a member to store state. Revisit + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(evenodd), (fillrule == FILLRULE_EVENODD)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(nonzero), (fillrule == FILLRULE_NONZERO)); } } -void -sp_paint_selector_set_color_alpha(SPPaintSelector *psel, SPColor const *color, float alpha) +void SPPaintSelector::setColorAlpha(SPColor const &color, float alpha) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); - SPColorSelector *csel; - guint32 rgba; + SPColorSelector *csel = 0; + guint32 rgba = 0; /* if ( sp_color_get_colorspace_type(color) == SP_COLORSPACE_TYPE_CMYK ) @@ -382,7 +432,7 @@ sp_paint_selector_set_color_alpha(SPPaintSelector *psel, SPColor const *color, f #ifdef SP_PS_VERBOSE g_print("PaintSelector set CMYKA\n"); #endif - sp_paint_selector_set_mode(psel, SP_PAINT_SELECTOR_MODE_COLOR_CMYK); + sp_paint_selector_set_mode(psel, MODE_COLOR_CMYK); } else */ @@ -390,104 +440,105 @@ sp_paint_selector_set_color_alpha(SPPaintSelector *psel, SPColor const *color, f #ifdef SP_PS_VERBOSE g_print("PaintSelector set RGBA\n"); #endif - sp_paint_selector_set_mode(psel, SP_PAINT_SELECTOR_MODE_COLOR_RGB); + setMode(MODE_COLOR_RGB); } - csel = (SPColorSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "color-selector"); - rgba = color->toRGBA32( alpha ); - csel->base->setColorAlpha( *color, alpha ); + csel = reinterpret_cast<SPColorSelector*>(gtk_object_get_data(GTK_OBJECT(selector), "color-selector")); + rgba = color.toRGBA32( alpha ); + csel->base->setColorAlpha( color, alpha ); } -void -sp_paint_selector_set_gradient_linear(SPPaintSelector *psel, SPGradient *vector) +void SPPaintSelector::setSwatch(SPGradient *vector ) +{ +#ifdef SP_PS_VERBOSE + g_print("PaintSelector set SWATCH\n"); +#endif + setMode(MODE_SWATCH); + + SwatchSelector *swatchsel = static_cast<SwatchSelector*>(g_object_get_data(G_OBJECT(selector), "swatch-selector")); + if (swatchsel) { + swatchsel->setVector( (vector) ? SP_OBJECT_DOCUMENT(vector) : 0, vector ); + } +} + +void SPPaintSelector::setGradientLinear(SPGradient *vector) { - SPGradientSelector *gsel; #ifdef SP_PS_VERBOSE g_print("PaintSelector set GRADIENT LINEAR\n"); #endif - sp_paint_selector_set_mode(psel, SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR); + setMode(MODE_GRADIENT_LINEAR); - gsel = (SPGradientSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector"); + SPGradientSelector *gsel = getGradientFromData(this); - sp_gradient_selector_set_mode(gsel, SP_GRADIENT_SELECTOR_MODE_LINEAR); - sp_gradient_selector_set_vector(gsel, (vector) ? SP_OBJECT_DOCUMENT(vector) : NULL, vector); + gsel->setMode(SPGradientSelector::MODE_LINEAR); + gsel->setVector((vector) ? SP_OBJECT_DOCUMENT(vector) : 0, vector); } -void -sp_paint_selector_set_gradient_radial(SPPaintSelector *psel, SPGradient *vector) +void SPPaintSelector::setGradientRadial(SPGradient *vector) { - SPGradientSelector *gsel; #ifdef SP_PS_VERBOSE g_print("PaintSelector set GRADIENT RADIAL\n"); #endif - sp_paint_selector_set_mode(psel, SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL); + setMode(MODE_GRADIENT_RADIAL); - gsel = (SPGradientSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector"); + SPGradientSelector *gsel = getGradientFromData(this); - sp_gradient_selector_set_mode(gsel, SP_GRADIENT_SELECTOR_MODE_RADIAL); - sp_gradient_selector_set_vector(gsel, (vector) ? SP_OBJECT_DOCUMENT(vector) : NULL, vector); + gsel->setMode(SPGradientSelector::MODE_RADIAL); + + gsel->setVector((vector) ? SP_OBJECT_DOCUMENT(vector) : 0, vector); } -void -sp_paint_selector_set_gradient_properties(SPPaintSelector *psel, SPGradientUnits units, SPGradientSpread spread) +void SPPaintSelector::setGradientProperties( SPGradientUnits units, SPGradientSpread spread ) { - SPGradientSelector *gsel; - g_return_if_fail(SP_IS_PAINT_SELECTOR(psel)); - g_return_if_fail((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) || - (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL)); - gsel = (SPGradientSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector"); - sp_gradient_selector_set_units(gsel, units); - sp_gradient_selector_set_spread(gsel, spread); + g_return_if_fail(isPaintModeGradient(mode)); + + SPGradientSelector *gsel = getGradientFromData(this); + gsel->setUnits(units); + gsel->setSpread(spread); } -void -sp_paint_selector_get_gradient_properties(SPPaintSelector *psel, SPGradientUnits *units, SPGradientSpread *spread) +void SPPaintSelector::getGradientProperties( SPGradientUnits &units, SPGradientSpread &spread) const { - SPGradientSelector *gsel; - g_return_if_fail(SP_IS_PAINT_SELECTOR(psel)); - g_return_if_fail((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) || - (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL)); - gsel = (SPGradientSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector"); - if (units) *units = sp_gradient_selector_get_units(gsel); - if (spread) *spread = sp_gradient_selector_get_spread(gsel); + g_return_if_fail(isPaintModeGradient(mode)); + + SPGradientSelector *gsel = getGradientFromData(this); + units = gsel->getUnits(); + spread = gsel->getSpread(); } /** * \post (alpha == NULL) || (*alpha in [0.0, 1.0]). */ -void -sp_paint_selector_get_color_alpha(SPPaintSelector *psel, SPColor *color, gfloat *alpha) +void SPPaintSelector::getColorAlpha(SPColor &color, gfloat &alpha) const { SPColorSelector *csel; - csel = (SPColorSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "color-selector"); + csel = reinterpret_cast<SPColorSelector*>(g_object_get_data(G_OBJECT(selector), "color-selector")); - csel->base->getColorAlpha( *color, alpha ); + csel->base->getColorAlpha( color, alpha ); - g_assert( !alpha - || ( ( 0.0 <= *alpha ) - && ( *alpha <= 1.0 ) ) ); + g_assert( ( 0.0 <= alpha ) + && ( alpha <= 1.0 ) ); } -SPGradient * -sp_paint_selector_get_gradient_vector(SPPaintSelector *psel) +SPGradient *SPPaintSelector::getGradientVector() { - SPGradientSelector *gsel; + SPGradient* vect = 0; - g_return_val_if_fail((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) || - (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL), NULL); - - gsel = (SPGradientSelector*)gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector"); + if (isPaintModeGradient(mode)) { + SPGradientSelector *gsel = getGradientFromData(this); + vect = gsel->getVector(); + } - return sp_gradient_selector_get_vector(gsel); + return vect; } -void -sp_gradient_selector_attrs_to_gradient(SPGradient *gr, SPPaintSelector *psel) + +void SPPaintSelector::pushAttrsToGradient( SPGradient *gr ) const { - SPGradientUnits units; - SPGradientSpread spread; - sp_paint_selector_get_gradient_properties(psel, &units, &spread); + SPGradientUnits units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX; + SPGradientSpread spread = SP_GRADIENT_SPREAD_PAD; + getGradientProperties( units, spread ); sp_gradient_set_units(gr, units); sp_gradient_set_spread(gr, spread); SP_OBJECT(gr)->updateRepr(); @@ -566,45 +617,37 @@ sp_paint_selector_set_mode_none(SPPaintSelector *psel) /* Color paint */ -static void -sp_paint_selector_color_grabbed(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_color_grabbed(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[GRABBED]); } -static void -sp_paint_selector_color_dragged(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_color_dragged(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[DRAGGED]); } -static void -sp_paint_selector_color_released(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_color_released(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[RELEASED]); } static void sp_paint_selector_color_changed(SPColorSelector *csel, SPPaintSelector *psel) { - csel->base->getColorAlpha( psel->color, &psel->alpha ); + csel->base->getColorAlpha( psel->color, psel->alpha ); gtk_signal_emit(GTK_OBJECT(psel), psel_signals[CHANGED]); } -static void -sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelectorMode mode) +static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelector::Mode /*mode*/) { - (void)mode; GtkWidget *csel; sp_paint_selector_set_style_buttons(psel, psel->solid); gtk_widget_set_sensitive(psel->style, TRUE); - if ((psel->mode == SP_PAINT_SELECTOR_MODE_COLOR_RGB) || (psel->mode == SP_PAINT_SELECTOR_MODE_COLOR_CMYK)) { + if ((psel->mode == SPPaintSelector::MODE_COLOR_RGB) || (psel->mode == SPPaintSelector::MODE_COLOR_CMYK)) { /* Already have color selector */ csel = (GtkWidget*)gtk_object_get_data(GTK_OBJECT(psel->selector), "color-selector"); } else { @@ -641,49 +684,40 @@ sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelectorMode mode /* Gradient */ -static void -sp_paint_selector_gradient_grabbed(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_gradient_grabbed(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[GRABBED]); } -static void -sp_paint_selector_gradient_dragged(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_gradient_dragged(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[DRAGGED]); } -static void -sp_paint_selector_gradient_released(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_gradient_released(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[RELEASED]); } -static void -sp_paint_selector_gradient_changed(SPColorSelector *csel, SPPaintSelector *psel) +static void sp_paint_selector_gradient_changed(SPColorSelector * /*csel*/, SPPaintSelector *psel) { - (void)csel; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[CHANGED]); } -static void -sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelectorMode mode) +static void sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelector::Mode mode) { GtkWidget *gsel; /* fixme: We do not need function-wide gsel at all */ - if (mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) { + if (mode == SPPaintSelector::MODE_GRADIENT_LINEAR) { sp_paint_selector_set_style_buttons(psel, psel->gradient); } else { sp_paint_selector_set_style_buttons(psel, psel->radial); } gtk_widget_set_sensitive(psel->style, TRUE); - if ((psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) || (psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL)) { + if ((psel->mode == SPPaintSelector::MODE_GRADIENT_LINEAR) || (psel->mode == SPPaintSelector::MODE_GRADIENT_RADIAL)) { /* Already have gradient selector */ gsel = (GtkWidget*)gtk_object_get_data(GTK_OBJECT(psel->selector), "gradient-selector"); } else { @@ -702,11 +736,12 @@ sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelectorMode m } /* Actually we have to set option menu history here */ - if (mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR) { - sp_gradient_selector_set_mode(SP_GRADIENT_SELECTOR(gsel), SP_GRADIENT_SELECTOR_MODE_LINEAR); + if (mode == SPPaintSelector::MODE_GRADIENT_LINEAR) { + SP_GRADIENT_SELECTOR(gsel)->setMode(SPGradientSelector::MODE_LINEAR); + //sp_gradient_selector_set_mode(SP_GRADIENT_SELECTOR(gsel), SP_GRADIENT_SELECTOR_MODE_LINEAR); gtk_frame_set_label(GTK_FRAME(psel->frame), _("Linear gradient")); } else { - sp_gradient_selector_set_mode(SP_GRADIENT_SELECTOR(gsel), SP_GRADIENT_SELECTOR_MODE_RADIAL); + SP_GRADIENT_SELECTOR(gsel)->setMode(SPGradientSelector::MODE_RADIAL); gtk_frame_set_label(GTK_FRAME(psel->frame), _("Radial gradient")); } #ifdef SP_PS_VERBOSE @@ -722,21 +757,18 @@ sp_paint_selector_set_style_buttons(SPPaintSelector *psel, GtkWidget *active) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->gradient), (active == psel->gradient)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->radial), (active == psel->radial)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->pattern), (active == psel->pattern)); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->swatch), (active == psel->swatch)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->unset), (active == psel->unset)); } -static void -sp_psel_pattern_destroy(GtkWidget *widget, SPPaintSelector *psel) +static void sp_psel_pattern_destroy(GtkWidget *widget, SPPaintSelector * /*psel*/) { - (void)psel; // drop our reference to the pattern menu widget g_object_unref( G_OBJECT(widget) ); } -static void -sp_psel_pattern_change(GtkWidget *widget, SPPaintSelector *psel) +static void sp_psel_pattern_change(GtkWidget * /*widget*/, SPPaintSelector *psel) { - (void)widget; gtk_signal_emit(GTK_OBJECT(psel), psel_signals[CHANGED]); } @@ -812,17 +844,15 @@ sp_pattern_menu_build (GtkWidget *m, GSList *pattern_list, SPDocument */*source* * current_doc (if non-NULL), and add items to the pattern menu * */ -static void -sp_pattern_list_from_doc (GtkWidget *m, SPDocument *current_doc, SPDocument *source, SPDocument *pattern_doc) +static void sp_pattern_list_from_doc (GtkWidget *m, SPDocument * /*current_doc*/, SPDocument *source, SPDocument * /*pattern_doc*/) { - (void)current_doc; - (void)pattern_doc; GSList *pl = ink_pattern_list_get(source); GSList *clean_pl = NULL; for (; pl != NULL; pl = pl->next) { - if (!SP_IS_PATTERN(pl->data)) + if (!SP_IS_PATTERN(pl->data)) { continue; + } // Add to the list of patterns we really do wish to show clean_pl = g_slist_prepend (clean_pl, pl->data); @@ -901,11 +931,12 @@ ink_pattern_menu(GtkWidget *mnu) /*update pattern list*/ -void -sp_update_pattern_list( SPPaintSelector *psel, SPPattern *pattern) +void SPPaintSelector::updatePatternList( SPPattern *pattern ) { - if (psel->update) return; - GtkWidget *mnu = (GtkWidget *)g_object_get_data(G_OBJECT(psel), "patternmenu"); + if (update) { + return; + } + GtkWidget *mnu = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "patternmenu")); g_assert( mnu != NULL ); /* Clear existing menu if any */ @@ -944,17 +975,17 @@ sp_update_pattern_list( SPPaintSelector *psel, SPPattern *pattern) //gtk_option_menu_set_history(GTK_OPTION_MENU(mnu), 0); } -static void -sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelectorMode mode) +static void sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelector::Mode mode) { - if (mode == SP_PAINT_SELECTOR_MODE_PATTERN) + if (mode == SPPaintSelector::MODE_PATTERN) { sp_paint_selector_set_style_buttons(psel, psel->pattern); + } gtk_widget_set_sensitive(psel->style, TRUE); GtkWidget *tbl = NULL; - if (psel->mode == SP_PAINT_SELECTOR_MODE_PATTERN){ + if (psel->mode == SPPaintSelector::MODE_PATTERN) { /* Already have pattern menu */ tbl = (GtkWidget*)gtk_object_get_data(GTK_OBJECT(psel->selector), "pattern-selector"); } else { @@ -1001,13 +1032,12 @@ sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelectorMode mo #endif } -SPPattern * -sp_paint_selector_get_pattern(SPPaintSelector *psel) +SPPattern *SPPaintSelector::getPattern() { - SPPattern *pat; - g_return_val_if_fail((psel->mode == SP_PAINT_SELECTOR_MODE_PATTERN) , NULL); + SPPattern *pat = 0; + g_return_val_if_fail((mode == MODE_PATTERN) , NULL); - GtkWidget *patmnu = (GtkWidget *) g_object_get_data(G_OBJECT(psel), "patternmenu"); + GtkWidget *patmnu = (GtkWidget *) g_object_get_data(G_OBJECT(this), "patternmenu"); /* no pattern menu if we were just selected */ if ( patmnu == NULL ) return NULL; @@ -1019,34 +1049,73 @@ sp_paint_selector_get_pattern(SPPaintSelector *psel) return NULL; } gchar *patid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), - "pattern"); + "pattern"); //gchar *pattern = ""; if (strcmp(patid, "none")){ - gchar *stockid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), - "stockid"); - gchar *paturn = patid; - if (!strcmp(stockid,"true")) paturn = g_strconcat("urn:inkscape:pattern:",patid,NULL); - SPObject *pat_obj = get_stock_item(paturn); - if (pat_obj) { + gchar *stockid = (gchar *) g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), + "stockid"); + gchar *paturn = patid; + if (!strcmp(stockid,"true")) paturn = g_strconcat("urn:inkscape:pattern:",patid,NULL); + SPObject *pat_obj = get_stock_item(paturn); + if (pat_obj) { pat = SP_PATTERN(pat_obj); } } else { - pat = pattern_getroot(SP_PATTERN(g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), "pattern"))); + pat = pattern_getroot(SP_PATTERN(g_object_get_data(G_OBJECT(gtk_menu_get_active(m)), "pattern"))); } - if SP_IS_PATTERN(pat) return pat; - return NULL; + if (pat && !SP_IS_PATTERN(pat)) { + pat = 0; + } + + return pat; } -void -sp_paint_selector_set_flat_color(SPPaintSelector *psel, SPDesktop *desktop, gchar const *color_property, gchar const *opacity_property) +static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSelector::Mode mode) +{ + if (mode == SPPaintSelector::MODE_SWATCH) { + sp_paint_selector_set_style_buttons(psel, psel->swatch); + } + + gtk_widget_set_sensitive(psel->style, TRUE); + + SwatchSelector *swatchsel = 0; + + if (psel->mode == SPPaintSelector::MODE_SWATCH){ + /* Already have pattern menu */ + swatchsel = static_cast<SwatchSelector*>(g_object_get_data(G_OBJECT(psel->selector), "swatch-selector")); + } else { + sp_paint_selector_clear_frame(psel); + /* Create new gradient selector */ + SwatchSelector *swatchsel = new SwatchSelector(); + swatchsel->show(); + + swatchsel->connectGrabbedHandler( G_CALLBACK(sp_paint_selector_gradient_grabbed), psel ); + swatchsel->connectDraggedHandler( G_CALLBACK(sp_paint_selector_gradient_dragged), psel ); + swatchsel->connectReleasedHandler( G_CALLBACK(sp_paint_selector_gradient_released), psel ); + swatchsel->connectchangedHandler( G_CALLBACK(sp_paint_selector_gradient_changed), psel ); + + // Pack everything to frame + gtk_container_add(GTK_CONTAINER(psel->frame), GTK_WIDGET(swatchsel->gobj())); + psel->selector = GTK_WIDGET(swatchsel->gobj()); + gtk_object_set_data(GTK_OBJECT(psel->selector), "swatch-selector", swatchsel); + + gtk_frame_set_label(GTK_FRAME(psel->frame), _("Swatch fill")); + } +#ifdef SP_PS_VERBOSE + g_print("Swatch req\n"); +#endif +} + +// TODO this seems very bad to be taking in a desktop pointer to muck with. Logic probably belongs elsewhere +void SPPaintSelector::setFlatColor( SPDesktop *desktop, gchar const *color_property, gchar const *opacity_property ) { SPCSSAttr *css = sp_repr_css_attr_new(); SPColor color; - gfloat alpha; - sp_paint_selector_get_color_alpha(psel, &color, &alpha); + gfloat alpha = 0; + getColorAlpha( color, alpha ); std::string colorStr = color.toString(); @@ -1055,7 +1124,7 @@ sp_paint_selector_set_flat_color(SPPaintSelector *psel, SPDesktop *desktop, gcha g_message("sp_paint_selector_set_flat_color() to '%s' from 0x%08x::%s", colorStr.c_str(), rgba, - (color.icc?color.icc->colorProfile.c_str():"<null>") ); + (color.icc ? color.icc->colorProfile.c_str():"<null>") ); #endif // SP_PS_VERBOSE sp_repr_css_set_property(css, color_property, colorStr.c_str()); @@ -1068,34 +1137,42 @@ sp_paint_selector_set_flat_color(SPPaintSelector *psel, SPDesktop *desktop, gcha sp_repr_css_attr_unref(css); } -SPPaintSelectorMode -sp_style_determine_paint_selector_mode(SPStyle *style, bool isfill) +SPPaintSelector::Mode SPPaintSelector::getModeForStyle(SPStyle const & style, bool isfill) { - SPPaintSelectorMode mode = SP_PAINT_SELECTOR_MODE_UNSET; - SPIPaint& target = isfill ? style->fill : style->stroke; + Mode mode = MODE_UNSET; + SPIPaint const & target = isfill ? style.fill : style.stroke; if ( !target.set ) { - mode = SP_PAINT_SELECTOR_MODE_UNSET; + mode = MODE_UNSET; } else if ( target.isPaintserver() ) { - SPPaintServer *server = isfill? SP_STYLE_FILL_SERVER(style) : SP_STYLE_STROKE_SERVER(style); + SPPaintServer const *server = isfill ? style.getFillPaintServer() : style.getStrokePaintServer(); + +#ifdef SP_PS_VERBOSE + g_message("SPPaintSelector::getModeForStyle(%p, %d)", &style, isfill); + g_message("==== server:%p %s grad:%s swatch:%s", server, server->getId(), (SP_IS_GRADIENT(server)?"Y":"n"), (SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()?"Y":"n")); +#endif // SP_PS_VERBOSE + - if (SP_IS_LINEARGRADIENT(server)) { - mode = SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR; + if (server && SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()) { + mode = MODE_SWATCH; + } else if (SP_IS_LINEARGRADIENT(server)) { + mode = MODE_GRADIENT_LINEAR; } else if (SP_IS_RADIALGRADIENT(server)) { - mode = SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL; + mode = MODE_GRADIENT_RADIAL; } else if (SP_IS_PATTERN(server)) { - mode = SP_PAINT_SELECTOR_MODE_PATTERN; + mode = MODE_PATTERN; } else { g_warning( "file %s: line %d: Unknown paintserver", __FILE__, __LINE__ ); - mode = SP_PAINT_SELECTOR_MODE_NONE; + mode = MODE_NONE; } } else if ( target.isColor() ) { - mode = SP_PAINT_SELECTOR_MODE_COLOR_RGB; // so far only rgb can be read from svg + // TODO this is no longer a valid assertion: + mode = MODE_COLOR_RGB; // so far only rgb can be read from svg } else if ( target.isNone() ) { - mode = SP_PAINT_SELECTOR_MODE_NONE; + mode = MODE_NONE; } else { g_warning( "file %s: line %d: Unknown paint type", __FILE__, __LINE__ ); - mode = SP_PAINT_SELECTOR_MODE_NONE; + mode = MODE_NONE; } return mode; diff --git a/src/widgets/paint-selector.h b/src/widgets/paint-selector.h index de8bbf46a..a3c4cb973 100644 --- a/src/widgets/paint-selector.h +++ b/src/widgets/paint-selector.h @@ -1,16 +1,22 @@ -#ifndef __SP_PAINT_SELECTOR_H__ -#define __SP_PAINT_SELECTOR_H__ +#ifndef SEEN_SP_PAINT_SELECTOR_H +#define SEEN_SP_PAINT_SELECTOR_H /** \file * Generic paint selector widget * + * Authors: + * Lauris + * Jon A. Cruz <jon@joncruz.org> + * * Copyright (C) Lauris 2002 + * Copyright (C) 2010 Authors * */ #include <glib.h> #include "sp-gradient-spread.h" #include "sp-gradient-units.h" + class SPGradient; #define SP_TYPE_PAINT_SELECTOR (sp_paint_selector_get_type ()) @@ -25,86 +31,102 @@ class SPGradient; #include <color.h> #include <libnr/nr-forward.h> -typedef enum { - SP_PAINT_SELECTOR_MODE_EMPTY, - SP_PAINT_SELECTOR_MODE_MULTIPLE, - SP_PAINT_SELECTOR_MODE_NONE, - SP_PAINT_SELECTOR_MODE_COLOR_RGB, - SP_PAINT_SELECTOR_MODE_COLOR_CMYK, - SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR, - SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL, - SP_PAINT_SELECTOR_MODE_PATTERN, - SP_PAINT_SELECTOR_MODE_UNSET -} SPPaintSelectorMode; - -typedef enum { - SP_PAINT_SELECTOR_FILLRULE_NONZERO, - SP_PAINT_SELECTOR_FILLRULE_EVENODD -} SPPaintSelectorFillRule; /// Generic paint selector widget struct SPPaintSelector { - GtkVBox vbox; + GtkVBox vbox; - guint update : 1; + enum Mode { + MODE_EMPTY, + MODE_MULTIPLE, + MODE_NONE, + MODE_COLOR_RGB, + MODE_COLOR_CMYK, + MODE_GRADIENT_LINEAR, + MODE_GRADIENT_RADIAL, + MODE_PATTERN, + MODE_SWATCH, + MODE_UNSET + } ; - SPPaintSelectorMode mode; + enum FillRule { + FILLRULE_NONZERO, + FILLRULE_EVENODD + } ; - GtkWidget *style; - GtkWidget *none, *solid, *gradient, *radial, *pattern, *unset; + guint update : 1; - GtkWidget *fillrulebox; - GtkWidget *evenodd, *nonzero; + Mode mode; - GtkWidget *frame, *selector; + GtkWidget *style; + GtkWidget *none; + GtkWidget *solid; + GtkWidget *gradient; + GtkWidget *radial; + GtkWidget *pattern; + GtkWidget *swatch; + GtkWidget *unset; - SPColor color; - float alpha; -}; + GtkWidget *fillrulebox; + GtkWidget *evenodd, *nonzero; -/// The SPPaintSelector vtable -struct SPPaintSelectorClass { - GtkVBoxClass parent_class; + GtkWidget *frame, *selector; - void (* mode_changed) (SPPaintSelector *psel, SPPaintSelectorMode mode); + SPColor color; + float alpha; - void (* grabbed) (SPPaintSelector *psel); - void (* dragged) (SPPaintSelector *psel); - void (* released) (SPPaintSelector *psel); - void (* changed) (SPPaintSelector *psel); - void (* fillrule_changed) (SPPaintSelector *psel, SPPaintSelectorFillRule fillrule); -}; + static Mode getModeForStyle(SPStyle const & style, bool isfill); -GtkType sp_paint_selector_get_type (void); + void setMode( Mode mode ); + void setFillrule( FillRule fillrule ); -GtkWidget *sp_paint_selector_new (bool is_fill); + void setColorAlpha( SPColor const &color, float alpha ); + void getColorAlpha( SPColor &color, gfloat &alpha ) const; -void sp_paint_selector_set_mode (SPPaintSelector *psel, SPPaintSelectorMode mode); -void sp_paint_selector_set_fillrule (SPPaintSelector *psel, SPPaintSelectorFillRule fillrule); + void setGradientLinear( SPGradient *vector ); + void setGradientRadial( SPGradient *vector ); + void setSwatch( SPGradient *vector ); -void sp_paint_selector_set_color_alpha (SPPaintSelector *psel, const SPColor *color, float alpha); + void setGradientProperties( SPGradientUnits units, SPGradientSpread spread ); + void getGradientProperties( SPGradientUnits &units, SPGradientSpread &spread ) const; -void sp_paint_selector_set_gradient_linear (SPPaintSelector *psel, SPGradient *vector); + void pushAttrsToGradient( SPGradient *gr ) const; + SPGradient *getGradientVector(); + SPPattern * getPattern(); + void updatePatternList( SPPattern *pat ); -void sp_paint_selector_set_gradient_radial (SPPaintSelector *psel, SPGradient *vector); - -void sp_paint_selector_set_gradient_properties (SPPaintSelector *psel, SPGradientUnits units, SPGradientSpread spread); -void sp_paint_selector_get_gradient_properties (SPPaintSelector *psel, SPGradientUnits *units, SPGradientSpread *spread); + // TODO move this elsewhere: + void setFlatColor( SPDesktop *desktop, const gchar *color_property, const gchar *opacity_property ); +}; -void sp_gradient_selector_attrs_to_gradient (SPGradient *gr, SPPaintSelector *psel); +/// The SPPaintSelector vtable +struct SPPaintSelectorClass { + GtkVBoxClass parent_class; -void sp_paint_selector_get_color_alpha (SPPaintSelector *psel, SPColor *color, gfloat *alpha); + void (* mode_changed) (SPPaintSelector *psel, SPPaintSelector::Mode mode); -SPGradient *sp_paint_selector_get_gradient_vector (SPPaintSelector *psel); + void (* grabbed) (SPPaintSelector *psel); + void (* dragged) (SPPaintSelector *psel); + void (* released) (SPPaintSelector *psel); + void (* changed) (SPPaintSelector *psel); + void (* fillrule_changed) (SPPaintSelector *psel, SPPaintSelector::FillRule fillrule); +}; -void sp_paint_selector_system_color_set (SPPaintSelector *psel, const SPColor *color, float opacity); +GtkType sp_paint_selector_get_type (void); -SPPattern * sp_paint_selector_get_pattern (SPPaintSelector *psel); +GtkWidget *sp_paint_selector_new (bool is_fill); -void sp_update_pattern_list ( SPPaintSelector *psel, SPPattern *pat); -void sp_paint_selector_set_flat_color (SPPaintSelector *psel, SPDesktop *desktop, const gchar *color_property, const gchar *opacity_property); -SPPaintSelectorMode sp_style_determine_paint_selector_mode (SPStyle *style, bool isfill); +#endif // SEEN_SP_PAINT_SELECTOR_H -#endif +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/widgets/select-toolbar.cpp b/src/widgets/select-toolbar.cpp index f168cedeb..e2ad61e2d 100644 --- a/src/widgets/select-toolbar.cpp +++ b/src/widgets/select-toolbar.cpp @@ -390,7 +390,7 @@ static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::Vi void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { Inkscape::UI::View::View *view = desktop; - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = Inkscape::UI::ToolboxFactory::prefToSize("/toolbox/secondary", 1); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); GtkAction* act = 0; @@ -457,7 +457,7 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - eact = create_adjustment_action( "XAction", _("select_toolbar|X position"), _("select_toolbar|X"), "X", + eact = create_adjustment_action( "XAction", _("select toolbar|X position"), _("select toolbar|X"), "X", -1e6, GTK_WIDGET(desktop->canvas), tracker, spw, _("Horizontal coordinate of selection"), TRUE ); gtk_action_group_add_action( selectionActions, GTK_ACTION(eact) ); @@ -465,7 +465,7 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - eact = create_adjustment_action( "YAction", _("select_toolbar|Y position"), _("select_toolbar|Y"), "Y", + eact = create_adjustment_action( "YAction", _("select toolbar|Y position"), _("select toolbar|Y"), "Y", -1e6, GTK_WIDGET(desktop->canvas), tracker, spw, _("Vertical coordinate of selection"), FALSE ); gtk_action_group_add_action( selectionActions, GTK_ACTION(eact) ); @@ -473,7 +473,7 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - eact = create_adjustment_action( "WidthAction", _("select_toolbar|Width"), _("select_toolbar|W"), "width", + eact = create_adjustment_action( "WidthAction", _("select toolbar|Width"), _("select toolbar|W"), "width", 1e-3, GTK_WIDGET(desktop->canvas), tracker, spw, _("Width of selection"), FALSE ); gtk_action_group_add_action( selectionActions, GTK_ACTION(eact) ); @@ -494,7 +494,7 @@ void sp_select_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GOb //TRANSLATORS: only translate "string" in "context|string". // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS - eact = create_adjustment_action( "HeightAction", _("select_toolbar|Height"), _("select_toolbar|H"), "height", + eact = create_adjustment_action( "HeightAction", _("select toolbar|Height"), _("select toolbar|H"), "height", 1e-3, GTK_WIDGET(desktop->canvas), tracker, spw, _("Height of selection"), FALSE ); gtk_action_group_add_action( selectionActions, GTK_ACTION(eact) ); diff --git a/src/widgets/sp-color-notebook.cpp b/src/widgets/sp-color-notebook.cpp index 3ba39dd30..a249786ff 100644 --- a/src/widgets/sp-color-notebook.cpp +++ b/src/widgets/sp-color-notebook.cpp @@ -4,7 +4,7 @@ * A notebook with RGB, CMYK, CMS, HSL, and Wheel pages * * Author: - * Lauris Kaplinski <lauris@kaplinski.com> + * Lauris Kaplinski <lauris@kaplinski.com> * bulia byak <buliabyak@users.sf.net> * * Copyright (C) 2001-2002 Lauris Kaplinski @@ -38,13 +38,13 @@ #include "../profile-manager.h" struct SPColorNotebookTracker { - const gchar* name; - const gchar* className; - GType type; - guint submode; - gboolean enabledFull; - gboolean enabledBrief; - SPColorNotebook *backPointer; + const gchar* name; + const gchar* className; + GType type; + guint submode; + gboolean enabledFull; + gboolean enabledBrief; + SPColorNotebook *backPointer; }; static void sp_color_notebook_class_init (SPColorNotebookClass *klass); @@ -83,20 +83,20 @@ GType sp_color_notebook_get_type(void) static void sp_color_notebook_class_init (SPColorNotebookClass *klass) { - GtkObjectClass *object_class; - GtkWidgetClass *widget_class; - SPColorSelectorClass *selector_class; + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + SPColorSelectorClass *selector_class; - object_class = (GtkObjectClass *) klass; - widget_class = (GtkWidgetClass *) klass; - selector_class = SP_COLOR_SELECTOR_CLASS (klass); + object_class = (GtkObjectClass *) klass; + widget_class = (GtkWidgetClass *) klass; + selector_class = SP_COLOR_SELECTOR_CLASS (klass); - parent_class = SP_COLOR_SELECTOR_CLASS (g_type_class_peek_parent (klass)); + parent_class = SP_COLOR_SELECTOR_CLASS (g_type_class_peek_parent (klass)); - object_class->destroy = sp_color_notebook_destroy; + object_class->destroy = sp_color_notebook_destroy; - widget_class->show_all = sp_color_notebook_show_all; - widget_class->hide_all = sp_color_notebook_hide_all; + widget_class->show_all = sp_color_notebook_show_all; + widget_class->hide_all = sp_color_notebook_hide_all; } static void @@ -126,7 +126,7 @@ void ColorNotebook::switchPage(GtkNotebook*, if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) >= 0 ) { csel = getCurrentSelector(); - csel->base->getColorAlpha(_color, &_alpha); + csel->base->getColorAlpha(_color, _alpha); } widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), page_num); if ( widget && SP_IS_COLOR_SELECTOR (widget) ) @@ -165,21 +165,21 @@ gint ColorNotebook::menuHandler( GdkEvent* event ) static void sp_color_notebook_menuitem_response (GtkMenuItem *menuitem, gpointer user_data) { - gboolean active = FALSE; - - active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)); - SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (user_data); - if ( entry ) - { - if ( active ) - { - ((ColorNotebook*)(SP_COLOR_SELECTOR(entry->backPointer)->base))->addPage(entry->type, entry->submode); - } - else - { - ((ColorNotebook*)(SP_COLOR_SELECTOR(entry->backPointer)->base))->removePage(entry->type, entry->submode); - } - } + gboolean active = FALSE; + + active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)); + SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (user_data); + if ( entry ) + { + if ( active ) + { + ((ColorNotebook*)(SP_COLOR_SELECTOR(entry->backPointer)->base))->addPage(entry->type, entry->submode); + } + else + { + ((ColorNotebook*)(SP_COLOR_SELECTOR(entry->backPointer)->base))->removePage(entry->type, entry->submode); + } + } } static void @@ -195,249 +195,249 @@ sp_color_notebook_init (SPColorNotebook *colorbook) void ColorNotebook::init() { - GtkWidget* table = 0; - guint row = 0; - guint i = 0; - guint j = 0; - GType *selector_types = 0; - guint selector_type_count = 0; - - GtkTooltips *tt = gtk_tooltips_new (); - - /* tempory hardcoding to get types loaded */ - SP_TYPE_COLOR_SCALES; - SP_TYPE_COLOR_WHEEL_SELECTOR; + GtkWidget* table = 0; + guint row = 0; + guint i = 0; + guint j = 0; + GType *selector_types = 0; + guint selector_type_count = 0; + + GtkTooltips *tt = gtk_tooltips_new (); + + /* tempory hardcoding to get types loaded */ + SP_TYPE_COLOR_SCALES; + SP_TYPE_COLOR_WHEEL_SELECTOR; #if ENABLE_LCMS - SP_TYPE_COLOR_ICC_SELECTOR; + SP_TYPE_COLOR_ICC_SELECTOR; #endif // ENABLE_LCMS - /* REJON: Comment out the next line to not use the normal GTK Color + /* REJON: Comment out the next line to not use the normal GTK Color wheel. */ // SP_TYPE_COLOR_GTKSELECTOR; - _updating = FALSE; - _updatingrgba = FALSE; - _btn = 0; - _popup = 0; - _trackerList = g_ptr_array_new (); - - _book = gtk_notebook_new (); - gtk_widget_show (_book); - - selector_types = g_type_children (SP_TYPE_COLOR_SELECTOR, &selector_type_count); - - for ( i = 0; i < selector_type_count; i++ ) - { - if (!g_type_is_a (selector_types[i], SP_TYPE_COLOR_NOTEBOOK)) - { - guint howmany = 1; - gpointer klass = gtk_type_class (selector_types[i]); - if ( klass && SP_IS_COLOR_SELECTOR_CLASS (klass) ) - { - SPColorSelectorClass *ck = SP_COLOR_SELECTOR_CLASS (klass); - howmany = MAX (1, ck->submode_count); - for ( j = 0; j < howmany; j++ ) - { - SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (malloc(sizeof(SPColorNotebookTracker))); - if ( entry ) - { - memset( entry, 0, sizeof(SPColorNotebookTracker) ); - entry->name = ck->name[j]; - entry->type = selector_types[i]; - entry->submode = j; - entry->enabledFull = TRUE; - entry->enabledBrief = TRUE; - entry->backPointer = SP_COLOR_NOTEBOOK(_csel); - - g_ptr_array_add (_trackerList, entry); - } - } - } - } - } - - for ( i = 0; i < _trackerList->len; i++ ) - { - SPColorNotebookTracker *entry = - reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i)); - if ( entry ) - { - addPage(entry->type, entry->submode); - } - } - - table = gtk_table_new (2, 3, FALSE); - gtk_widget_show (table); - - gtk_box_pack_start (GTK_BOX (_csel), table, TRUE, TRUE, 0); - - gtk_table_attach (GTK_TABLE (table), _book, 0, 2, row, row + 1, + _updating = FALSE; + _updatingrgba = FALSE; + _btn = 0; + _popup = 0; + _trackerList = g_ptr_array_new (); + + _book = gtk_notebook_new (); + gtk_widget_show (_book); + + selector_types = g_type_children (SP_TYPE_COLOR_SELECTOR, &selector_type_count); + + for ( i = 0; i < selector_type_count; i++ ) + { + if (!g_type_is_a (selector_types[i], SP_TYPE_COLOR_NOTEBOOK)) + { + guint howmany = 1; + gpointer klass = gtk_type_class (selector_types[i]); + if ( klass && SP_IS_COLOR_SELECTOR_CLASS (klass) ) + { + SPColorSelectorClass *ck = SP_COLOR_SELECTOR_CLASS (klass); + howmany = MAX (1, ck->submode_count); + for ( j = 0; j < howmany; j++ ) + { + SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (malloc(sizeof(SPColorNotebookTracker))); + if ( entry ) + { + memset( entry, 0, sizeof(SPColorNotebookTracker) ); + entry->name = ck->name[j]; + entry->type = selector_types[i]; + entry->submode = j; + entry->enabledFull = TRUE; + entry->enabledBrief = TRUE; + entry->backPointer = SP_COLOR_NOTEBOOK(_csel); + + g_ptr_array_add (_trackerList, entry); + } + } + } + } + } + + for ( i = 0; i < _trackerList->len; i++ ) + { + SPColorNotebookTracker *entry = + reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i)); + if ( entry ) + { + addPage(entry->type, entry->submode); + } + } + + table = gtk_table_new (2, 3, FALSE); + gtk_widget_show (table); + + gtk_box_pack_start (GTK_BOX (_csel), table, TRUE, TRUE, 0); + + gtk_table_attach (GTK_TABLE (table), _book, 0, 2, row, row + 1, static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL), static_cast<GtkAttachOptions>(GTK_EXPAND|GTK_FILL), XPAD, YPAD); - // restore the last active page - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gtk_notebook_set_current_page (GTK_NOTEBOOK (_book), prefs->getInt("/colorselector/page", 0)); - - { - gboolean found = FALSE; + // restore the last active page + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gtk_notebook_set_current_page (GTK_NOTEBOOK (_book), prefs->getInt("/colorselector/page", 0)); - _popup = gtk_menu_new(); - GtkMenu *menu = GTK_MENU (_popup); + { + gboolean found = FALSE; - for ( i = 0; i < _trackerList->len; i++ ) - { - SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i)); - if ( entry ) - { - GtkWidget *item = gtk_check_menu_item_new_with_label (_(entry->name)); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), entry->enabledFull); - gtk_widget_show (item); - gtk_menu_append (menu, item); + _popup = gtk_menu_new(); + GtkMenu *menu = GTK_MENU (_popup); - g_signal_connect (G_OBJECT (item), "activate", - G_CALLBACK (sp_color_notebook_menuitem_response), - reinterpret_cast< gpointer > (entry) ); - found = TRUE; - } - } + for ( i = 0; i < _trackerList->len; i++ ) + { + SPColorNotebookTracker *entry = reinterpret_cast< SPColorNotebookTracker* > (g_ptr_array_index (_trackerList, i)); + if ( entry ) + { + GtkWidget *item = gtk_check_menu_item_new_with_label (_(entry->name)); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), entry->enabledFull); + gtk_widget_show (item); + gtk_menu_append (menu, item); + + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (sp_color_notebook_menuitem_response), + reinterpret_cast< gpointer > (entry) ); + found = TRUE; + } + } - GtkWidget *arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE); - gtk_widget_show (arrow); + GtkWidget *arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE); + gtk_widget_show (arrow); - _btn = gtk_button_new (); - gtk_widget_show (_btn); - gtk_container_add (GTK_CONTAINER (_btn), arrow); + _btn = gtk_button_new (); + gtk_widget_show (_btn); + gtk_container_add (GTK_CONTAINER (_btn), arrow); - GtkWidget *align = gtk_alignment_new (1.0, 0.0, 0.0, 0.0); - gtk_widget_show (align); - gtk_container_add (GTK_CONTAINER (align), _btn); + GtkWidget *align = gtk_alignment_new (1.0, 0.0, 0.0, 0.0); + gtk_widget_show (align); + gtk_container_add (GTK_CONTAINER (align), _btn); - // uncomment to reenable the "show/hide modes" menu, - // but first fix it so it remembers its settings in prefs and does not take that much space (entire vertical column!) - //gtk_table_attach (GTK_TABLE (table), align, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); + // uncomment to reenable the "show/hide modes" menu, + // but first fix it so it remembers its settings in prefs and does not take that much space (entire vertical column!) + //gtk_table_attach (GTK_TABLE (table), align, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); - gtk_signal_connect_object(GTK_OBJECT(_btn), "event", GTK_SIGNAL_FUNC (sp_color_notebook_menu_handler), GTK_OBJECT(_csel)); - if ( !found ) - { - gtk_widget_set_sensitive (_btn, FALSE); - } - } + gtk_signal_connect_object(GTK_OBJECT(_btn), "event", GTK_SIGNAL_FUNC (sp_color_notebook_menu_handler), GTK_OBJECT(_csel)); + if ( !found ) + { + gtk_widget_set_sensitive (_btn, FALSE); + } + } - row++; + row++; - GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0); + GtkWidget *rgbabox = gtk_hbox_new (FALSE, 0); #if ENABLE_LCMS - /* Create color management icons */ - _box_colormanaged = gtk_event_box_new (); - GtkWidget *colormanaged = gtk_image_new_from_icon_name ("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (_box_colormanaged), colormanaged); - GtkTooltips *tooltips_colormanaged = gtk_tooltips_new (); - gtk_tooltips_set_tip (tooltips_colormanaged, _box_colormanaged, _("Color Managed"), ""); - gtk_widget_set_sensitive (_box_colormanaged, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, FALSE, FALSE, 2); - - _box_outofgamut = gtk_event_box_new (); - GtkWidget *outofgamut = gtk_image_new_from_icon_name ("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (_box_outofgamut), outofgamut); - GtkTooltips *tooltips_outofgamut = gtk_tooltips_new (); - gtk_tooltips_set_tip (tooltips_outofgamut, _box_outofgamut, _("Out of gamut!"), ""); - gtk_widget_set_sensitive (_box_outofgamut, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, FALSE, FALSE, 2); - - _box_toomuchink = gtk_event_box_new (); - GtkWidget *toomuchink = gtk_image_new_from_icon_name ("too-much-ink-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); - gtk_container_add (GTK_CONTAINER (_box_toomuchink), toomuchink); - GtkTooltips *tooltips_toomuchink = gtk_tooltips_new (); - gtk_tooltips_set_tip (tooltips_toomuchink, _box_toomuchink, _("Too much ink!"), ""); - gtk_widget_set_sensitive (_box_toomuchink, false); - gtk_box_pack_start(GTK_BOX(rgbabox), _box_toomuchink, FALSE, FALSE, 2); + /* Create color management icons */ + _box_colormanaged = gtk_event_box_new (); + GtkWidget *colormanaged = gtk_image_new_from_icon_name ("color-management-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (_box_colormanaged), colormanaged); + GtkTooltips *tooltips_colormanaged = gtk_tooltips_new (); + gtk_tooltips_set_tip (tooltips_colormanaged, _box_colormanaged, _("Color Managed"), ""); + gtk_widget_set_sensitive (_box_colormanaged, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_colormanaged, FALSE, FALSE, 2); + + _box_outofgamut = gtk_event_box_new (); + GtkWidget *outofgamut = gtk_image_new_from_icon_name ("out-of-gamut-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (_box_outofgamut), outofgamut); + GtkTooltips *tooltips_outofgamut = gtk_tooltips_new (); + gtk_tooltips_set_tip (tooltips_outofgamut, _box_outofgamut, _("Out of gamut!"), ""); + gtk_widget_set_sensitive (_box_outofgamut, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_outofgamut, FALSE, FALSE, 2); + + _box_toomuchink = gtk_event_box_new (); + GtkWidget *toomuchink = gtk_image_new_from_icon_name ("too-much-ink-icon", GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (_box_toomuchink), toomuchink); + GtkTooltips *tooltips_toomuchink = gtk_tooltips_new (); + gtk_tooltips_set_tip (tooltips_toomuchink, _box_toomuchink, _("Too much ink!"), ""); + gtk_widget_set_sensitive (_box_toomuchink, false); + gtk_box_pack_start(GTK_BOX(rgbabox), _box_toomuchink, FALSE, FALSE, 2); #endif //ENABLE_LCMS - /* Create RGBA entry and color preview */ - _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:")); - gtk_misc_set_alignment (GTK_MISC (_rgbal), 1.0, 0.5); - gtk_box_pack_start(GTK_BOX(rgbabox), _rgbal, TRUE, TRUE, 2); + /* Create RGBA entry and color preview */ + _rgbal = gtk_label_new_with_mnemonic (_("RGBA_:")); + gtk_misc_set_alignment (GTK_MISC (_rgbal), 1.0, 0.5); + gtk_box_pack_start(GTK_BOX(rgbabox), _rgbal, TRUE, TRUE, 2); - _rgbae = gtk_entry_new (); - sp_dialog_defocus_on_enter (_rgbae); - gtk_entry_set_max_length (GTK_ENTRY (_rgbae), 8); - gtk_entry_set_width_chars (GTK_ENTRY (_rgbae), 8); - gtk_tooltips_set_tip (tt, _rgbae, _("Hexadecimal RGBA value of the color"), NULL); - gtk_box_pack_start(GTK_BOX(rgbabox), _rgbae, FALSE, FALSE, 0); - gtk_label_set_mnemonic_widget (GTK_LABEL(_rgbal), _rgbae); + _rgbae = gtk_entry_new (); + sp_dialog_defocus_on_enter (_rgbae); + gtk_entry_set_max_length (GTK_ENTRY (_rgbae), 8); + gtk_entry_set_width_chars (GTK_ENTRY (_rgbae), 8); + gtk_tooltips_set_tip (tt, _rgbae, _("Hexadecimal RGBA value of the color"), NULL); + gtk_box_pack_start(GTK_BOX(rgbabox), _rgbae, FALSE, FALSE, 0); + gtk_label_set_mnemonic_widget (GTK_LABEL(_rgbal), _rgbae); - sp_set_font_size_smaller (rgbabox); - gtk_widget_show_all (rgbabox); + sp_set_font_size_smaller (rgbabox); + gtk_widget_show_all (rgbabox); #if ENABLE_LCMS - //the "too much ink" icon is initially hidden - gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); + //the "too much ink" icon is initially hidden + gtk_widget_hide(GTK_WIDGET(_box_toomuchink)); #endif //ENABLE_LCMS - gtk_table_attach (GTK_TABLE (table), rgbabox, 0, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); + gtk_table_attach (GTK_TABLE (table), rgbabox, 0, 2, row, row + 1, GTK_FILL, GTK_SHRINK, XPAD, YPAD); #ifdef SPCS_PREVIEW - _p = sp_color_preview_new (0xffffffff); - gtk_widget_show (_p); - gtk_table_attach (GTK_TABLE (table), _p, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); + _p = sp_color_preview_new (0xffffffff); + gtk_widget_show (_p); + gtk_table_attach (GTK_TABLE (table), _p, 2, 3, row, row + 1, GTK_FILL, GTK_FILL, XPAD, YPAD); #endif - _switchId = g_signal_connect(GTK_OBJECT (_book), "switch-page", - GTK_SIGNAL_FUNC (sp_color_notebook_switch_page), SP_COLOR_NOTEBOOK(_csel)); + _switchId = g_signal_connect(GTK_OBJECT (_book), "switch-page", + GTK_SIGNAL_FUNC (sp_color_notebook_switch_page), SP_COLOR_NOTEBOOK(_csel)); - _entryId = gtk_signal_connect (GTK_OBJECT (_rgbae), "changed", GTK_SIGNAL_FUNC (ColorNotebook::_rgbaEntryChangedHook), _csel); + _entryId = gtk_signal_connect (GTK_OBJECT (_rgbae), "changed", GTK_SIGNAL_FUNC (ColorNotebook::_rgbaEntryChangedHook), _csel); } static void sp_color_notebook_destroy (GtkObject *object) { - if (((GtkObjectClass *) (parent_class))->destroy) - (* ((GtkObjectClass *) (parent_class))->destroy) (object); + if (((GtkObjectClass *) (parent_class))->destroy) + (* ((GtkObjectClass *) (parent_class))->destroy) (object); } ColorNotebook::~ColorNotebook() { - if ( _trackerList ) - { - g_ptr_array_free (_trackerList, TRUE); - _trackerList = 0; - } - - if ( _switchId ) - { - if ( _book ) - { - g_signal_handler_disconnect (_book, _switchId); - _switchId = 0; - } - } + if ( _trackerList ) + { + g_ptr_array_free (_trackerList, TRUE); + _trackerList = 0; + } + + if ( _switchId ) + { + if ( _book ) + { + g_signal_handler_disconnect (_book, _switchId); + _switchId = 0; + } + } } static void sp_color_notebook_show_all (GtkWidget *widget) { - gtk_widget_show (widget); + gtk_widget_show (widget); } static void sp_color_notebook_hide_all (GtkWidget *widget) { - gtk_widget_hide (widget); + gtk_widget_hide (widget); } GtkWidget * sp_color_notebook_new (void) { - SPColorNotebook *colorbook; + SPColorNotebook *colorbook; - colorbook = (SPColorNotebook*)gtk_type_new (SP_TYPE_COLOR_NOTEBOOK); + colorbook = (SPColorNotebook*)gtk_type_new (SP_TYPE_COLOR_NOTEBOOK); - return GTK_WIDGET (colorbook); + return GTK_WIDGET (colorbook); } ColorNotebook::ColorNotebook( SPColorSelector* csel ) @@ -523,7 +523,7 @@ void ColorNotebook::_updateRgbaEntry( const SPColor& color, gfloat alpha ) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); -#if ENABLE_LCMS +#if ENABLE_LCMS /* update color management icon*/ gtk_widget_set_sensitive (_box_colormanaged, color.icc != NULL); @@ -584,15 +584,15 @@ void ColorNotebook::_entryGrabbed (SPColorSelector *, SPColorNotebook *colorbook void ColorNotebook::_entryDragged (SPColorSelector *csel, SPColorNotebook *colorbook) { - gboolean oldState; + gboolean oldState; ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base); - oldState = nb->_dragging; + oldState = nb->_dragging; - nb->_dragging = TRUE; - nb->_entryModified( csel, colorbook ); + nb->_dragging = TRUE; + nb->_entryModified( csel, colorbook ); - nb->_dragging = oldState; + nb->_dragging = oldState; } void ColorNotebook::_entryReleased (SPColorSelector *, SPColorNotebook *colorbook) @@ -603,116 +603,116 @@ void ColorNotebook::_entryReleased (SPColorSelector *, SPColorNotebook *colorboo void ColorNotebook::_entryChanged (SPColorSelector *csel, SPColorNotebook *colorbook) { - gboolean oldState; + gboolean oldState; ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base); - oldState = nb->_dragging; + oldState = nb->_dragging; - nb->_dragging = FALSE; - nb->_entryModified( csel, colorbook ); + nb->_dragging = FALSE; + nb->_entryModified( csel, colorbook ); - nb->_dragging = oldState; + nb->_dragging = oldState; } void ColorNotebook::_entryModified (SPColorSelector *csel, SPColorNotebook *colorbook) { - g_return_if_fail (colorbook != NULL); - g_return_if_fail (SP_IS_COLOR_NOTEBOOK (colorbook)); - g_return_if_fail (csel != NULL); - g_return_if_fail (SP_IS_COLOR_SELECTOR (csel)); + g_return_if_fail (colorbook != NULL); + g_return_if_fail (SP_IS_COLOR_NOTEBOOK (colorbook)); + g_return_if_fail (csel != NULL); + g_return_if_fail (SP_IS_COLOR_SELECTOR (csel)); ColorNotebook* nb = (ColorNotebook*)(SP_COLOR_SELECTOR(colorbook)->base); SPColor color; gfloat alpha = 1.0; - csel->base->getColorAlpha( color, &alpha ); + csel->base->getColorAlpha( color, alpha ); nb->_updateRgbaEntry( color, alpha ); nb->_updateInternals( color, alpha, nb->_dragging ); } GtkWidget* ColorNotebook::addPage(GType page_type, guint submode) { - GtkWidget *page; - - page = sp_color_selector_new( page_type ); - if ( page ) - { - GtkWidget* tab_label = 0; - SPColorSelector* csel; - - csel = SP_COLOR_SELECTOR (page); - if ( submode > 0 ) - { - csel->base->setSubmode( submode ); - } - gtk_widget_show (page); + GtkWidget *page; + + page = sp_color_selector_new( page_type ); + if ( page ) + { + GtkWidget* tab_label = 0; + SPColorSelector* csel; + + csel = SP_COLOR_SELECTOR (page); + if ( submode > 0 ) + { + csel->base->setSubmode( submode ); + } + gtk_widget_show (page); int index = csel->base ? csel->base->getSubmode() : 0; const gchar* str = _(SP_COLOR_SELECTOR_GET_CLASS (csel)->name[index]); // g_message( "Hitting up for tab for '%s'", str ); - tab_label = gtk_label_new(_(str)); - gtk_notebook_append_page( GTK_NOTEBOOK (_book), page, tab_label ); - gtk_signal_connect (GTK_OBJECT (page), "grabbed", GTK_SIGNAL_FUNC (_entryGrabbed), _csel); - gtk_signal_connect (GTK_OBJECT (page), "dragged", GTK_SIGNAL_FUNC (_entryDragged), _csel); - gtk_signal_connect (GTK_OBJECT (page), "released", GTK_SIGNAL_FUNC (_entryReleased), _csel); - gtk_signal_connect (GTK_OBJECT (page), "changed", GTK_SIGNAL_FUNC (_entryChanged), _csel); - } - - return page; + tab_label = gtk_label_new(_(str)); + gtk_notebook_append_page( GTK_NOTEBOOK (_book), page, tab_label ); + gtk_signal_connect (GTK_OBJECT (page), "grabbed", GTK_SIGNAL_FUNC (_entryGrabbed), _csel); + gtk_signal_connect (GTK_OBJECT (page), "dragged", GTK_SIGNAL_FUNC (_entryDragged), _csel); + gtk_signal_connect (GTK_OBJECT (page), "released", GTK_SIGNAL_FUNC (_entryReleased), _csel); + gtk_signal_connect (GTK_OBJECT (page), "changed", GTK_SIGNAL_FUNC (_entryChanged), _csel); + } + + return page; } GtkWidget* ColorNotebook::getPage(GType page_type, guint submode) { - gint count = 0; - gint i = 0; - GtkWidget* page = 0; - -// count = gtk_notebook_get_n_pages (_book); - count = 200; - for ( i = 0; i < count && !page; i++ ) - { - page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), i); - if ( page ) - { - SPColorSelector* csel; - guint pagemode; - csel = SP_COLOR_SELECTOR (page); - pagemode = csel->base->getSubmode(); - if ( G_TYPE_FROM_INSTANCE (page) == page_type - && pagemode == submode ) - { - // found it. - break; - } - else - { - page = 0; - } - } - else - { - break; - } - } - return page; + gint count = 0; + gint i = 0; + GtkWidget* page = 0; + +// count = gtk_notebook_get_n_pages (_book); + count = 200; + for ( i = 0; i < count && !page; i++ ) + { + page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (_book), i); + if ( page ) + { + SPColorSelector* csel; + guint pagemode; + csel = SP_COLOR_SELECTOR (page); + pagemode = csel->base->getSubmode(); + if ( G_TYPE_FROM_INSTANCE (page) == page_type + && pagemode == submode ) + { + // found it. + break; + } + else + { + page = 0; + } + } + else + { + break; + } + } + return page; } void ColorNotebook::removePage( GType page_type, guint submode ) { - GtkWidget *page = 0; - - page = getPage(page_type, submode); - if ( page ) - { - gint where = gtk_notebook_page_num (GTK_NOTEBOOK (_book), page); - if ( where >= 0 ) - { - if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) == where ) - { + GtkWidget *page = 0; + + page = getPage(page_type, submode); + if ( page ) + { + gint where = gtk_notebook_page_num (GTK_NOTEBOOK (_book), page); + if ( where >= 0 ) + { + if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (_book)) == where ) + { // getColorAlpha(_color, &_alpha); - } - gtk_notebook_remove_page (GTK_NOTEBOOK (_book), where); - } - } + } + gtk_notebook_remove_page (GTK_NOTEBOOK (_book), where); + } + } } /* diff --git a/src/widgets/sp-color-selector.cpp b/src/widgets/sp-color-selector.cpp index 312128781..203bc3c2a 100644 --- a/src/widgets/sp-color-selector.cpp +++ b/src/widgets/sp-color-selector.cpp @@ -318,15 +318,12 @@ void ColorSelector::_colorChanged() { } -void ColorSelector::getColorAlpha( SPColor& color, gfloat* alpha ) const +void ColorSelector::getColorAlpha( SPColor &color, gfloat &alpha ) const { gint i = 0; color = _color; - if ( alpha ) - { - *alpha = _alpha; - } + alpha = _alpha; // Try to catch uninitialized value usage if ( color.v.c[0] ) @@ -345,7 +342,7 @@ void ColorSelector::getColorAlpha( SPColor& color, gfloat* alpha ) const { i++; } - if ( alpha && *alpha ) + if ( alpha ) { i++; } diff --git a/src/widgets/sp-color-selector.h b/src/widgets/sp-color-selector.h index 40446fe0a..a2ad87dd5 100644 --- a/src/widgets/sp-color-selector.h +++ b/src/widgets/sp-color-selector.h @@ -26,7 +26,7 @@ public: gfloat getAlpha() const; void setColorAlpha( const SPColor& color, gfloat alpha, bool emit = false ); - void getColorAlpha( SPColor& color, gfloat* alpha ) const; + void getColorAlpha( SPColor &color, gfloat &alpha ) const; virtual void setSubmode( guint submode ); virtual guint getSubmode() const; diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index f502f87d3..ddd1c92d3 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -63,7 +63,7 @@ static void sp_stroke_style_paint_selection_modified (SPWidget *spw, Inkscape::S static void sp_stroke_style_paint_selection_changed (SPWidget *spw, Inkscape::Selection *selection, SPPaintSelector *psel); static void sp_stroke_style_paint_update(SPWidget *spw); -static void sp_stroke_style_paint_mode_changed(SPPaintSelector *psel, SPPaintSelectorMode mode, SPWidget *spw); +static void sp_stroke_style_paint_mode_changed(SPPaintSelector *psel, SPPaintSelector::Mode mode, SPWidget *spw); static void sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw); static void sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw); @@ -189,7 +189,7 @@ sp_stroke_style_paint_update (SPWidget *spw) case QUERY_STYLE_NOTHING: { /* No paint at all */ - sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY); + psel->setMode(SPPaintSelector::MODE_EMPTY); break; } @@ -197,43 +197,43 @@ sp_stroke_style_paint_update (SPWidget *spw) case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector case QUERY_STYLE_MULTIPLE_SAME: { - SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, false); - sp_paint_selector_set_mode (psel, pselmode); + SPPaintSelector::Mode pselmode = SPPaintSelector::getModeForStyle(*query, false); + psel->setMode(pselmode); if (query->stroke.set && query->stroke.isPaintserver()) { SPPaintServer *server = SP_STYLE_STROKE_SERVER (query); - if (SP_IS_LINEARGRADIENT (server)) { - SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); - sp_paint_selector_set_gradient_linear (psel, vector); + if (server && server->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); - sp_paint_selector_set_gradient_properties (psel, - SP_GRADIENT_UNITS (lg), - SP_GRADIENT_SPREAD (lg)); + psel->setGradientProperties( SP_GRADIENT_UNITS(lg), + SP_GRADIENT_SPREAD(lg) ); } else if (SP_IS_RADIALGRADIENT (server)) { - SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE); - sp_paint_selector_set_gradient_radial (psel, vector); + SPGradient *vector = SP_GRADIENT(server)->getVector(); + psel->setGradientRadial( vector ); SPRadialGradient *rg = SP_RADIALGRADIENT (server); - sp_paint_selector_set_gradient_properties (psel, - SP_GRADIENT_UNITS (rg), - SP_GRADIENT_SPREAD (rg)); + psel->setGradientProperties( SP_GRADIENT_UNITS(rg), + SP_GRADIENT_SPREAD(rg) ); } else if (SP_IS_PATTERN (server)) { SPPattern *pat = pattern_getroot (SP_PATTERN (server)); - sp_update_pattern_list (psel, pat); + psel->updatePatternList( pat ); } } else if (query->stroke.set && query->stroke.isColor()) { - sp_paint_selector_set_color_alpha (psel, &query->stroke.value.color, SP_SCALE24_TO_FLOAT (query->stroke_opacity.value)); - + psel->setColorAlpha(query->stroke.value.color, SP_SCALE24_TO_FLOAT(query->stroke_opacity.value)); } break; } case QUERY_STYLE_MULTIPLE_DIFFERENT: { - sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE); + psel->setMode(SPPaintSelector::MODE_MULTIPLE); break; } } @@ -248,7 +248,7 @@ sp_stroke_style_paint_update (SPWidget *spw) */ static void sp_stroke_style_paint_mode_changed( SPPaintSelector *psel, - SPPaintSelectorMode /*mode*/, + SPPaintSelector::Mode /*mode*/, SPWidget *spw ) { if (gtk_object_get_data(GTK_OBJECT(spw), "update")) { @@ -278,10 +278,10 @@ sp_stroke_style_paint_dragged(SPPaintSelector *psel, SPWidget *spw) } switch (psel->mode) { - case SP_PAINT_SELECTOR_MODE_COLOR_RGB: - case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + case SPPaintSelector::MODE_COLOR_RGB: + case SPPaintSelector::MODE_COLOR_CMYK: { - sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity"); + psel->setFlatColor( SP_ACTIVE_DESKTOP, "stroke", "stroke-opacity" ); sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE, _("Set stroke color")); break; @@ -313,17 +313,17 @@ sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) GSList const *items = selection->itemList(); switch (psel->mode) { - case SP_PAINT_SELECTOR_MODE_EMPTY: + case SPPaintSelector::MODE_EMPTY: // This should not happen. g_warning ( "file %s: line %d: Paint %d should not emit 'changed'", __FILE__, __LINE__, psel->mode); break; - case SP_PAINT_SELECTOR_MODE_MULTIPLE: + case SPPaintSelector::MODE_MULTIPLE: // This happens when you switch multiple objects with different gradients to flat color; // nothing to do here. break; - case SP_PAINT_SELECTOR_MODE_NONE: + case SPPaintSelector::MODE_NONE: { SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_set_property(css, "stroke", "none"); @@ -337,10 +337,10 @@ sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) break; } - case SP_PAINT_SELECTOR_MODE_COLOR_RGB: - case SP_PAINT_SELECTOR_MODE_COLOR_CMYK: + case SPPaintSelector::MODE_COLOR_RGB: + case SPPaintSelector::MODE_COLOR_CMYK: { - sp_paint_selector_set_flat_color (psel, desktop, "stroke", "stroke-opacity"); + psel->setFlatColor(desktop, "stroke", "stroke-opacity"); sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE, _("Set stroke color")); @@ -353,13 +353,13 @@ sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) break; } - case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR: - case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL: + case SPPaintSelector::MODE_GRADIENT_LINEAR: + case SPPaintSelector::MODE_GRADIENT_RADIAL: if (items) { - SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR + SPGradientType const gradient_type = ( psel->mode == SPPaintSelector::MODE_GRADIENT_LINEAR ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL ); - SPGradient *vector = sp_paint_selector_get_gradient_vector(psel); + SPGradient *vector = psel->getGradientVector(); if (!vector) { /* No vector in paint selector should mean that we just changed mode */ @@ -389,7 +389,7 @@ sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) vector = sp_gradient_ensure_vector_normalized(vector); for (GSList const *i = items; i != NULL; i = i->next) { SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, false); - sp_gradient_selector_attrs_to_gradient(gr, psel); + psel->pushAttrsToGradient( gr ); } } @@ -398,11 +398,11 @@ sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) } break; - case SP_PAINT_SELECTOR_MODE_PATTERN: + case SPPaintSelector::MODE_PATTERN: if (items) { - SPPattern *pattern = sp_paint_selector_get_pattern (psel); + SPPattern *pattern = psel->getPattern(); if (!pattern) { /* No Pattern in paint selector should mean that we just @@ -443,7 +443,11 @@ sp_stroke_style_paint_changed(SPPaintSelector *psel, SPWidget *spw) break; - case SP_PAINT_SELECTOR_MODE_UNSET: + case SPPaintSelector::MODE_SWATCH: + // TODO + break; + + case SPPaintSelector::MODE_UNSET: if (items) { SPCSSAttr *css = sp_repr_css_attr_new (); sp_repr_css_unset_property (css, "stroke"); diff --git a/src/widgets/swatch-selector.cpp b/src/widgets/swatch-selector.cpp new file mode 100644 index 000000000..a6f5133b7 --- /dev/null +++ b/src/widgets/swatch-selector.cpp @@ -0,0 +1,203 @@ + + +#include <glibmm/i18n.h> + +#include "swatch-selector.h" + +#include "document.h" +#include "gradient-chemistry.h" +#include "gradient-selector.h" +#include "sp-color-notebook.h" +#include "sp-stop.h" +#include "svg/css-ostringstream.h" +#include "svg/svg-color.h" +#include "verbs.h" +#include "xml/node.h" + +namespace Inkscape +{ +namespace Widgets +{ + +SwatchSelector::SwatchSelector() : + Gtk::VBox(), + _gsel(0), + _csel(0) +{ + GtkWidget *gsel = sp_gradient_selector_new(); + _gsel = SP_GRADIENT_SELECTOR(gsel); + g_object_set_data( G_OBJECT(gobj()), "base", this ); + _gsel->setMode(SPGradientSelector::MODE_SWATCH); + + gtk_widget_show(gsel); + + pack_start(*Gtk::manage(Glib::wrap(gsel))); + + + GtkWidget *csel = sp_color_selector_new( SP_TYPE_COLOR_NOTEBOOK ); + _csel = SP_COLOR_SELECTOR(csel); + Gtk::Widget *wrappedCSel = Glib::wrap(csel); + wrappedCSel->show(); + //gtk_widget_show(csel); + + + GObject *obj = G_OBJECT(csel); + + g_signal_connect(obj, "grabbed", G_CALLBACK(_grabbedCb), this); + g_signal_connect(obj, "dragged", G_CALLBACK(_draggedCb), this); + g_signal_connect(obj, "released", G_CALLBACK(_releasedCb), this); + g_signal_connect(obj, "changed", G_CALLBACK(_changedCb), this); + + pack_start(*Gtk::manage(wrappedCSel)); +} + +SwatchSelector::~SwatchSelector() +{ + _csel = 0; // dtor should be handled by Gtk::manage() + _gsel = 0; +} + +SPGradientSelector *SwatchSelector::getGradientSelector() +{ + return _gsel; +} + +void SwatchSelector::_grabbedCb(SPColorSelector * /*csel*/, void * /*data*/) +{ +} + +void SwatchSelector::_draggedCb(SPColorSelector * /*csel*/, void *data) +{ + if (data) { + //SwatchSelector *swsel = reinterpret_cast<SwatchSelector*>(data); + + // TODO might have to block cycles + + // Copied from gradient-vector.cpp, but does not appear to cause visible changes: + /* + if (swsel->_gsel) { + SPGradient *gradient = swsel->_gsel->getVector(); + SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); + if (ngr != gradient) { + // Our master gradient has changed + // TODO replace with proper - sp_gradient_vector_widget_load_gradient(GTK_WIDGET(swsel->_gsel), ngr); + } + + sp_gradient_ensure_vector(ngr); + + + SPStop* stop = ngr->getFirstStop(); + if (stop) { + swsel->_csel->base->getColorAlpha(stop->specified_color, &stop->opacity); + stop->currentColor = false; + // TODO push refresh + } + } + */ + } +} + +void SwatchSelector::_releasedCb(SPColorSelector * /*csel*/, void * /*data*/) +{ +} + +void SwatchSelector::_changedCb(SPColorSelector */*csel*/, void *data) +{ + if (data) { + SwatchSelector *swsel = reinterpret_cast<SwatchSelector*>(data); + + // TODO might have to block cycles + + if (swsel->_gsel && swsel->_gsel->getVector()) { + SPGradient *gradient = swsel->_gsel->getVector(); + SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); + if (ngr != gradient) { + /* Our master gradient has changed */ + // TODO replace with proper - sp_gradient_vector_widget_load_gradient(GTK_WIDGET(swsel->_gsel), ngr); + } + + sp_gradient_ensure_vector(ngr); + + + SPStop* stop = ngr->getFirstStop(); + if (stop) { + SPColor color; + float alpha = 0; + guint32 rgb = 0; + + swsel->_csel->base->getColorAlpha( color, alpha ); + rgb = color.toRGBA32( 0x00 ); + + // TODO replace with generic shared code that also handles icc-color + Inkscape::CSSOStringStream os; + gchar c[64]; + sp_svg_write_color(c, sizeof(c), rgb); + os << "stop-color:" << c << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; + SP_OBJECT_REPR(stop)->setAttribute("style", os.str().c_str()); + + sp_document_done(SP_OBJECT_DOCUMENT(ngr), SP_VERB_CONTEXT_GRADIENT, + _("Change swatch color")); + } + } + } +} + +void SwatchSelector::connectGrabbedHandler( GCallback handler, void *data ) +{ + GObject* obj = G_OBJECT(_gsel); + g_signal_connect( obj, "grabbed", handler, data ); +} + +void SwatchSelector::connectDraggedHandler( GCallback handler, void *data ) +{ + GObject* obj = G_OBJECT(_gsel); + g_signal_connect( obj, "dragged", handler, data ); +} + +void SwatchSelector::connectReleasedHandler( GCallback handler, void *data ) +{ + GObject* obj = G_OBJECT(_gsel); + g_signal_connect( obj, "released", handler, data ); +} + +void SwatchSelector::connectchangedHandler( GCallback handler, void *data ) +{ + GObject* obj = G_OBJECT(_gsel); + g_signal_connect( obj, "changed", handler, data ); +} + +void SwatchSelector::setVector(SPDocument */*doc*/, SPGradient *vector) +{ + //GtkVBox * box = gobj(); + + _gsel->setVector((vector) ? SP_OBJECT_DOCUMENT(vector) : 0, vector); + + if (vector) { + SPStop* stop = vector->getFirstStop(); + + guint32 const colorVal = sp_stop_get_rgba32(stop); + _csel->base->setAlpha(SP_RGBA32_A_F(colorVal)); + SPColor color( SP_RGBA32_R_F(colorVal), SP_RGBA32_G_F(colorVal), SP_RGBA32_B_F(colorVal) ); + // set its color, from the stored array + _csel->base->setColor( color ); + } + +/* +*/ +} + +} // namespace Widgets +} // namespace Inkscape + + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : diff --git a/src/widgets/swatch-selector.h b/src/widgets/swatch-selector.h new file mode 100644 index 000000000..83acf9fda --- /dev/null +++ b/src/widgets/swatch-selector.h @@ -0,0 +1,59 @@ +#ifndef SEEN_SP_SWATCH_SELECTOR_H +#define SEEN_SP_SWATCH_SELECTOR_H + + + +#include <gtkmm/box.h> + +class SPDocument; +class SPGradient; +class SPColorSelector; +class SPGradientSelector; + +namespace Inkscape +{ +namespace Widgets +{ + +class SwatchSelector : public Gtk::VBox +{ +public: + SwatchSelector(); + virtual ~SwatchSelector(); + + void connectGrabbedHandler( GCallback handler, void *data ); + void connectDraggedHandler( GCallback handler, void *data ); + void connectReleasedHandler( GCallback handler, void *data ); + void connectchangedHandler( GCallback handler, void *data ); + + void setVector(SPDocument *doc, SPGradient *vector); + + SPGradientSelector *getGradientSelector(); + +private: + static void _grabbedCb(SPColorSelector *csel, void *data); + static void _draggedCb(SPColorSelector *csel, void *data); + static void _releasedCb(SPColorSelector *csel, void *data); + static void _changedCb(SPColorSelector *csel, void *data); + + SPGradientSelector *_gsel; + SPColorSelector *_csel; +}; + + +} // namespace Widgets +} // namespace Inkscape + +#endif // SEEN_SP_SWATCH_SELECTOR_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : + diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 9fc077778..4b0d84d90 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -9,7 +9,7 @@ * Frank Felfe <innerspace@iname.com> * John Cliff <simarilius@yahoo.com> * David Turner <novalis@gnu.org> - * Josh Andler <scislac@users.sf.net> + * Josh Andler <scislac@scislac.com> * Jon A. Cruz <jon@joncruz.org> * Maximilian Albert <maximilian.albert@gmail.com> * @@ -50,7 +50,7 @@ #include "../ege-select-one-action.h" #include "../flood-context.h" #include "gradient-toolbar.h" -#include "../graphlayout/graphlayout.h" +#include "../graphlayout.h" #include "../helper/unit-menu.h" #include "../helper/units.h" #include "../helper/unit-tracker.h" @@ -65,7 +65,6 @@ #include "../live_effects/lpe-line_segment.h" #include "../lpe-tool-context.h" #include "../mod360.h" -#include "../node-context.h" #include "../pen-context.h" #include "../preferences.h" #include "../selection-chemistry.h" @@ -89,6 +88,9 @@ #include "../spray-context.h" #include "../ui/dialog/calligraphic-profile-rename.h" #include "../ui/icon-names.h" +#include "../ui/tool/control-point-selection.h" +#include "../ui/tool/node-tool.h" +#include "../ui/tool/multi-path-manipulator.h" #include "../ui/widget/style-swatch.h" #include "../verbs.h" #include "../widgets/button.h" @@ -98,14 +100,28 @@ #include "../xml/attribute-record.h" #include "../xml/node-event-vector.h" #include "../xml/repr.h" +#include "ui/uxmanager.h" #include "toolbox.h" +#define ENABLE_TASK_SUPPORT 1 + using Inkscape::UnitTracker; +using Inkscape::UI::UXManager; typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop); typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox); +enum BarId { + BAR_TOOL = 0, + BAR_AUX, + BAR_COMMANDS, + BAR_SNAP, +}; + +#define BAR_ID_KEY "BarIdValue" +#define HANDLE_POS_MARK "x-inkscape-pos" + static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder); @@ -128,7 +144,18 @@ static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* ma namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); } -Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) { +#if ENABLE_TASK_SUPPORT +static void fireTaskChange( EgeSelectOneAction *act, SPDesktop *dt ) +{ + gint selected = ege_select_one_action_get_active( act ); + UXManager::getInstance()->setTask(dt, selected); +} +#endif // ENABLE_TASK_SUPPORT + +using Inkscape::UI::ToolboxFactory; + + +Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) { static Inkscape::IconSize sizeChoices[] = { Inkscape::ICON_SIZE_LARGE_TOOLBAR, Inkscape::ICON_SIZE_SMALL_TOOLBAR, @@ -146,7 +173,7 @@ static struct { sp_verb_t doubleclick_verb; } const tools[] = { { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS}, - { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS }, + { "InkNodeTool", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS }, { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS }, { "SPSprayContext", "spray_tool", SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS }, { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS }, @@ -180,7 +207,7 @@ static struct { } const aux_toolboxes[] = { { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar", SP_VERB_INVALID, 0, 0}, - { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar", + { "InkNodeTool", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar", SP_VERB_INVALID, 0, 0}, { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar", SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")}, @@ -255,6 +282,7 @@ static gchar const * ui_descr = " </toolbar>" " <toolbar name='NodeToolbar'>" + " <separator />" " <toolitem action='NodeInsertAction' />" " <toolitem action='NodeDeleteAction' />" " <separator />" @@ -283,6 +311,7 @@ static gchar const * ui_descr = " <toolitem action='ObjectEditMaskPathAction' />" " <toolitem action='EditNextLPEParameterAction' />" " <separator />" + " <toolitem action='NodesShowTransformHandlesAction' />" " <toolitem action='NodesShowHandlesAction' />" " <toolitem action='NodesShowHelperpath' />" " </toolbar>" @@ -470,7 +499,7 @@ static gchar const * ui_descr = " </toolbar>" " <toolbar name='ConnectorToolbar'>" - " <toolitem action='ConnectorEditModeAction' />" +// " <toolitem action='ConnectorEditModeAction' />" " <toolitem action='ConnectorAvoidAction' />" " <toolitem action='ConnectorIgnoreAction' />" " <toolitem action='ConnectorOrthogonalAction' />" @@ -480,8 +509,8 @@ static gchar const * ui_descr = " <toolitem action='ConnectorLengthAction' />" " <toolitem action='ConnectorDirectedAction' />" " <toolitem action='ConnectorOverlapAction' />" - " <toolitem action='ConnectorNewConnPointAction' />" - " <toolitem action='ConnectorRemoveConnPointAction' />" +// " <toolitem action='ConnectorNewConnPointAction' />" +// " <toolitem action='ConnectorRemoveConnPointAction' />" " </toolbar>" "</ui>" @@ -489,7 +518,7 @@ static gchar const * ui_descr = static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop ); -static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*); +void setup_snap_toolbox (GtkWidget *toolbox, SPDesktop *desktop); static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop); static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox); @@ -572,7 +601,7 @@ Gtk::Widget* VerbAction::create_menu_item_vfunc() Gtk::Widget* VerbAction::create_tool_item_vfunc() { // Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc(); - Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small"); + Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small"); GtkWidget* toolbox = 0; GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize, SP_BUTTON_TYPE_TOGGLE, @@ -780,7 +809,7 @@ Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop ) SP_VERB_ZOOM_SELECTION, }; - Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small"); + Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small"); static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups; Glib::RefPtr<Gtk::ActionGroup> mainActions; @@ -816,6 +845,43 @@ Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop ) } } +#if ENABLE_TASK_SUPPORT + if ( !mainActions->get_action("TaskSetAction") ) { + GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING ); + + GtkTreeIter iter; + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Default"), + 1, _("Default interface setup"), + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Custom"), + 1, _("Set the custom task"), + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Wide"), + 1, _("Setup for widescreen work"), + -1 ); + + EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) ); + g_object_set( act, "short_label", _("Task:"), NULL ); + mainActions->add(Glib::wrap(GTK_ACTION(act))); + //g_object_set_data( holder, "mode_action", act ); + + ege_select_one_action_set_appearance( act, "minimal" ); + ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); + //ege_select_one_action_set_icon_size( act, secondarySize ); + ege_select_one_action_set_tooltip_column( act, 1 ); + + //ege_select_one_action_set_active( act, mode ); + g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop ); + } +#endif // ENABLE_TASK_SUPPORT return mainActions; } @@ -833,25 +899,21 @@ void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer gtk_widget_set_size_request( widget, -1, -1 ); } - - -GtkWidget * -sp_tool_toolbox_new() +static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos ) { - GtkTooltips *tt = gtk_tooltips_new(); - GtkWidget* tb = gtk_toolbar_new(); - gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL); - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE); - g_object_set_data(G_OBJECT(tb), "desktop", NULL); - g_object_set_data(G_OBJECT(tb), "tooltips", tt); gtk_widget_set_sensitive(tb, FALSE); - GtkWidget *hb = gtk_handle_box_new(); - gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP); - gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT); - gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); + GtkWidget *hb = 0; + if ( UXManager::getInstance()->isFloatWindowProblem() ) { + hb = gtk_event_box_new(); // A simple, neutral container. + } else { + hb = gtk_handle_box_new(); + gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos); + gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT); + gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); + } gtk_container_add(GTK_CONTAINER(hb), tb); gtk_widget_show(GTK_WIDGET(tb)); @@ -859,96 +921,47 @@ sp_tool_toolbox_new() sigc::connection* conn = new sigc::connection; g_object_set_data(G_OBJECT(hb), "event_context_connection", conn); - g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0)); - g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0)); + if ( GTK_IS_HANDLE_BOX(hb) ) { + g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0)); + g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0)); + } + + gpointer val = GINT_TO_POINTER(id); + g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val); return hb; } -GtkWidget * -sp_aux_toolbox_new() +GtkWidget *ToolboxFactory::createToolToolbox() { GtkWidget *tb = gtk_vbox_new(FALSE, 0); - gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING); - - g_object_set_data(G_OBJECT(tb), "desktop", NULL); - - gtk_widget_set_sensitive(tb, FALSE); - - GtkWidget *hb = gtk_handle_box_new(); - gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); - gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT); - gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); - - gtk_container_add(GTK_CONTAINER(hb), tb); - gtk_widget_show(GTK_WIDGET(tb)); - - sigc::connection* conn = new sigc::connection; - g_object_set_data(G_OBJECT(hb), "event_context_connection", conn); + return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP ); +} - g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0)); - g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0)); +GtkWidget *ToolboxFactory::createAuxToolbox() +{ + GtkWidget *tb = gtk_vbox_new(FALSE, 0); - return hb; + return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT ); } //#################################### //# Commands Bar //#################################### -GtkWidget * -sp_commands_toolbox_new() +GtkWidget *ToolboxFactory::createCommandsToolbox() { - GtkWidget *tb = gtk_toolbar_new(); - - g_object_set_data(G_OBJECT(tb), "desktop", NULL); - gtk_widget_set_sensitive(tb, FALSE); - - GtkWidget *hb = gtk_handle_box_new(); - gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); - gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT); - gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); - - gtk_container_add(GTK_CONTAINER(hb), tb); - gtk_widget_show(GTK_WIDGET(tb)); - - sigc::connection* conn = new sigc::connection; - g_object_set_data(G_OBJECT(hb), "event_context_connection", conn); - - g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0)); - g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0)); + GtkWidget *tb = gtk_vbox_new(FALSE, 0); - return hb; + return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT ); } -GtkWidget * -sp_snap_toolbox_new() +GtkWidget *ToolboxFactory::createSnapToolbox() { GtkWidget *tb = gtk_vbox_new(FALSE, 0); - gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING); - g_object_set_data(G_OBJECT(tb), "desktop", NULL); - - //GtkWidget *tb = gtk_toolbar_new(); - //g_object_set_data(G_OBJECT(tb), "desktop", NULL); - - gtk_widget_set_sensitive(tb, FALSE); - GtkWidget *hb = gtk_handle_box_new(); - gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); - gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT); - gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT); - - gtk_container_add(GTK_CONTAINER(hb), tb); - gtk_widget_show(GTK_WIDGET(tb)); - - sigc::connection* conn = new sigc::connection; - g_object_set_data(G_OBJECT(hb), "event_context_connection", conn); - - g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0)); - g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0)); - - return hb; + return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT ); } static EgeAdjustmentAction * create_adjustment_action( gchar const *name, @@ -1006,135 +1019,157 @@ static EgeAdjustmentAction * create_adjustment_action( gchar const *name, //# node editing callbacks //#################################### -/** - * FIXME: Returns current shape_editor in context. // later eliminate this function at all! - */ -static ShapeEditor *get_current_shape_editor() +/** Temporary hack: Returns the node tool in the active desktop. + * Will go away during tool refactoring. */ +static InkNodeTool *get_node_tool() { - if (!SP_ACTIVE_DESKTOP) { - return NULL; - } - - SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context; - - if (!SP_IS_NODE_CONTEXT(event_context)) { - return NULL; - } - - return event_context->shape_editor; + if (!SP_ACTIVE_DESKTOP) return NULL; + SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context; + if (!INK_IS_NODE_TOOL(ec)) return NULL; + return static_cast<InkNodeTool*>(ec); } - void sp_node_path_edit_add(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->add_node(); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->insertNodes(); + } } void sp_node_path_edit_delete(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->delete_nodes_preserving_shape(); + InkNodeTool *nt = get_node_tool(); + if (nt) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true)); + } } void sp_node_path_edit_delete_segment(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->delete_segment(); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->deleteSegments(); + } } void sp_node_path_edit_break(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->break_at_nodes(); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->breakNodes(); + } } void sp_node_path_edit_join(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->join_nodes(); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->joinNodes(); + } } void sp_node_path_edit_join_segment(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->join_segments(); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->joinSegments(); + } } void sp_node_path_edit_toline(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT); + } } void sp_node_path_edit_tocurve(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER); + } } void sp_node_path_edit_cusp(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP); + } } void sp_node_path_edit_smooth(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH); + } } void sp_node_path_edit_symmetrical(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC); + } } void sp_node_path_edit_auto(void) { - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO); + InkNodeTool *nt = get_node_tool(); + if (nt) { + nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO); + } +} + +static void toggle_show_transform_handles (GtkToggleAction *act, gpointer /*data*/) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool show = gtk_toggle_action_get_active( act ); + prefs->setBool("/tools/nodes/show_transform_handles", show); } static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool show = gtk_toggle_action_get_active( act ); prefs->setBool("/tools/nodes/show_handles", show); - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->show_handles(show); } static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool show = gtk_toggle_action_get_active( act ); - prefs->setBool("/tools/nodes/show_helperpath", show); - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor) shape_editor->show_helperpath(show); + prefs->setBool("/tools/nodes/show_outline", show); } void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) { sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) ); } -void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) { - sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true); +void toggle_edit_clip (GtkToggleAction *act, gpointer /*data*/) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool edit = gtk_toggle_action_get_active( act ); + prefs->setBool("/tools/nodes/edit_clipping_paths", edit); } -void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) { - sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false); +void toggle_edit_mask (GtkToggleAction *act, gpointer /*data*/) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool edit = gtk_toggle_action_get_active( act ); + prefs->setBool("/tools/nodes/edit_masks", edit); } /* is called when the node selection is modified */ @@ -1157,54 +1192,29 @@ sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl) UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) ); SPUnit const *unit = tracker->getActiveUnit(); - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor && shape_editor->has_nodepath()) { - Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath(); - int n_selected = 0; - if (nodepath) { - n_selected = nodepath->numSelected(); - } - - if (n_selected == 0) { - gtk_action_set_sensitive(xact, FALSE); - gtk_action_set_sensitive(yact, FALSE); - } else { - gtk_action_set_sensitive(xact, TRUE); - gtk_action_set_sensitive(yact, TRUE); - Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit); - Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit); - - if (n_selected == 1) { - Geom::Point sel_node = nodepath->singleSelectedCoords(); - if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) { - gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit)); - gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit)); - } - } else { - boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X); - boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y); - if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) { - /* Note: Currently x and y will always have a value, even if the coordinates of the - selected nodes don't coincide (in this case we use the coordinates of the center - of the bounding box). So the entries are never set to zero. */ - // FIXME: Maybe we should clear the entry if several nodes are selected - // instead of providing a kind of average value - gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit)); - gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit)); - } - } - } - } else { - // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive + InkNodeTool *nt = get_node_tool(); + if (!nt || nt->_selected_nodes->empty()) { + // no path selected gtk_action_set_sensitive(xact, FALSE); gtk_action_set_sensitive(yact, FALSE); + } else { + gtk_action_set_sensitive(xact, TRUE); + gtk_action_set_sensitive(yact, TRUE); + Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit); + Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit); + Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint(); + + if (oldx != mid[Geom::X]) + gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit)); + if (oldy != mid[Geom::Y]) + gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit)); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } static void -sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name) +sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d) { SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" ); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -1213,7 +1223,8 @@ sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_ SPUnit const *unit = tracker->getActiveUnit(); if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) { - prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit)); + prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"), + sp_units_get_pixels(adj->value, *unit)); } // quit if run by the attr_changed listener @@ -1224,15 +1235,13 @@ sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_ // in turn, prevent listener from responding g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE)); - ShapeEditor *shape_editor = get_current_shape_editor(); - if (shape_editor && shape_editor->has_nodepath()) { + InkNodeTool *nt = get_node_tool(); + if (nt && !nt->_selected_nodes->empty()) { double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit); - if (!strcmp(value_name, "x")) { - sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X); - } - if (!strcmp(value_name, "y")) { - sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y); - } + double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d]; + Geom::Point delta(0,0); + delta[d] = val - oldval; + nt->_multipath->move(delta); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -1241,13 +1250,13 @@ sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_ static void sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl) { - sp_node_path_value_changed(adj, tbl, "x"); + sp_node_path_value_changed(adj, tbl, Geom::X); } static void sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl) { - sp_node_path_value_changed(adj, tbl, "y"); + sp_node_path_value_changed(adj, tbl, Geom::Y); } void @@ -1266,26 +1275,6 @@ sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl) gtk_action_set_sensitive(w, FALSE); } } - - { - GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) ); - SPItem *item = selection->singleItem(); - if (item && item->clip_ref && item->clip_ref->getObject()) { - gtk_action_set_sensitive(w, TRUE); - } else { - gtk_action_set_sensitive(w, FALSE); - } - } - - { - GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) ); - SPItem *item = selection->singleItem(); - if (item && item->mask_ref && item->mask_ref->getObject()) { - gtk_action_set_sensitive(w, TRUE); - } else { - gtk_action_set_sensitive(w, FALSE); - } - } } void @@ -1307,7 +1296,7 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units ); g_object_set_data( holder, "tracker", tracker ); - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { InkAction* inky = ink_action_new( "NodeInsertAction", @@ -1333,8 +1322,8 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions { InkAction* inky = ink_action_new( "NodeJoinAction", - _("Join endnodes"), - _("Join selected endnodes"), + _("Join nodes"), + _("Join selected nodes"), INKSCAPE_ICON_NODE_JOIN, secondarySize ); g_object_set( inky, "short_label", _("Join"), NULL ); @@ -1434,11 +1423,22 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions } { + InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction", + _("Show Transform Handles"), + _("Show node transformation handles"), + "node-transform", + secondarySize ); + gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_transform_handles), desktop ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_transform_handles", false) ); + } + + { InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction", _("Show Handles"), _("Show the Bezier handles of selected nodes"), INKSCAPE_ICON_SHOW_NODE_HANDLES, - Inkscape::ICON_SIZE_DECORATION ); + secondarySize ); gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop ); gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) ); @@ -1449,10 +1449,10 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions _("Show Outline"), _("Show the outline of the path"), INKSCAPE_ICON_SHOW_PATH_OUTLINE, - Inkscape::ICON_SIZE_DECORATION ); + secondarySize ); gtk_action_group_add_action( mainActions, GTK_ACTION( act ) ); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop ); - gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_outline", false) ); } { @@ -1460,32 +1460,32 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions _("Next path effect parameter"), _("Show next path effect parameter for editing"), INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT, - Inkscape::ICON_SIZE_DECORATION ); + secondarySize ); g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop ); gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); g_object_set_data( holder, "nodes_lpeedit", inky); } { - InkAction* inky = ink_action_new( "ObjectEditClipPathAction", - _("Edit clipping path"), - _("Edit the clipping path of the object"), + InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction", + _("Edit clipping paths"), + _("Show editing controls for clipping paths of selected objects"), INKSCAPE_ICON_PATH_CLIP_EDIT, - Inkscape::ICON_SIZE_DECORATION ); - g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop ); + secondarySize ); gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); - g_object_set_data( holder, "nodes_clippathedit", inky); + g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_clip), desktop ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_clipping_paths") ); } { - InkAction* inky = ink_action_new( "ObjectEditMaskPathAction", - _("Edit mask path"), - _("Edit the mask of the object"), + InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction", + _("Edit masks"), + _("Show editing controls for masks of selected objects"), INKSCAPE_ICON_PATH_MASK_EDIT, - Inkscape::ICON_SIZE_DECORATION ); - g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop ); + secondarySize ); gtk_action_group_add_action( mainActions, GTK_ACTION(inky) ); - g_object_set_data( holder, "nodes_maskedit", inky); + g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_mask), desktop ); + gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_masks") ); } /* X coord of selected node(s) */ @@ -1566,55 +1566,41 @@ static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainA // no custom GtkAction setup needed } // end of sp_zoom_toolbox_prep() -void -sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop) +void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop) { - toolbox_set_desktop(toolbox, - desktop, - setup_tool_toolbox, - update_tool_toolbox, - static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), - "event_context_connection"))); -} + sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), + "event_context_connection")); + BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) ); -void -sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop) -{ - toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), - desktop, - setup_aux_toolbox, - update_aux_toolbox, - static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), - "event_context_connection"))); -} + SetupFunction setup_func = 0; + UpdateFunction update_func = 0; -void -sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop) -{ - toolbox_set_desktop(toolbox, - desktop, - setup_commands_toolbox, - update_commands_toolbox, - static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), - "event_context_connection"))); -} + switch (id) { + case BAR_TOOL: + setup_func = setup_tool_toolbox; + update_func = update_tool_toolbox; + break; -void -sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop) -{ - toolbox_set_desktop(toolbox, - desktop, - setup_snap_toolbox, - update_snap_toolbox, - static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), - "event_context_connection"))); -} + case BAR_AUX: + toolbox = gtk_bin_get_child(GTK_BIN(toolbox)); + setup_func = setup_aux_toolbox; + update_func = update_aux_toolbox; + break; + case BAR_COMMANDS: + setup_func = setup_commands_toolbox; + update_func = update_commands_toolbox; + break; + + case BAR_SNAP: + setup_func = setup_snap_toolbox; + update_func = updateSnapToolbox; + break; + default: + g_warning("Unexpected toolbox id encountered."); + } -static void -toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn) -{ gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop"); SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr); @@ -1630,19 +1616,156 @@ toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_ g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop); - if (desktop) { + if (desktop && setup_func && update_func) { gtk_widget_set_sensitive(toolbox, TRUE); setup_func(toolbox, desktop); update_func(desktop, desktop->event_context, toolbox); - *conn = desktop->connectEventContextChanged - (sigc::bind (sigc::ptr_fun(update_func), toolbox)); + *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox)); } else { gtk_widget_set_sensitive(toolbox, FALSE); } -} // end of toolbox_set_desktop() +} // end of sp_toolbox_set_desktop() +static void setupToolboxCommon( GtkWidget *toolbox, + SPDesktop *desktop, + gchar const *descr, + gchar const* toolbarName, + gchar const* sizePref ) +{ + Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop ); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + GtkUIManager* mgr = gtk_ui_manager_new(); + GError* errVal = 0; + + GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL; + + gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 ); + gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal ); + + GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName ); + if ( prefs->getBool("/toolbox/icononly", true) ) { + gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS ); + } + + Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref); + gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) ); + + if (GTK_IS_HANDLE_BOX(toolbox)) { + // g_message("GRABBING ORIENTATION [%s]", toolbarName); + GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox)); + orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; + } else { + GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK ))); + orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; + } + gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation); + gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE); + + g_object_set_data(G_OBJECT(toolBar), "desktop", NULL); + + GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox)); + if ( child ) { + gtk_container_remove( GTK_CONTAINER(toolbox), child ); + } + + gtk_container_add( GTK_CONTAINER(toolbox), toolBar ); +} + +#define noDUMP_DETAILS 1 + +void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation) +{ +#if DUMP_DETAILS + g_message("Set orientation for %p to be %d", toolbox, orientation); + GType type = GTK_WIDGET_TYPE(toolbox); + g_message(" [%s]", g_type_name(type)); + g_message(" %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)); +#endif + + GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP; + GtkHandleBox* handleBox = 0; + + if (GTK_IS_BIN(toolbox)) { +#if DUMP_DETAILS + g_message(" is a BIN"); +#endif // DUMP_DETAILS + GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox)); + if (child) { +#if DUMP_DETAILS + GType type2 = GTK_WIDGET_TYPE(child); + g_message(" child [%s]", g_type_name(type2)); +#endif // DUMP_DETAILS + + if (GTK_IS_BOX(child)) { +#if DUMP_DETAILS + g_message(" is a BOX"); +#endif // DUMP_DETAILS + + GList* children = gtk_container_get_children(GTK_CONTAINER(child)); + if (children) { + for (GList* curr = children; curr; curr = g_list_next(curr)) { + GtkWidget* child2 = GTK_WIDGET(curr->data); +#if DUMP_DETAILS + GType type3 = GTK_WIDGET_TYPE(child2); + g_message(" child2 [%s]", g_type_name(type3)); +#endif // DUMP_DETAILS + + if (GTK_IS_CONTAINER(child2)) { + GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2)); + if (children2) { + for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) { + GtkWidget* child3 = GTK_WIDGET(curr2->data); +#if DUMP_DETAILS + GType type4 = GTK_WIDGET_TYPE(child3); + g_message(" child3 [%s]", g_type_name(type4)); +#endif // DUMP_DETAILS + if (GTK_IS_TOOLBAR(child3)) { + GtkToolbar* childBar = GTK_TOOLBAR(child3); + gtk_toolbar_set_orientation(childBar, orientation); + } + } + g_list_free(children2); + } + } + + + if (GTK_IS_TOOLBAR(child2)) { + GtkToolbar* childBar = GTK_TOOLBAR(child2); + gtk_toolbar_set_orientation(childBar, orientation); + if (GTK_IS_HANDLE_BOX(toolbox)) { + handleBox = GTK_HANDLE_BOX(toolbox); + } + } else { + g_message("need to add dynamic switch"); + } + } + g_list_free(children); + } else { + // The call is being made before the toolbox proper has been setup. + if (GTK_IS_HANDLE_BOX(toolbox)) { + handleBox = GTK_HANDLE_BOX(toolbox); + } else { + g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos)); + } + } + } else if (GTK_IS_TOOLBAR(child)) { + GtkToolbar* toolbar = GTK_TOOLBAR(child); + gtk_toolbar_set_orientation( toolbar, orientation ); + if (GTK_IS_HANDLE_BOX(toolbox)) { + handleBox = GTK_HANDLE_BOX(toolbox); + } + } + } + } + + if (handleBox) { + gtk_handle_box_set_handle_position(handleBox, pos); + } +} + static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { @@ -1671,36 +1794,12 @@ setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " <toolitem action='ToolDropper' />" " </toolbar>" "</ui>"; - Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop ); - GtkUIManager* mgr = gtk_ui_manager_new(); - GError* errVal = 0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 ); - gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal ); - GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" ); - if ( prefs->getBool("/toolbox/icononly", true) ) { - gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS ); - } - Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small"); - gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize ); - - gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL); - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE); - - g_object_set_data(G_OBJECT(toolBar), "desktop", NULL); - - GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox)); - if ( child ) { - gtk_container_remove( GTK_CONTAINER(toolbox), child ); - } - - gtk_container_add( GTK_CONTAINER(toolbox), toolBar ); -// Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small"); + setupToolboxCommon( toolbox, desktop, descr, + "/ui/ToolToolbar", + "/toolbox/tools/small"); } - static void update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ ) { @@ -1746,9 +1845,9 @@ setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop) } else { GtkWidget *sub_toolbox = 0; - if (aux_toolboxes[i].create_func == NULL) + if (aux_toolboxes[i].create_func == NULL) { sub_toolbox = sp_empty_toolbox_new(desktop); - else { + } else { sub_toolbox = aux_toolboxes[i].create_func(desktop); } @@ -1779,7 +1878,7 @@ setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop) gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS ); } - Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small"); + Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small"); gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) ); gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 ); @@ -1863,37 +1962,16 @@ setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " <separator />" " <toolitem action='DialogPreferences' />" " <toolitem action='DialogDocumentProperties' />" +#if ENABLE_TASK_SUPPORT + " <separator />" + " <toolitem action='TaskSetAction' />" +#endif // ENABLE_TASK_SUPPORT " </toolbar>" "</ui>"; - Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop ); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - GtkUIManager* mgr = gtk_ui_manager_new(); - GError* errVal = 0; - - gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 ); - gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal ); - - GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" ); - if ( prefs->getBool("/toolbox/icononly", true) ) { - gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS ); - } - - Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small"); - gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize ); - - gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL); - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE); - - - g_object_set_data(G_OBJECT(toolBar), "desktop", NULL); - - GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox)); - if ( child ) { - gtk_container_remove( GTK_CONTAINER(toolbox), child ); - } - - gtk_container_add( GTK_CONTAINER(toolbox), toolBar ); + setupToolboxCommon( toolbox, desktop, descr, + "/ui/CommandsToolbar", + "/toolbox/small" ); } static void @@ -2017,7 +2095,6 @@ void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop); gchar const * descr = @@ -2047,7 +2124,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " </toolbar>" "</ui>"; - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal", @@ -2212,42 +2289,41 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); }*/ - GtkUIManager* mgr = gtk_ui_manager_new(); - GError* errVal = 0; - - gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 ); - gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal ); - - GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" ); - if ( prefs->getBool("/toolbox/icononly", true) ) { - gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS ); - } - - Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary"); - gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) ); - - gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL); - gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE); - - g_object_set_data(G_OBJECT(toolBar), "desktop", NULL); + setupToolboxCommon( toolbox, desktop, descr, + "/ui/SnapToolbar", + "/toolbox/secondary" ); +} - GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox)); - if ( child ) { - gtk_container_remove( GTK_CONTAINER(toolbox), child ); +Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox) +{ + Glib::ustring name; + BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) ); + switch(id) { + case BAR_TOOL: + name = "ToolToolbar"; + break; + case BAR_AUX: + name = "AuxToolbar"; + break; + case BAR_COMMANDS: + name = "CommandsToolbar"; + break; + case BAR_SNAP: + name = "SnapToolbar"; + break; } - gtk_container_add( GTK_CONTAINER(toolbox), toolBar ); - + return name; } -void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox) +void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox) { g_assert(desktop != NULL); g_assert(toolbox != NULL); SPNamedView *nv = sp_desktop_namedview(desktop); if (nv == NULL) { - g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!"); + g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!"); return; } @@ -2332,7 +2408,7 @@ void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, G g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above) } -void show_aux_toolbox(GtkWidget *toolbox_toplevel) +void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel) { gtk_widget_show(toolbox_toplevel); GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel)); @@ -2346,8 +2422,7 @@ void show_aux_toolbox(GtkWidget *toolbox_toplevel) gtk_widget_show_all(shown_toolbox); } -static GtkWidget * -sp_empty_toolbox_new(SPDesktop *desktop) +static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop) { GtkWidget *tbl = gtk_toolbar_new(); gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas); @@ -2728,7 +2803,7 @@ sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide) static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 ); @@ -3097,7 +3172,7 @@ sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl) static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { EgeAdjustmentAction* eact = 0; - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 ); @@ -3461,7 +3536,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, box3d_angle_x = eact; } - if (!persp3d_VP_is_finite(persp_impl, Proj::X)) { + if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) { gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); @@ -3501,7 +3576,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, box3d_angle_y = eact; } - if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) { + if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) { gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); @@ -3540,7 +3615,7 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, box3d_angle_z = eact; } - if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) { + if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) { gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); } else { gtk_action_set_sensitive( GTK_ACTION(eact), FALSE ); @@ -3739,7 +3814,7 @@ sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { EgeAdjustmentAction* eact = 0; - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 ); @@ -3846,7 +3921,7 @@ static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* ho { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0); - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); @@ -4140,7 +4215,7 @@ static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) { static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); { @@ -4454,7 +4529,7 @@ static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ ) static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder) { - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); { @@ -4561,6 +4636,7 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction 1, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), sp_spray_population_value_changed, 1, 0 ); + ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "spray_population", eact ); @@ -4583,12 +4659,14 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction", _("Rotation"), _("Rotation:"), + // xgettext:no-c-format _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."), "/tools/spray/rotation_variation", 0, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation", 0, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), sp_spray_rotation_value_changed, 1, 0 ); + ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "spray_rotation", eact ); @@ -4599,12 +4677,14 @@ static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainAction gdouble values[] = {10, 25, 35, 50, 60, 80, 100}; EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction", _("Scale"), _("Scale:"), + // xgettext:no-c-format _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."), "/tools/spray/scale_variation", 0, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale", 0, 100, 1.0, 10.0, labels, values, G_N_ELEMENTS(labels), sp_spray_scale_value_changed, 1, 0 ); + ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT ); gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); gtk_action_set_sensitive( GTK_ACTION(eact), TRUE ); g_object_set_data( holder, "spray_scale", eact ); @@ -5396,7 +5476,7 @@ static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, Inkscape::Preferences *prefs = Inkscape::Preferences::get(); EgeAdjustmentAction* eact = 0; - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); { @@ -6660,10 +6740,10 @@ sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject * case GDK_Return: // unfreeze and update, which will defocus g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); - sp_text_toolbox_family_changed (NULL, tbl); + sp_text_toolbox_family_changed (NULL, tbl); return TRUE; // I consumed the event break; - case GDK_Escape: + case GDK_Escape: // defocus gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas)); return TRUE; // I consumed the event @@ -6939,10 +7019,10 @@ void sp_text_toolbox_family_popnotify(GtkComboBox *widget, } // update - sp_text_toolbox_family_changed (NULL, tbl); + sp_text_toolbox_family_changed (NULL, tbl); break; } - } + } } } @@ -6954,11 +7034,10 @@ void sp_text_toolbox_family_popnotify(GtkComboBox *widget, } } -GtkWidget* -sp_text_toolbox_new (SPDesktop *desktop) +GtkWidget *sp_text_toolbox_new (SPDesktop *desktop) { GtkToolbar *tbl = GTK_TOOLBAR(gtk_toolbar_new()); - GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1)); + GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1)); gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas); gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop); @@ -6981,7 +7060,7 @@ sp_text_toolbox_new (SPDesktop *desktop) g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl); cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl)); - + gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", ""); g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel); @@ -6994,7 +7073,7 @@ sp_text_toolbox_new (SPDesktop *desktop) g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl); g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl); - g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown", + g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown", G_CALLBACK (sp_text_toolbox_family_popnotify), tbl); g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl); g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl); @@ -7317,7 +7396,7 @@ static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl) if ( !repr->attribute("inkscape:connector-spacing") && ( adj->value == defaultConnSpacing )) { - // Don't need to update the repr if the attribute doesn't + // Don't need to update the repr if the attribute doesn't // exist and it is being set to the default value -- as will // happen at startup. return; @@ -7455,7 +7534,7 @@ static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selectio static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder ) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1); + Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1); // Editing mode toggle button { @@ -7567,8 +7646,7 @@ static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainA gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE )); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder ); - sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder)) - ); + sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder)); } // Avoid overlaps toggle button diff --git a/src/widgets/toolbox.h b/src/widgets/toolbox.h index 4bc417e8f..2e4b2958a 100644 --- a/src/widgets/toolbox.h +++ b/src/widgets/toolbox.h @@ -7,6 +7,7 @@ * Authors: * Lauris Kaplinski <lauris@kaplinski.com> * Frank Felfe <innerspace@iname.com> + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 1999-2002 Authors * Copyright (C) 2001-2002 Ximian, Inc. @@ -16,38 +17,45 @@ #include <gtk/gtkstyle.h> #include <gtk/gtktooltips.h> +#include <glibmm/ustring.h> #include "forward.h" #include "icon-size.h" -GtkWidget *sp_tool_toolbox_new (); -void sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop); +namespace Inkscape { +namespace UI { -GtkWidget *sp_aux_toolbox_new (); -void sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop); +class ToolboxFactory +{ +public: + static void setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop); + static void setOrientation(GtkWidget* toolbox, GtkOrientation orientation); + static void showAuxToolbox(GtkWidget* toolbox); -GtkWidget *sp_commands_toolbox_new (); -void sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop); + static GtkWidget *createToolToolbox(); + static GtkWidget *createAuxToolbox(); + static GtkWidget *createCommandsToolbox(); + static GtkWidget *createSnapToolbox(); -GtkWidget *sp_snap_toolbox_new (); -void sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop); -void update_snap_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox); -void setup_snap_toolbox (GtkWidget *toolbox, SPDesktop *desktop); + static Glib::ustring getToolboxName(GtkWidget* toolbox); -void show_aux_toolbox(GtkWidget *toolbox); + static void updateSnapToolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox); -GtkWidget *sp_toolbox_button_normal_new_from_verb(GtkWidget *t, - Inkscape::IconSize size, - Inkscape::Verb * verb, - Inkscape::UI::View::View *view, - GtkTooltips *tt); + static Inkscape::IconSize prefToSize(Glib::ustring const &path, int base = 0 ); + +private: + ToolboxFactory(); +}; + +} // namespace UI +} // namespace Inkscape -void aux_toolbox_space(GtkWidget *tb, gint space); // utility + +// TODO remove this: void sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide = true); -Inkscape::IconSize prefToSize(Glib::ustring const &path, int base = 0 ); #endif /* !SEEN_TOOLBOX_H */ diff --git a/src/winmain.cpp b/src/winmain.cpp index dc6b7e008..752ab8e6b 100644 --- a/src/winmain.cpp +++ b/src/winmain.cpp @@ -6,8 +6,12 @@ #include <glib.h> #ifdef G_OS_WIN32 - #undef DATADIR + +#include <stdio.h> +#include <io.h> +#include <conio.h> +#define _WIN32_WINNT 0x0501 #include <windows.h> extern int main (int argc, char **argv); @@ -26,6 +30,28 @@ WinMain (struct HINSTANCE__ *hInstance, char *lpszCmdLine, int nCmdShow) { + if (fileno (stdout) != -1 && + _get_osfhandle (fileno (stdout)) != -1) + { + /* stdout is fine, presumably redirected to a file or pipe */ + } + else + { + typedef BOOL (* WINAPI AttachConsole_t) (DWORD); + + AttachConsole_t p_AttachConsole = + (AttachConsole_t) GetProcAddress (GetModuleHandle ("kernel32.dll"), "AttachConsole"); + + if (p_AttachConsole != NULL && p_AttachConsole (ATTACH_PARENT_PROCESS)) + { + freopen ("CONOUT$", "w", stdout); + dup2 (fileno (stdout), 1); + freopen ("CONOUT$", "w", stderr); + dup2 (fileno (stderr), 2); + + } + } + int ret; ret = main (__argc, __argv); return ret; diff --git a/src/xml/Makefile_insert b/src/xml/Makefile_insert index e4269026c..7190b7948 100644 --- a/src/xml/Makefile_insert +++ b/src/xml/Makefile_insert @@ -1,7 +1,6 @@ ## Makefile.am fragment sourced by src/Makefile.am. ink_common_sources += \ - algorithms/find-if-before.h \ xml/comment-node.h \ xml/composite-node-observer.cpp xml/composite-node-observer.h \ xml/element-node.h \ diff --git a/src/xml/composite-node-observer.cpp b/src/xml/composite-node-observer.cpp index e65981616..564938dda 100644 --- a/src/xml/composite-node-observer.cpp +++ b/src/xml/composite-node-observer.cpp @@ -15,7 +15,7 @@ #include <cstring> #include <glib.h> -#include "algorithms/find-if-before.h" +#include "util/find-if-before.h" #include "xml/composite-node-observer.h" #include "xml/node-event-vector.h" #include "debug/event-tracker.h" diff --git a/src/xml/node-fns.cpp b/src/xml/node-fns.cpp index deb88fb5e..bf860d8f3 100644 --- a/src/xml/node-fns.cpp +++ b/src/xml/node-fns.cpp @@ -8,7 +8,7 @@ #include <glib.h> // g_assert() #include "xml/node-iterators.h" -#include "algorithms/find-if-before.h" +#include "util/find-if-before.h" namespace Inkscape { namespace XML { diff --git a/src/xml/repr-sorting.cpp b/src/xml/repr-sorting.cpp index 123df33ee..27293d3c2 100644 --- a/src/xml/repr-sorting.cpp +++ b/src/xml/repr-sorting.cpp @@ -1,5 +1,5 @@ -#include "algorithms/longest-common-suffix.h" +#include "util/longest-common-suffix.h" #include "xml/repr.h" #include "xml/node-iterators.h" |
