diff options
| author | Jabiertxof <jtx@jtx> | 2017-03-16 19:08:44 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx> | 2017-03-16 19:08:44 +0000 |
| commit | 8330d0ef2b97c73121ead78ea9fbcec6ee01f879 (patch) | |
| tree | 1b1717d1706ee6ebfecc800f2cc80430eb0450e0 /src | |
| parent | update to trunk (diff) | |
| parent | Fix rendering when canvas rotated. General code clean-up and documentation. (diff) | |
| download | inkscape-8330d0ef2b97c73121ead78ea9fbcec6ee01f879.tar.gz inkscape-8330d0ef2b97c73121ead78ea9fbcec6ee01f879.zip | |
Update to trunk
(bzr r13645.1.170)
Diffstat (limited to 'src')
186 files changed, 5235 insertions, 1962 deletions
diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt index aa51d51bd..d2c1ea50e 100644 --- a/src/2geom/CMakeLists.txt +++ b/src/2geom/CMakeLists.txt @@ -1,3 +1,7 @@ +# Override error flag just for this folder +if (CMAKE_BUILD_TYPE MATCHES Strict) + set(CMAKE_CXX_FLAGS_STRICT "${CMAKE_CXX_FLAGS_STRICT} -Wno-error=deprecated-declarations") +endif() set(2geom_SRC affine.cpp diff --git a/src/2geom/conicsec.cpp b/src/2geom/conicsec.cpp index 2d396ba30..3e0a53816 100644 --- a/src/2geom/conicsec.cpp +++ b/src/2geom/conicsec.cpp @@ -249,7 +249,7 @@ std::vector<Point> decompose_degenerate(xAx const & C1, xAx const & C2, xAx cons (-A[1][0]*b[0] + A[0][0]*b[1])); B0 *= ideterm; Point n0, n1; - // Are these just the eigenvectors of A11? + // Are these just the eigenvectors of A[1][1]? if(xC0.c[0] == xC0.c[2]) { double b = 0.5*xC0.c[1]/xC0.c[0]; double c = xC0.c[2]/xC0.c[0]; diff --git a/src/2geom/sbasis-geometric.cpp b/src/2geom/sbasis-geometric.cpp index 19eccc451..3fe27748e 100644 --- a/src/2geom/sbasis-geometric.cpp +++ b/src/2geom/sbasis-geometric.cpp @@ -227,7 +227,7 @@ Geom::unitVector(D2<SBasis> const &V_in, double tol, unsigned order){ // -This approach is numerically bad. Find a stable way to rescale V_in to have non vanishing ends. // -This done, unitVector will have jumps at zeros: fill the gaps with arcs of circles. D2<SBasis> V = RescaleForNonVanishingEnds(V_in); - if (V[0].isZero(0) && V[1].isZero(0)) + if (V[0].isZero(tol) && V[1].isZero(tol)) return Piecewise<D2<SBasis> >(D2<SBasis>(Linear(1),SBasis())); SBasis x = V[0], y = V[1]; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47aa02ef1..6df71e17e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -200,6 +200,7 @@ set(inkscape_SRC event-log.cpp extract-uri.cpp file.cpp + file-update.cpp filter-chemistry.cpp filter-enums.cpp gc-anchored.cpp @@ -325,7 +326,6 @@ set(inkscape_SRC id-clash.h inkscape-version.h inkscape.h - isinf.h knot-enums.h knot-holder-entity.h knot-ptr.h @@ -366,7 +366,6 @@ set(inkscape_SRC removeoverlap.h require-config.h resource-manager.h - round.h rubberband.h satisfied-guide-cns.h selcue.h diff --git a/src/attribute-rel-svg.cpp b/src/attribute-rel-svg.cpp index afa578061..1f4bee1b3 100644 --- a/src/attribute-rel-svg.cpp +++ b/src/attribute-rel-svg.cpp @@ -28,6 +28,27 @@ SPAttributeRelSVG * SPAttributeRelSVG::instance = NULL; bool SPAttributeRelSVG::foundFile = false; /* + * This function returns true if element is an SVG element. + */ +bool SPAttributeRelSVG::isSVGElement(Glib::ustring element) +{ + if (SPAttributeRelSVG::instance == NULL) { + SPAttributeRelSVG::instance = new SPAttributeRelSVG(); + } + + // Always valid if data file not found! + if( !foundFile ) return true; + + // Strip off "svg:" from the element's name + Glib::ustring temp = element; + if ( temp.find("svg:") != std::string::npos ) { + temp.erase( temp.find("svg:"), 4 ); + } + + return (SPAttributeRelSVG::instance->attributesOfElements.count(temp) > 0); +} + +/* * This functions checks whether an element -> attribute pair is allowed or not */ bool SPAttributeRelSVG::findIfValid(Glib::ustring attribute, Glib::ustring element) @@ -39,7 +60,7 @@ bool SPAttributeRelSVG::findIfValid(Glib::ustring attribute, Glib::ustring eleme // Always valid if data file not found! if( !foundFile ) return true; - // Strip of "svg:" from the element's name + // Strip off "svg:" from the element's name Glib::ustring temp = element; if ( temp.find("svg:") != std::string::npos ) { temp.erase( temp.find("svg:"), 4 ); diff --git a/src/attribute-rel-svg.h b/src/attribute-rel-svg.h index 74c6d3b60..c0a9cd215 100644 --- a/src/attribute-rel-svg.h +++ b/src/attribute-rel-svg.h @@ -22,6 +22,7 @@ typedef std::map<Glib::ustring, std::set<Glib::ustring> > hashList; */ class SPAttributeRelSVG { public: + static bool isSVGElement(Glib::ustring element); static bool findIfValid(Glib::ustring attribute, Glib::ustring element); private: diff --git a/src/attributes.cpp b/src/attributes.cpp index 8154bfbbe..5b3d35590 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -174,7 +174,8 @@ static SPStyleProp const props[] = { {SP_ATTR_SODIPODI_RY, "sodipodi:ry"}, {SP_ATTR_SODIPODI_START, "sodipodi:start"}, {SP_ATTR_SODIPODI_END, "sodipodi:end"}, - {SP_ATTR_SODIPODI_OPEN, "sodipodi:open"}, + {SP_ATTR_SODIPODI_OPEN, "sodipodi:open"}, // Deprecated + {SP_ATTR_SODIPODI_ARC_TYPE, "sodipodi:arc-type"}, /* SPStar */ {SP_ATTR_SODIPODI_SIDES, "sodipodi:sides"}, {SP_ATTR_SODIPODI_R1, "sodipodi:r1"}, diff --git a/src/attributes.h b/src/attributes.h index e21851bcf..6d4ac7543 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -182,6 +182,7 @@ enum SPAttributeEnum { SP_ATTR_SODIPODI_START, SP_ATTR_SODIPODI_END, SP_ATTR_SODIPODI_OPEN, + SP_ATTR_SODIPODI_ARC_TYPE, /* SPStar */ SP_ATTR_SODIPODI_SIDES, SP_ATTR_SODIPODI_R1, diff --git a/src/box3d.cpp b/src/box3d.cpp index dfc4d06e7..23766895a 100644 --- a/src/box3d.cpp +++ b/src/box3d.cpp @@ -15,12 +15,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "box3d.h" + #include <glibmm/i18n.h> #include "attributes.h" #include "xml/document.h" #include "xml/repr.h" -#include "box3d.h" +#include "bad-uri-exception.h" #include "box3d-side.h" #include "ui/tools/box3d-tool.h" #include "perspective-line.h" diff --git a/src/debug/log-display-config.cpp b/src/debug/log-display-config.cpp index ecc05b7b7..1f436a708 100644 --- a/src/debug/log-display-config.cpp +++ b/src/debug/log-display-config.cpp @@ -27,9 +27,20 @@ typedef SimpleEvent<Event::CONFIGURATION> ConfigurationEvent; class Monitor : public ConfigurationEvent { public: - Monitor(GdkScreen *screen, gint monitor) : ConfigurationEvent("monitor") { +#if GTK_CHECK_VERSION(3,22,0) + Monitor(GdkMonitor *monitor) +#else + Monitor(GdkScreen *screen, gint monitor) +#endif + : ConfigurationEvent("monitor") { GdkRectangle area; + +#if GTK_CHECK_VERSION(3,22,0) + gdk_monitor_get_geometry(monitor, &area); +#else gdk_screen_get_monitor_geometry(screen, monitor, &area); +#endif + _addProperty("x", area.x); _addProperty("y", area.y); _addProperty("width", area.width); @@ -37,10 +48,13 @@ public: } }; +#if !GTK_CHECK_VERSION(3,22,0) +// We don't need this in newer Gtk+ versions as GdkMonitor information is now +// returned directly from GdkDisplay rather than needing GdkScreen first class Screen : public ConfigurationEvent { public: Screen(GdkScreen *s) : ConfigurationEvent("screen"), screen(s) { - _addProperty("width", gdk_screen_get_width(screen)); + _addProperty("width", gdk_screen_get_width(screen)); _addProperty("height", gdk_screen_get_height(screen)); } void generateChildEvents() const { @@ -53,21 +67,28 @@ public: private: GdkScreen *screen; }; +#endif class Display : public ConfigurationEvent { public: Display() : ConfigurationEvent("display") {} void generateChildEvents() const { GdkDisplay *display=gdk_display_get_default(); -#if GTK_CHECK_VERSION(3,10,0) - GdkScreen *screen = gdk_display_get_screen(display, 0); - Logger::write<Screen>(screen); -#else - gint n_screens = gdk_display_get_n_screens(display); - for ( gint i = 0 ; i < n_screens ; i++ ) { - GdkScreen *screen = gdk_display_get_screen(display, i); - Logger::write<Screen>(screen); + +#if GTK_CHECK_VERSION(3,22,0) + gint const n_monitors = gdk_display_get_n_monitors(display); + + // Loop through all monitors and log their details + for (gint i_monitor = 0; i_monitor < n_monitors; ++i_monitor) { + GdkMonitor *monitor = gdk_display_get_monitor(display, i_monitor); + Logger::write<Monitor>(monitor); } +#else + // We used to find the number of screens, and log info for + // each of them. However, the number of screens is always + // one in Gtk+ 3 + GdkScreen *screen = gdk_display_get_default_screen(display); + Logger::write<Screen>(screen); #endif } }; diff --git a/src/decimal-round.h b/src/decimal-round.h index 6b412685e..b90958cae 100644 --- a/src/decimal-round.h +++ b/src/decimal-round.h @@ -3,14 +3,11 @@ #include <cmath> -#include "round.h" - - namespace Inkscape { /** Returns x rounded to the nearest \a nplaces decimal places. - Implemented in terms of Inkscape::round, i.e. we make no guarantees as to what happens if x is + Implemented in terms of std::round, i.e. we make no guarantees as to what happens if x is half way between two rounded numbers. Add a note to the documentation if you care which candidate is chosen for such case (round towards -infinity, +infinity, 0, or "even"). @@ -25,7 +22,7 @@ inline double decimal_round(double const x, int const nplaces) { double const multiplier = std::pow(10.0, nplaces); - return Inkscape::round( x * multiplier ) / multiplier; + return round( x * multiplier ) / multiplier; } } diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp index cb3e1f7c8..9e5f1c20b 100644 --- a/src/desktop-events.cpp +++ b/src/desktop-events.cpp @@ -156,9 +156,9 @@ static gint sp_dt_ruler_event(GtkWidget *widget, GdkEvent *event, SPDesktopWidge } SPNamedView *namedview = desktop->namedview; - if (namedview && namedview->document_rotation) { - normal *= Geom::Rotate(Geom::rad_from_deg(namedview->document_rotation * -1)); - } + //if (namedview && namedview->document_rotation) { + // normal *= Geom::Rotate(Geom::rad_from_deg(namedview->document_rotation * -1)); + //} guide = sp_guideline_new(desktop->guides, NULL, event_dt, normal); sp_guideline_set_color(SP_GUIDELINE(guide), desktop->namedview->guidehicolor); diff --git a/src/desktop.cpp b/src/desktop.cpp index 2476b10bb..c46d6e2f6 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -120,20 +120,16 @@ SPDesktop::SPDesktop() : _widget( NULL ), _guides_message_context( NULL ), _active( false ), - _w2d(), - _d2w(), _doc2dt( Geom::Scale(1, -1) ), _image_render_observer(this, "/options/rendering/imageinoutlinemode"), grids_visible( false ) { - _d2w.setIdentity(); - _w2d.setIdentity(); - layers = new Inkscape::LayerModel(); layers->_layer_activated_signal.connect(sigc::bind(sigc::ptr_fun(_layer_activated), this)); layers->_layer_deactivated_signal.connect(sigc::bind(sigc::ptr_fun(_layer_deactivated), this)); layers->_layer_changed_signal.connect(sigc::bind(sigc::ptr_fun(_layer_hierarchy_changed), this)); selection = Inkscape::GC::release( new Inkscape::Selection(layers, this) ); + // _current_affine.setRotate(M_PI/4); // To test zooming with rotation } void @@ -479,7 +475,7 @@ SPDesktop::remove_temporary_canvasitem (Inkscape::Display::TemporaryItem * tempi } void SPDesktop::redrawDesktop() { - sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _d2w); // redraw + sp_canvas_item_affine_absolute (SP_CANVAS_ITEM (main), _current_affine.d2w()); // redraw } void SPDesktop::_setDisplayMode(Inkscape::RenderMode mode) { @@ -733,164 +729,298 @@ Geom::Point SPDesktop::point() const { Geom::Point p = _widget->getPointer(); Geom::Point pw = sp_canvas_window_to_world (canvas, p); - p = w2d(pw); - Geom::Rect const r = canvas->getViewbox(); - Geom::Point r0 = w2d(r.min()); - Geom::Point r1 = w2d(r.max()); - - if (p[Geom::X] >= r0[Geom::X] && - p[Geom::X] <= r1[Geom::X] && - p[Geom::Y] >= r1[Geom::Y] && - p[Geom::Y] <= r0[Geom::Y]) - { + if (r.interiorContains(pw)) { + p = w2d(pw); return p; - } else { - return (r0 + r1) / 2; } + + // Shouldn't happen + std::cerr << "SPDesktop::point(): point outside of canvas!" << std::endl; + Geom::Point r0 = w2d(r.min()); + Geom::Point r1 = w2d(r.max()); + return (r0 + r1) / 2.0; } + /** - * Put current zoom data in history list. + * Revert back to previous transform if possible. Note: current transform is + * always at front of stack. */ void -SPDesktop::push_current_zoom (std::list<Geom::Rect> &history) +SPDesktop::prev_transform() { - Geom::Rect area = get_display_area(); + if (transforms_past.empty()) { + std::cerr << "SPDesktop::prev_transform: current transform missing!" << std::endl; + return; + } - if (history.empty() || history.front() != area) { - history.push_front(area); + if (transforms_past.size() == 1) { + messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous transform.")); + return; } + + // Push current transform into future transforms list. + transforms_future.push_front( _current_affine ); + + // Remove the current transform from the past transforms list. + transforms_past.pop_front(); + + // restore previous transform + _current_affine = transforms_past.front(); + set_display_area (false); + } + /** - * Set viewbox (x0, x1, y0 and y1 are in document pixels. Border is in screen pixels). + * Set transform to next in list. */ -void -SPDesktop::set_display_area (double x0, double y0, double x1, double y1, double border, bool log) +void SPDesktop::next_transform() { - g_assert(_widget); - bool zoomChanged = false; - - // save the zoom - if (log) { - push_current_zoom(zooms_past); - // if we do a logged zoom, our zoom-forward list is invalidated, so delete it - zooms_future.clear(); + if (transforms_future.empty()) { + this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next transform.")); + return; } - double const cx = 0.5 * (x0 + x1); - double const cy = 0.5 * (y0 + y1); + // restore next transform + _current_affine = transforms_future.front(); + set_display_area (false); - // FIXME: This 2geom idiom doesn't allow us to declare dbox const - Geom::Rect viewbox = canvas->getViewbox(); - viewbox.expandBy(-border); + // remove the just-used transform from the future transforms list + transforms_future.pop_front(); - double scale = _d2w.descrim(); - double newscale; - if (((x1 - x0) * viewbox.dimensions()[Geom::Y]) > ((y1 - y0) * viewbox.dimensions()[Geom::X])) { - newscale = viewbox.dimensions()[Geom::X] / (x1 - x0); - } else { - newscale = viewbox.dimensions()[Geom::Y] / (y1 - y0); - } + // push current transform into past transforms list + transforms_past.push_front( _current_affine ); +} - newscale = CLAMP(newscale, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); // unit: 'screen pixels' per 'document pixels' - int clear = FALSE; - if (!Geom::are_near(newscale, scale, Geom::EPSILON * scale)) { - // zoom changed - set new zoom factors - _d2w = Geom::Scale(newscale, -newscale); - _w2d = Geom::Scale(1/newscale, 1/-newscale); - redrawDesktop(); - clear = TRUE; - zoomChanged = true; +/** + * Clear transform lists. + */ +void +SPDesktop::clear_transform_history() +{ + transforms_past.clear(); + transforms_future.clear(); +} + + +/** + * Does all the dirty work in setting the display area. + * _current_affine must already be full updated (including offset). + * log: if true, save transform in transform stack for reuse. + */ +void +SPDesktop::set_display_area (bool log) +{ + // Save the transform + if (log) { + transforms_past.push_front( _current_affine ); + // if we do a logged transform, our transform-forward list is invalidated, so delete it + transforms_future.clear(); } - /* Calculate top left corner (in document pixels) */ - x0 = cx - 0.5 * viewbox.dimensions()[Geom::X] / newscale; - y1 = cy + 0.5 * viewbox.dimensions()[Geom::Y] / newscale; + redrawDesktop(); // Scroll - canvas->scrollTo(x0 * newscale - border, y1 * -newscale - border, clear); + Geom::Point offset = _current_affine.getOffset(); + canvas->scrollTo(offset, true); + // To do: if transform unchanged call with 'false' (redraw only newly exposed areas). - /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ - //sp_box3d_context_update_lines(event_context); + /* Update perspective lines if we are in the 3D box tool (so that infinite ones are shown + * correctly) */ if (SP_IS_BOX3D_CONTEXT(event_context)) { SP_BOX3D_CONTEXT(event_context)->_vpdrag->updateLines(); } _widget->updateRulers(); - _widget->updateScrollbars(_d2w.descrim()); + _widget->updateScrollbars(_current_affine.getZoom()); _widget->updateZoom(); - if ( zoomChanged ) { - signal_zoom_changed.emit(_d2w.descrim()); - } + signal_zoom_changed.emit(_current_affine.getZoom()); } -void SPDesktop::set_display_area(Geom::Rect const &a, Geom::Coord b, bool log) + +/** + * Map the drawing to the window so that 'c' lies at 'w' where where 'c' + * is a point on the canvas and 'w' is position in window in screen pixels. + */ +void +SPDesktop::set_display_area (Geom::Point const &c, Geom::Point const &w, bool log) { - set_display_area(a.min()[Geom::X], a.min()[Geom::Y], a.max()[Geom::X], a.max()[Geom::Y], b, log); + // The relative offset needed to keep c at w. + Geom::Point offset = d2w(c) - w; + _current_affine.addOffset( offset ); + set_display_area( log ); } + /** - * Return viewbox dimensions. + * Map the center of rectangle 'r' (which specifies a non-rotated region of the + * drawing) to lie at the center of the window. The zoom factor is calculated such that + * the edges of 'r' closest to 'w' are 'border' length inside of the window (if + * there is no rotation). 'r' is in document pixel units, 'border' is in screen pixels. + */ +void +SPDesktop::set_display_area( Geom::Rect const &r, double border, bool log) +{ + // Create a rectangle the size of the window aligned with origin. + Geom::Rect w( Geom::Point(), canvas->getViewbox().dimensions() ); // Not the SVG 'viewBox'. + + // Shrink window to account for border padding. + w.expandBy( -border ); + + double zoom = 1.0; + // Determine which direction limits scale: + // if (r.width/w.width > r.height/w.height) then zoom using width. + // Avoiding division in test: + if ( r.width()*w.height() > r.height()*w.width() ) { + zoom = w.width() / r.width(); + } else { + zoom = w.height() / r.height(); + } + _current_affine.setScale( zoom ); + + // Zero offset, actual offset calculated later. + _current_affine.setOffset( Geom::Point( 0, 0 ) ); + + set_display_area( r.midpoint(), w.midpoint(), log ); +} + + +/** + * Return viewbox dimensions. FixMe: Doesn't handle rotation. FixMe InvertedY */ Geom::Rect SPDesktop::get_display_area() const { Geom::Rect const viewbox = canvas->getViewbox(); - - double const scale = _d2w[0]; + double const scale = _current_affine.getZoom(); /// @fixme hardcoded desktop transform return Geom::Rect(Geom::Point(viewbox.min()[Geom::X] / scale, viewbox.max()[Geom::Y] / -scale), Geom::Point(viewbox.max()[Geom::X] / scale, viewbox.min()[Geom::Y] / -scale)); } + /** - * Revert back to previous zoom if possible. + * Zoom keeping the point 'c' fixed in the desktop window. */ void -SPDesktop::prev_zoom() +SPDesktop::zoom_absolute_keep_point (Geom::Point const &c, double zoom) +{ + zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); + Geom::Point w = d2w( c ); // Must be before zoom changed. + _current_affine.setScale( zoom ); + set_display_area( c, w ); +} + + +void +SPDesktop::zoom_relative_keep_point (Geom::Point const &c, double zoom) { - if (zooms_past.empty()) { - messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No previous zoom.")); + double new_zoom = _current_affine.getZoom() * zoom; + zoom_absolute_keep_point( c, new_zoom ); +} + + +/** + * Zoom aligning the point 'c' to the center of desktop window. + */ +void +SPDesktop::zoom_absolute_center_point (Geom::Point const &c, double zoom) +{ + zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); + _current_affine.setScale( zoom ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); +} + + +void +SPDesktop::zoom_relative_center_point (Geom::Point const &c, double zoom) +{ + double new_zoom = _current_affine.getZoom() * zoom; + zoom_absolute_center_point( c, new_zoom ); +} + + +/** + * Set display area to origin and current document dimensions. + */ +void +SPDesktop::zoom_page() +{ + Geom::Rect d(Geom::Point(0, 0), + Geom::Point(doc()->getWidth().value("px"), doc()->getHeight().value("px"))); + + if (d.minExtent() < 1.0) { return; } - // push current zoom into forward zooms list - push_current_zoom (zooms_future); + set_display_area(d, 10); +} - // restore previous zoom - Geom::Rect past = zooms_past.front(); - set_display_area (past.left(), past.top(), past.right(), past.bottom(), 0, false); +/** + * Set display area to current document width. + */ +void +SPDesktop::zoom_page_width() +{ + Geom::Rect const a = get_display_area(); + + if (doc()->getWidth().value("px") < 1.0) { + return; + } - // remove the just-added zoom from the past zooms list - zooms_past.pop_front(); + Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]), + Geom::Point(doc()->getWidth().value("px"), a.midpoint()[Geom::Y])); + + set_display_area(d, 10); } + /** - * Set zoom to next in list. + * Zoom to whole drawing. */ -void SPDesktop::next_zoom() +void +SPDesktop::zoom_drawing() { - if (zooms_future.empty()) { - this->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No next zoom.")); + g_return_if_fail (doc() != NULL); + SPItem *docitem = doc()->getRoot(); + g_return_if_fail (docitem != NULL); + + docitem->bbox_valid = FALSE; + Geom::OptRect d = docitem->desktopVisualBounds(); + + /* Note that the second condition here indicates that + ** there are no items in the drawing. + */ + if ( !d || d->minExtent() < 0.1 ) { return; } - // push current zoom into past zooms list - push_current_zoom (zooms_past); + set_display_area(*d, 10); +} - // restore next zoom - Geom::Rect future = zooms_future.front(); - set_display_area (future.left(), future.top(), future.right(), future.bottom(), 0, false); - // remove the just-used zoom from the zooms_future list - zooms_future.pop_front(); +/** + * Zoom to selection. + */ +void +SPDesktop::zoom_selection() +{ + Geom::OptRect const d = selection->visualBounds(); + + if ( !d || d->minExtent() < 0.1 ) { + return; + } + + set_display_area(*d, 10); } + /** * Performs a quick zoom into what the user is working on. * @@ -903,7 +1033,7 @@ void SPDesktop::zoom_quick(bool enable) } if (enable) { - _quick_zoom_stored_area = get_display_area(); + _quick_zoom_affine = _current_affine; bool zoomed = false; // TODO This needs to migrate into the node tool, but currently the design @@ -915,7 +1045,7 @@ void SPDesktop::zoom_quick(bool enable) 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()) { + if (!Geom::are_near(area, 0)) { set_display_area(nodes, true); zoomed = true; } @@ -924,274 +1054,148 @@ void SPDesktop::zoom_quick(bool enable) if (!zoomed) { Geom::OptRect const d = selection->visualBounds(); - if (d && d->area() * 2.0 < _quick_zoom_stored_area.area()) { + if (d) { set_display_area(*d, true); zoomed = true; } } if (!zoomed) { - zoom_relative(_quick_zoom_stored_area.midpoint()[Geom::X], _quick_zoom_stored_area.midpoint()[Geom::Y], 2.0); + Geom::Rect const d_canvas = canvas->getViewbox(); // Not SVG 'viewBox' + Geom::Point midpoint = w2d(d_canvas.midpoint()); // Midpoint of drawing on canvas. + zoom_relative_center_point(midpoint, 2.0); } } else { - set_display_area(_quick_zoom_stored_area, false); + _current_affine = _quick_zoom_affine; + set_display_area( false ); } _quick_zoom_enabled = enable; return; } + /** - * Zoom to point with absolute zoom factor. + * Tell widget to let zoom widget grab keyboard focus. */ void -SPDesktop::zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom) +SPDesktop::zoom_grab_focus() { - zoom = CLAMP (zoom, SP_DESKTOP_ZOOM_MIN, SP_DESKTOP_ZOOM_MAX); - - // maximum or minimum zoom reached, but there's no exact equality because of rounding errors; - // this check prevents "sliding" when trying to zoom in at maximum zoom; - /// \todo someone please fix calculations properly and remove this hack - if (fabs(_d2w.descrim() - zoom) < 0.0001*zoom && (fabs(SP_DESKTOP_ZOOM_MAX - zoom) < 0.01 || fabs(SP_DESKTOP_ZOOM_MIN - zoom) < 0.000001)) - return; - - Geom::Rect const viewbox = canvas->getViewbox(); - - double const width2 = viewbox.dimensions()[Geom::X] / zoom; - double const height2 = viewbox.dimensions()[Geom::Y] / zoom; - - set_display_area(cx - px * width2, - cy - py * height2, - cx + (1 - px) * width2, - cy + (1 - py) * height2, - 0.0); + _widget->letZoomGrabFocus(); } -/** - * Apply the desktop's current style or the tool style to the object. - */ -void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text) -{ - SPCSSAttr *css_current = sp_desktop_get_style(this, with_text); - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - if (prefs->getBool(tool_path + "/usecurrent") && css_current) { - obj->setCSS(css_current,"style"); - } else { - SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style"); - obj->setCSS(css,"style"); - sp_repr_css_attr_unref(css); - } - if (css_current) { - sp_repr_css_attr_unref(css_current); - } -} /** - * Zoom to center with absolute zoom factor. + * Rotate keeping the point 'c' fixed in the desktop window. */ void -SPDesktop::zoom_absolute (double cx, double cy, double zoom) +SPDesktop::rotate_absolute_keep_point (Geom::Point const &c, double rotate) { - zoom_absolute_keep_point (cx, cy, 0.5, 0.5, zoom); + Geom::Point w = d2w( c ); // Must be before rotate changed. + _current_affine.setRotate( rotate ); + set_display_area( c, w ); } -/** - * Zoom to point with relative zoom factor. - */ -void -SPDesktop::zoom_relative_keep_point (double cx, double cy, double zoom) -{ - Geom::Rect const area = get_display_area(); - - if (cx < area.min()[Geom::X]) { - cx = area.min()[Geom::X]; - } - if (cx > area.max()[Geom::X]) { - cx = area.max()[Geom::X]; - } - if (cy < area.min()[Geom::Y]) { - cy = area.min()[Geom::Y]; - } - if (cy > area.max()[Geom::Y]) { - cy = area.max()[Geom::Y]; - } - - gdouble const scale = _d2w.descrim() * zoom; - double const px = (cx - area.min()[Geom::X]) / area.dimensions()[Geom::X]; - double const py = (cy - area.min()[Geom::Y]) / area.dimensions()[Geom::Y]; - zoom_absolute_keep_point(cx, cy, px, py, scale); -} - -/** - * Zoom to center with relative zoom factor. - */ -void -SPDesktop::zoom_relative (double cx, double cy, double zoom) +void +SPDesktop::rotate_relative_keep_point (Geom::Point const &c, double rotate) { - gdouble scale = _d2w.descrim() * zoom; - zoom_absolute (cx, cy, scale); + Geom::Point w = d2w( c ); // Must be before rotate changed. + _current_affine.addRotate( rotate ); + set_display_area( c, w ); } -/** - * Set display area to origin and current document dimensions. - */ -void -SPDesktop::zoom_page() -{ - Geom::Rect d(Geom::Point(0, 0), - Geom::Point(doc()->getWidth().value("px"), doc()->getHeight().value("px"))); - - if (d.minExtent() < 1.0) { - return; - } - - set_display_area(d, 10); -} /** - * Set display area to current document width. + * Rotate aligning the point 'c' to the center of desktop window. */ -void -SPDesktop::zoom_page_width() +void +SPDesktop::rotate_absolute_center_point (Geom::Point const &c, double rotate) { - Geom::Rect const a = get_display_area(); - - if (doc()->getWidth().value("px") < 1.0) { - return; - } + _current_affine.setRotate( rotate ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); +} - Geom::Rect d(Geom::Point(0, a.midpoint()[Geom::Y]), - Geom::Point(doc()->getWidth().value("px"), a.midpoint()[Geom::Y])); - set_display_area(d, 10); +void +SPDesktop::rotate_relative_center_point (Geom::Point const &c, double rotate) +{ + _current_affine.addRotate( rotate ); + Geom::Rect viewbox = canvas->getViewbox(); + set_display_area( c, viewbox.midpoint() ); } + /** - * Zoom to selection. + * Scroll canvas by to a particular point (window coordinates). */ void -SPDesktop::zoom_selection() +SPDesktop::scroll_absolute (Geom::Point const &point, bool is_scrolling) { - Geom::OptRect const d = selection->visualBounds(); + canvas->scrollTo(point, FALSE, is_scrolling); + _current_affine.setOffset( point ); - if ( !d || d->minExtent() < 0.1 ) { - return; - } + /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ + //sp_box3d_context_update_lines(event_context); + if (SP_IS_BOX3D_CONTEXT(event_context)) { + SP_BOX3D_CONTEXT(event_context)->_vpdrag->updateLines(); + } - set_display_area(*d, 10); + _widget->updateRulers(); + _widget->updateScrollbars(_current_affine.getZoom()); } -/** - * Tell widget to let zoom widget grab keyboard focus. - */ -void -SPDesktop::zoom_grab_focus() -{ - _widget->letZoomGrabFocus(); -} /** - * Zoom to whole drawing. + * Scroll canvas by specific coordinate amount (window coordinates). */ void -SPDesktop::zoom_drawing() +SPDesktop::scroll_relative (Geom::Point const &delta, bool is_scrolling) { - g_return_if_fail (doc() != NULL); - SPItem *docitem = doc()->getRoot(); - g_return_if_fail (docitem != NULL); - - docitem->bbox_valid = FALSE; - Geom::OptRect d = docitem->desktopVisualBounds(); - - /* Note that the second condition here indicates that - ** there are no items in the drawing. - */ - if ( !d || d->minExtent() < 0.1 ) { - return; - } - - set_display_area(*d, 10); + Geom::Rect const viewbox = canvas->getViewbox(); + scroll_absolute( viewbox.min() - delta, is_scrolling ); } + /** * Scroll canvas by specific coordinate amount in svg coordinates. */ void -SPDesktop::scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling) +SPDesktop::scroll_relative_in_svg_coords (double dx, double dy, bool is_scrolling) { - double scale = _d2w.descrim(); - scroll_world(dx*scale, dy*scale, is_scrolling); + double scale = _current_affine.getZoom(); + scroll_relative(Geom::Point(dx*scale, dy*scale), is_scrolling); } + /** - * Scroll canvas by specific coordinate amount. + * Scroll screen so as to keep point 'p' visible in window. + * (Used, for example, when a node is being dragged.) + * 'p': The point in desktop coordinates. + * 'autoscrollspeed': The scroll speed (or zero to use preferences' value). */ -void -SPDesktop::scroll_world (double dx, double dy, bool is_scrolling) -{ - g_assert(_widget); - - Geom::Rect const viewbox = canvas->getViewbox(); - - canvas->scrollTo(viewbox.min()[Geom::X] - dx, viewbox.min()[Geom::Y] - dy, FALSE, is_scrolling); - - /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ - //sp_box3d_context_update_lines(event_context); - if (SP_IS_BOX3D_CONTEXT(event_context)) { - SP_BOX3D_CONTEXT(event_context)->_vpdrag->updateLines(); - } - - _widget->updateRulers(); - _widget->updateScrollbars(_d2w.descrim()); -} - bool SPDesktop::scroll_to_point (Geom::Point const &p, gdouble autoscrollspeed) { - using Geom::X; - using Geom::Y; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + // autoscrolldistance is in screen pixels. gdouble autoscrolldistance = (gdouble) prefs->getIntLimited("/options/autoscrolldistance/value", 0, -1000, 10000); - // autoscrolldistance is in screen pixels, but the display area is in document units - autoscrolldistance /= _d2w.descrim(); - // FIXME: This 2geom idiom doesn't allow us to declare dbox const - Geom::Rect dbox = get_display_area(); - dbox.expandBy(-autoscrolldistance); - - if (!(p[X] > dbox.min()[X] && p[X] < dbox.max()[X]) || - !(p[Y] > dbox.min()[Y] && p[Y] < dbox.max()[Y]) ) { - - Geom::Point const s_w( p * (Geom::Affine)_d2w ); - - gdouble x_to; - if (p[X] < dbox.min()[X]) - x_to = dbox.min()[X]; - else if (p[X] > dbox.max()[X]) - x_to = dbox.max()[X]; - else - x_to = p[X]; - - gdouble y_to; - if (p[Y] < dbox.min()[Y]) - y_to = dbox.min()[Y]; - else if (p[Y] > dbox.max()[Y]) - y_to = dbox.max()[Y]; - else - y_to = p[Y]; - - Geom::Point const d_dt(x_to, y_to); - Geom::Point const d_w( d_dt * _d2w ); - Geom::Point const moved_w( d_w - s_w ); + Geom::Rect w = canvas->getViewbox(); // Window in screen coordinates. + w.expandBy(-autoscrolldistance); // Shrink window + + Geom::Point c = d2w(p); // Point 'p' in screen coordinates. + if (!w.contains(c)) { + + Geom::Point c2 = w.clamp(c); // Constrain c to window. if (autoscrollspeed == 0) autoscrollspeed = prefs->getDoubleLimited("/options/autoscrollspeed/value", 1, 0, 10); if (autoscrollspeed != 0) - scroll_world (autoscrollspeed * moved_w); + scroll_relative (autoscrollspeed * (c2 - c) ); return true; } @@ -1380,6 +1384,28 @@ SPDesktop::onWindowStateEvent (GdkEventWindowState* event) return false; } + +/** + * Apply the desktop's current style or the tool style to the object. + */ +void SPDesktop::applyCurrentOrToolStyle(SPObject *obj, Glib::ustring const &tool_path, bool with_text) +{ + SPCSSAttr *css_current = sp_desktop_get_style(this, with_text); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + if (prefs->getBool(tool_path + "/usecurrent") && css_current) { + obj->setCSS(css_current,"style"); + } else { + SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style"); + obj->setCSS(css,"style"); + sp_repr_css_attr_unref(css); + } + if (css_current) { + sp_repr_css_attr_unref(css_current); + } +} + + void SPDesktop::setToolboxFocusTo (gchar const *label) { @@ -1623,7 +1649,7 @@ SPDesktop::_onSelectionModified (Inkscape::Selection */*selection*/, guint /*flags*/, SPDesktop *dt) { if (!dt->_widget) return; - dt->_widget->updateScrollbars (dt->_d2w.descrim()); + dt->_widget->updateScrollbars (dt->_current_affine.getZoom()); } static void @@ -1765,17 +1791,17 @@ static void _namedview_modified (SPObject *obj, guint flags, SPDesktop *desktop) Geom::Affine SPDesktop::w2d() const { - return _w2d; + return _current_affine.w2d(); } Geom::Point SPDesktop::w2d(Geom::Point const &p) const { - return p * _w2d; + return p * _current_affine.w2d(); } Geom::Point SPDesktop::d2w(Geom::Point const &p) const { - return p * _d2w; + return p * _current_affine.d2w(); } Geom::Affine SPDesktop::doc2dt() const diff --git a/src/desktop.h b/src/desktop.h index 3652d4a97..3dc11b557 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -29,6 +29,7 @@ #include <sigc++/sigc++.h> #include <2geom/affine.h> +#include <2geom/transforms.h> #include <2geom/rect.h> #include "ui/view/view.h" @@ -179,11 +180,6 @@ public: SPCSSAttr *current; ///< current style bool _focusMode; ///< Whether we're focused working or general working - std::list<Geom::Rect> zooms_past; - std::list<Geom::Rect> zooms_future; - - bool _quick_zoom_enabled; ///< Signifies that currently we're in quick zoom mode - Geom::Rect _quick_zoom_stored_area; ///< The area of the screen before quick zoom unsigned int dkey; unsigned int number; guint window_state; @@ -323,38 +319,42 @@ public: SPItem *getGroupAtPoint(Geom::Point const &p) const; Geom::Point point() const; + void prev_transform(); + void next_transform(); + void clear_transform_history(); + + void set_display_area (bool log = true); + void set_display_area (Geom::Point const &c, Geom::Point const &w, bool log = true); + void set_display_area (Geom::Rect const &a, Geom::Coord border, bool log = true); Geom::Rect get_display_area() const; - void set_display_area (double x0, double y0, double x1, double y1, double border, bool log = true); - void set_display_area(Geom::Rect const &a, Geom::Coord border, bool log = true); - void zoom_absolute (double cx, double cy, double zoom); - void zoom_relative (double cx, double cy, double zoom); - void zoom_absolute_keep_point (double cx, double cy, double px, double py, double zoom); - void zoom_relative_keep_point (double cx, double cy, double zoom); - void zoom_relative_keep_point (Geom::Point const &c, double const zoom) - { - zoom_relative_keep_point (c[Geom::X], c[Geom::Y], zoom); - } + + void zoom_absolute_keep_point (Geom::Point const &c, double const zoom); + void zoom_relative_keep_point (Geom::Point const &c, double const zoom); + void zoom_absolute_center_point (Geom::Point const &c, double const zoom); + void zoom_relative_center_point (Geom::Point const &c, double const zoom); void zoom_page(); void zoom_page_width(); void zoom_drawing(); void zoom_selection(); - void zoom_grab_focus(); - double current_zoom() const { return _d2w.descrim(); } - void prev_zoom(); - void next_zoom(); - void zoom_quick(bool enable = true); + double current_zoom() const { return _current_affine.getZoom(); } + + void zoom_quick(bool enable = true); /** \brief Returns whether the desktop is in quick zoom mode or not */ bool quick_zoomed(void) { return _quick_zoom_enabled; } + void zoom_grab_focus(); + + void rotate_absolute_keep_point (Geom::Point const &c, double const rotate); + void rotate_relative_keep_point (Geom::Point const &c, double const rotate); + void rotate_absolute_center_point (Geom::Point const &c, double const rotate); + void rotate_relative_center_point (Geom::Point const &c, double const rotate); + + void scroll_absolute (Geom::Point const &point, bool is_scrolling = false); + void scroll_relative (Geom::Point const &delta, bool is_scrolling = false); + void scroll_relative_in_svg_coords (double dx, double dy, bool is_scrolling = false); bool scroll_to_point (Geom::Point const &s_dt, gdouble autoscrollspeed = 0); - void scroll_world (double dx, double dy, bool is_scrolling = false); - void scroll_world (Geom::Point const &scroll, bool is_scrolling = false) - { - scroll_world(scroll[Geom::X], scroll[Geom::Y], is_scrolling); - } - void scroll_world_in_svg_coords (double dx, double dy, bool is_scrolling = false); void getWindowGeometry (gint &x, gint &y, gint &w, gint &h); void setWindowPosition (Geom::Point p); @@ -407,7 +407,7 @@ public: */ void show_dialogs(); - Geom::Affine w2d() const; //transformation from window to desktop coordinates (used for zooming) + Geom::Affine w2d() const; //transformation from window to desktop coordinates (zoom/rotate). Geom::Point w2d(Geom::Point const &p) const; Geom::Point d2w(Geom::Point const &p) const; Geom::Affine doc2dt() const; @@ -430,8 +430,82 @@ private: Inkscape::UI::View::EditWidgetInterface *_widget; Inkscape::MessageContext *_guides_message_context; bool _active; - Geom::Affine _w2d; - Geom::Affine _d2w; + + // This simple class ensures that _w2d is always in sync with _rotation and _scale + // We keep rotation and scale separate to avoid having to extract them from the affine. + // With offset, this describes fully how to map the drawing to the window. + // Future: merge offset as a translation in w2d. + class DesktopAffine { + public: + Geom::Affine w2d() const { return _w2d; }; + Geom::Affine d2w() const { return _d2w; }; + + void setScale( Geom::Scale scale ) { + _scale = scale; + _update(); + } + void setScale( double scale ) { + _scale = Geom::Scale(scale, -scale); // Y flip + _update(); + } + void addScale( Geom::Scale scale) { + _scale *= scale; + _update(); + } + void addScale( double scale ) { + _scale *= Geom::Scale(scale, -scale); // Y flip?? Check + _update(); + } + + void setRotate( Geom::Rotate rotate ) { + _rotate = rotate; + _update(); + } + void setRotate( double rotate ) { + _rotate = Geom::Rotate( rotate ); + _update(); + } + void addRotate( Geom::Rotate rotate ) { + _rotate *= rotate; + _update(); + } + void addRotate( double rotate ) { + _rotate *= Geom::Rotate( rotate ); + _update(); + } + + double getZoom() const { + return _d2w.descrim(); + } + + void setOffset( Geom::Point offset ) { + _offset = offset; + } + void addOffset( Geom::Point offset ) { + _offset += offset; + } + Geom::Point getOffset() { + return _offset; + } + + private: + void _update() { + _d2w = _rotate * _scale; + _w2d = _d2w.inverse(); + } + Geom::Affine _w2d; // Window to desktop + Geom::Affine _d2w; // Desktop to window + Geom::Rotate _rotate; // Rotate part of _w2d + Geom::Scale _scale; // Scale part of _w2d + Geom::Point _offset; // Point on canvas to align to (0,0) of window + }; + + DesktopAffine _current_affine; + std::list<DesktopAffine> transforms_past; + std::list<DesktopAffine> transforms_future; + bool _quick_zoom_enabled; ///< Signifies that currently we're in quick zoom mode + DesktopAffine _quick_zoom_affine; ///< The transform of the screen before quick zoom + Geom::Affine _doc2dt; /* @@ -457,8 +531,6 @@ private: bool grids_visible; /* don't set this variable directly, use the method below */ void set_grids_visible(bool visible); - void push_current_zoom(std::list<Geom::Rect> &); - sigc::signal<void, SPDesktop*> _destroy_signal; sigc::signal<void,SPDesktop*,SPDocument*> _document_replaced_signal; sigc::signal<void> _activate_signal; diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index 94ee4ccfd..589cc849d 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -42,7 +42,6 @@ #include "2geom/line.h" #include "2geom/angle.h" #include "helper/mathfns.h" -#include "round.h" #include "util/units.h" using Inkscape::Util::unit_table; diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index cf7d04555..2dadde336 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -841,7 +841,9 @@ void CanvasXYGrid::Update (Geom::Affine const &affine, unsigned int /*flags*/) { ow = origin * affine; - sw = spacing * affine; + // Temp hack to insure grid doesn't collapse with rotation. + sw[0] = spacing[0] * sqrt(affine[0]*affine[0] + affine[1]*affine[1]); + sw[1] = spacing[1] * sqrt(affine[2]*affine[2] + affine[3]*affine[3]); sw -= Geom::Point(affine[4], affine[5]); for(int dim = 0; dim < 2; dim++) { diff --git a/src/display/guideline.cpp b/src/display/guideline.cpp index f66b65e1a..cde10f6c2 100644 --- a/src/display/guideline.cpp +++ b/src/display/guideline.cpp @@ -10,12 +10,15 @@ * Copyright (C) 2000-2002 Lauris Kaplinski * Copyright (C) 2007 Johan Engelen * Copyright (C) 2009 Maximilian Albert + * Copyright (C) 2017 Tavmjong Bah * * Released under GNU GPL, read the file 'COPYING' for more information */ #include <2geom/coord.h> #include <2geom/transforms.h> +#include <2geom/line.h> + #include "sp-canvas-util.h" #include "guideline.h" #include "display/cairo-utils.h" @@ -29,8 +32,6 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf); static double sp_guideline_point(SPCanvasItem *item, Geom::Point p, SPCanvasItem **actual_item); -static void sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 rgba); - G_DEFINE_TYPE(SPGuideLine, sp_guideline, SP_TYPE_CANVAS_ITEM); static void sp_guideline_class_init(SPGuideLineClass *c) @@ -76,10 +77,6 @@ static void sp_guideline_destroy(SPCanvasItem *object) static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) { - //TODO: the routine that renders the label of a specific guideline sometimes - // ends up erasing the labels of the other guidelines. - // Maybe we should render all labels everytime. - SPGuideLine const *gl = SP_GUIDELINE (item); cairo_save(buf->ct); @@ -89,8 +86,9 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) cairo_set_line_cap(buf->ct, CAIRO_LINE_CAP_SQUARE); cairo_set_font_size(buf->ct, 10); - Geom::Point normal_dt = /*unit_vector*/(gl->normal_to_line * gl->affine.withoutTranslation()); // note that normal_dt does not have unit length - Geom::Point point_on_line_dt = gl->point_on_line * gl->affine; + // normal_dt does not have unit length + Geom::Point normal_dt = gl->normal_to_line * gl->affine.withoutTranslation(); + Geom::Point point_on_line_dt = gl->point_on_line * gl->affine; if (gl->label) { int px = round(point_on_line_dt[Geom::X]); @@ -104,55 +102,54 @@ static void sp_guideline_render(SPCanvasItem *item, SPCanvasBuf *buf) cairo_restore(buf->ct); } - if ( Geom::are_near(normal_dt[Geom::Y], 0.) ) { // is vertical? + // Draw guide. + // Special case horizontal and vertical lines so they snap to pixels. + // Don't use isHorizontal()/isVertical() as they test only exact matches. + if ( Geom::are_near(normal_dt[Geom::Y], 0.0) ) { // Vertical? + int position = round(point_on_line_dt[Geom::X]); cairo_move_to(buf->ct, position + 0.5, buf->rect.top() + 0.5); cairo_line_to(buf->ct, position + 0.5, buf->rect.bottom() - 0.5); cairo_stroke(buf->ct); - } else if ( Geom::are_near(normal_dt[Geom::X], 0.) ) { // is horizontal? + + } else if ( Geom::are_near(normal_dt[Geom::X], 0.0) ) { // Horizontal? + int position = round(point_on_line_dt[Geom::Y]); cairo_move_to(buf->ct, buf->rect.left() + 0.5, position + 0.5); cairo_line_to(buf->ct, buf->rect.right() - 0.5, position + 0.5); cairo_stroke(buf->ct); - } else { - // render angled line. Once intersection has been detected, draw from there. - Geom::Point parallel_to_line( normal_dt.ccw() ); - - //try to intersect with left vertical of rect - double y_intersect_left = (buf->rect.left() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; - if ( (y_intersect_left >= buf->rect.top()) && (y_intersect_left <= buf->rect.bottom()) ) { - // intersects with left vertical! - double y_intersect_right = (buf->rect.right() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; - sp_guideline_drawline (buf, buf->rect.left(), static_cast<gint>(round(y_intersect_left)), buf->rect.right(), static_cast<gint>(round(y_intersect_right)), gl->rgba); - goto end; - } - //try to intersect with right vertical of rect - double y_intersect_right = (buf->rect.right() - point_on_line_dt[Geom::X]) * parallel_to_line[Geom::Y] / parallel_to_line[Geom::X] + point_on_line_dt[Geom::Y]; - if ( (y_intersect_right >= buf->rect.top()) && (y_intersect_right <= buf->rect.bottom()) ) { - // intersects with right vertical! - sp_guideline_drawline (buf, buf->rect.right(), static_cast<gint>(round(y_intersect_right)), buf->rect.left(), static_cast<gint>(round(y_intersect_left)), gl->rgba); - goto end; - } + } else { - //try to intersect with top horizontal of rect - double x_intersect_top = (buf->rect.top() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; - if ( (x_intersect_top >= buf->rect.left()) && (x_intersect_top <= buf->rect.right()) ) { - // intersects with top horizontal! - double x_intersect_bottom = (buf->rect.bottom() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; - sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_top)), buf->rect.top(), static_cast<gint>(round(x_intersect_bottom)), buf->rect.bottom(), gl->rgba); - goto end; + Geom::Line guide = + Geom::Line::from_origin_and_vector( point_on_line_dt, Geom::rot90(normal_dt) ); + + // Find intersections of guide with buf rectangle. There should be zero or two. + std::vector<Geom::Point> intersections; + for (unsigned i = 0; i < 4; ++i) { + Geom::LineSegment side( buf->rect.corner(i), buf->rect.corner((i+1)%4) ); + try { + Geom::OptCrossing oc = Geom::intersection(guide, side); + if (oc) { + intersections.push_back( guide.pointAt((*oc).ta)); + } + } catch (Geom::InfiniteSolutions) { + // Shouldn't happen as we have already taken care of horizontal/vertical guides. + std::cerr << "sp_guideline_render: Error: Infinite intersections." << std::endl; + } } - //try to intersect with bottom horizontal of rect - double x_intersect_bottom = (buf->rect.bottom() - point_on_line_dt[Geom::Y]) * parallel_to_line[Geom::X] / parallel_to_line[Geom::Y] + point_on_line_dt[Geom::X]; - if ( (x_intersect_top >= buf->rect.left()) && (x_intersect_top <= buf->rect.right()) ) { - // intersects with bottom horizontal! - sp_guideline_drawline (buf, static_cast<gint>(round(x_intersect_bottom)), buf->rect.bottom(), static_cast<gint>(round(x_intersect_top)), buf->rect.top(), gl->rgba); - goto end; + if (intersections.size() == 2) { + double x0 = intersections[0][Geom::X]; + double x1 = intersections[1][Geom::X]; + double y0 = intersections[0][Geom::Y]; + double y1 = intersections[1][Geom::Y]; + cairo_move_to(buf->ct, x0, y0); + cairo_line_to(buf->ct, x1, y1); + cairo_stroke(buf->ct); } } - end: + cairo_restore(buf->ct); } @@ -179,15 +176,10 @@ static void sp_guideline_update(SPCanvasItem *item, Geom::Affine const &affine, } gl->affine = affine; - Geom::Point pol_transformed = gl->point_on_line * affine; - if (gl->is_horizontal()) { - sp_canvas_update_bbox (item, -1000000, round(pol_transformed[Geom::Y] - 16), 1000000, round(pol_transformed[Geom::Y] + 1)); - } else if (gl->is_vertical()) { - sp_canvas_update_bbox (item, round(pol_transformed[Geom::X]), -1000000, round(pol_transformed[Geom::X] + 16), 1000000); - } else { - //TODO: labels in angled guidelines are not showing up for some reason. - sp_canvas_update_bbox (item, -1000000, -1000000, 1000000, 1000000); - } + + // Rotated canvas requires large bbox for all guides. + sp_canvas_update_bbox (item, -1000000, -1000000, 1000000, 1000000); + } // Returns 0.0 if point is on the guideline @@ -276,14 +268,6 @@ void sp_guideline_delete(SPGuideLine *gl) sp_canvas_item_destroy(SP_CANVAS_ITEM(gl)); } -static void -sp_guideline_drawline (SPCanvasBuf *buf, gint x0, gint y0, gint x1, gint y1, guint32 /*rgba*/) -{ - cairo_move_to(buf->ct, x0 + 0.5, y0 + 0.5); - cairo_line_to(buf->ct, x1 + 0.5, y1 + 0.5); - cairo_stroke(buf->ct); -} - /* Local Variables: mode:c++ diff --git a/src/display/nr-filter-turbulence.cpp b/src/display/nr-filter-turbulence.cpp index 19dedb67c..1397c0f34 100644 --- a/src/display/nr-filter-turbulence.cpp +++ b/src/display/nr-filter-turbulence.cpp @@ -286,15 +286,7 @@ private: static int const BSize = 0x100; static int const BMask = 0xff; -#ifdef CPP11 // GCC 4.6.1 (currently used on Windows) does not correctly set __cplusplus in C++11 mode, so configure with -DCPP11 to make this work in C++11 mode static double constexpr PerlinOffset = 4096.0; -#else -#if (__cplusplus < 201103L) - static double const PerlinOffset; -#else - static double constexpr PerlinOffset = 4096.0; -#endif -#endif Geom::Rect _tile; Geom::Point _baseFreq; @@ -311,10 +303,6 @@ private: bool _fractalnoise; }; -#if !defined(CPP11) && __cplusplus < 201103L - double const TurbulenceGenerator::PerlinOffset = 4096.0; -#endif - FilterTurbulence::FilterTurbulence() : gen(new TurbulenceGenerator()) , XbaseFrequency(0) diff --git a/src/display/nr-filter-utils.h b/src/display/nr-filter-utils.h index 35a74d7c1..52b6bd11a 100644 --- a/src/display/nr-filter-utils.h +++ b/src/display/nr-filter-utils.h @@ -14,8 +14,6 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "round.h" - namespace Inkscape { namespace Filters { diff --git a/src/display/nr-filter.cpp b/src/display/nr-filter.cpp index 789758d9c..d430553d4 100644 --- a/src/display/nr-filter.cpp +++ b/src/display/nr-filter.cpp @@ -48,11 +48,6 @@ #include "sp-filter-units.h" #include "preferences.h" -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif - namespace Inkscape { namespace Filters { diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 68eae0a65..61602e880 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -48,6 +48,10 @@ #include <iomanip> #include <glibmm/i18n.h> +#if GTK_CHECK_VERSION(3,20,0) +# include <gdkmm/seat.h> +#endif + using Inkscape::Debug::GdkEventLatencyTracker; // gtk_check_version returns non-NULL on failure @@ -72,9 +76,15 @@ struct SPCanvasGroupClass { static void ungrab_default_client_pointer(guint32 const time = GDK_CURRENT_TIME) { auto const display = Gdk::Display::get_default(); + +#if GTK_CHECK_VERSION(3,20,0) + auto const seat = display->get_default_seat(); + seat->ungrab(); +#else auto const dm = display->get_device_manager(); auto const device = dm->get_client_pointer(); device->ungrab(time); +#endif } /** @@ -625,9 +635,22 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor, // fixme: Top hack (Lauris) // fixme: If we add key masks to event mask, Gdk will abort (Lauris) - // fixme: But Canvas actualle does get key events, so all we need is routing these here - auto dm = gdk_display_get_device_manager(gdk_display_get_default()); + // fixme: But Canvas actually does get key events, so all we need is routing these here + auto display = gdk_display_get_default(); +#if GTK_CHECK_VERSION(3,20,0) + auto seat = gdk_display_get_default_seat(display); + gdk_seat_grab(seat, + getWindow(item->canvas), + GDK_SEAT_CAPABILITY_ALL_POINTING, + FALSE, + cursor, + NULL, + NULL, + NULL); +#else + auto dm = gdk_display_get_device_manager(display); auto device = gdk_device_manager_get_client_pointer(dm); + gdk_device_grab(device, getWindow(item->canvas), GDK_OWNERSHIP_NONE, @@ -635,6 +658,7 @@ int sp_canvas_item_grab(SPCanvasItem *item, guint event_mask, GdkCursor *cursor, (GdkEventMask)(event_mask & (~(GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK))), cursor, etime); +#endif item->canvas->_grabbed_item = item; item->canvas->_grabbed_event_mask = event_mask; @@ -1034,7 +1058,7 @@ void SPCanvas::handle_realize(GtkWidget *widget) attributes.width = allocation.width; attributes.height = allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gdk_visual_get_system(); + attributes.visual = gdk_screen_get_system_visual(gdk_screen_get_default()); attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK | @@ -1694,8 +1718,14 @@ bool SPCanvas::paintRect(int xx0, int yy0, int xx1, int yy1) gint x, y; auto const display = Gdk::Display::get_default(); + +#if GTK_CHECK_VERSION(3,20,0) + auto const seat = display->get_default_seat(); + auto const device = seat->get_pointer(); +#else auto const dm = display->get_device_manager(); auto const device = dm->get_client_pointer(); +#endif gdk_window_get_device_position(gtk_widget_get_window(GTK_WIDGET(this)), device->gobj(), @@ -1907,10 +1937,16 @@ double start_angle = 0; bool started = false; bool rotated = false; -void SPCanvas::scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling) +/** + * Scroll screen to point 'c'. 'c' is measured in screen pixels. + */ +void SPCanvas::scrollTo( Geom::Point const &c, unsigned int clear, bool is_scrolling) { GtkAllocation allocation; + double cx = c[Geom::X]; + double cy = c[Geom::Y]; + int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels) int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do! int dx = ix - _x0; // dx and dy specify the displacement (scroll) of the diff --git a/src/display/sp-canvas.h b/src/display/sp-canvas.h index 21b6760f2..708653bdf 100644 --- a/src/display/sp-canvas.h +++ b/src/display/sp-canvas.h @@ -70,8 +70,8 @@ GType sp_canvas_get_type() G_GNUC_CONST; * Port of GnomeCanvas for inkscape needs. */ struct SPCanvas { - /// Scrolls canvas to specific position (cx and cy are measured in screen pixels). - void scrollTo(double cx, double cy, unsigned int clear, bool is_scrolling = false); + /// Scrolls canvas to specific position (c is measured in screen pixels). + void scrollTo(Geom::Point const &c, unsigned int clear, bool is_scrolling = false); void startRotateTo(double angle); void rotateTo(double angle); bool endRotateTo(); diff --git a/src/document-undo.cpp b/src/document-undo.cpp index ae90c916e..fb1f47312 100644 --- a/src/document-undo.cpp +++ b/src/document-undo.cpp @@ -282,7 +282,7 @@ gboolean Inkscape::DocumentUndo::undo(SPDocument *doc) if (ret) INKSCAPE.external_change(); SPObject *updated = doc->getRoot(); - if (updated) { + if (updated && !ret) { updated->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } return ret; diff --git a/src/document.cpp b/src/document.cpp index 8072ec53d..c7115f906 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -49,6 +49,7 @@ #include "display/drawing.h" #include "document-private.h" #include "document-undo.h" +#include "file.h" #include "id-clash.h" #include "inkscape.h" #include "inkscape-version.h" @@ -61,6 +62,11 @@ #include "sp-symbol.h" #include "xml/rebase-hrefs.h" +#include "libcroco/cr-sel-eng.h" +#include "libcroco/cr-selector.h" +#include "libcroco/cr-parser.h" +#include "src/xml/croco-node-iface.h" + using Inkscape::DocumentUndo; using Inkscape::Util::unit_table; @@ -71,7 +77,7 @@ using Inkscape::Util::unit_table; // since we want it to happen when there are no more updates. #define SP_DOCUMENT_REROUTING_PRIORITY (G_PRIORITY_HIGH_IDLE - 1) - +bool sp_no_convert_text_baseline_spacing = false; static gint sp_document_idle_handler(gpointer data); static gint sp_document_rerouting_handler(gpointer data); @@ -452,6 +458,17 @@ SPDocument *SPDocument::createDoc(Inkscape::XML::Document *rdoc, )); document->oldSignalsConnected = true; + /** Fix baseline spacing (pre-92 files) **/ + if ( (!sp_no_convert_text_baseline_spacing) + && sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) { + sp_file_convert_text_baseline_spacing(document); + } + + /** Fix font names in legacy documents (pre-92 files) **/ + if ( sp_version_inside_range( document->root->version.inkscape, 0, 1, 0, 92 ) ) { + sp_file_convert_font_name(document); + } + return document; } @@ -1053,6 +1070,104 @@ sigc::connection SPDocument::connectIdChanged(gchar const *id, return priv->id_changed_signals[g_quark_from_string(id)].connect(slot); } +void _getObjectsByClassRecursive(Glib::ustring const &klass, SPObject *parent, std::vector<SPObject *> &objects) +{ + if (parent) { + Glib::ustring class_attribute; + char const *temp = parent->getAttribute("class"); + if (temp) { + class_attribute = temp; + } + + if (class_attribute.find( klass ) != std::string::npos) { + objects.push_back( parent ); + } + + // Check children + for (auto& child : parent->children) { + _getObjectsByClassRecursive( klass, &child, objects ); + } + } +} + +std::vector<SPObject *> SPDocument::getObjectsByClass(Glib::ustring const &klass) const +{ + std::vector<SPObject *> objects; + g_return_val_if_fail(!klass.empty(), objects); + + _getObjectsByClassRecursive(klass, root, objects); + return objects; +} + +void _getObjectsByElementRecursive(Glib::ustring const &element, SPObject *parent, + std::vector<SPObject *> &objects) +{ + if (parent) { + Glib::ustring prefixed = "svg:" + element; + if (parent->getRepr()->name() == prefixed) { + objects.push_back(parent); + } + + // Check children + for (auto& child : parent->children) { + _getObjectsByElementRecursive(element, &child, objects); + } + } +} + +std::vector<SPObject *> SPDocument::getObjectsByElement(Glib::ustring const &element) const +{ + std::vector<SPObject *> objects; + g_return_val_if_fail(!element.empty(), objects); + + _getObjectsByElementRecursive(element, root, objects); + return objects; +} + +void _getObjectsBySelectorRecursive(SPObject *parent, + CRSelEng *sel_eng, CRSimpleSel *simple_sel, + std::vector<SPObject *> &objects) +{ + if (parent) { + gboolean result = false; + cr_sel_eng_matches_node( sel_eng, simple_sel, parent->getRepr(), &result ); + if (result) { + objects.push_back(parent); + } + + // Check children + for (auto& child : parent->children) { + _getObjectsBySelectorRecursive(&child, sel_eng, simple_sel, objects); + } + } +} + +std::vector<SPObject *> SPDocument::getObjectsBySelector(Glib::ustring const &selector) const +{ + // std::cout << "\nSPDocument::getObjectsBySelector: " << selector << std::endl; + + std::vector<SPObject *> objects; + g_return_val_if_fail(!selector.empty(), objects); + + static CRSelEng *sel_eng = NULL; + if (!sel_eng) { + sel_eng = cr_sel_eng_new(); + cr_sel_eng_set_node_iface(sel_eng, &Inkscape::XML::croco_node_iface); + } + + Glib::ustring my_selector = selector + " {"; // Parsing fails sometimes without '{'. Fix me + CRSelector *cr_selector = cr_selector_parse_from_buf ((guchar*)my_selector.c_str(), CR_UTF_8); + // char * cr_string = (char*)cr_selector_to_string( cr_selector ); + // std::cout << " selector: |" << (cr_string?cr_string:"Empty") << "|" << std::endl; + CRSelector const *cur = NULL; + for (cur = cr_selector; cur; cur = cur->next) { + if (cur->simple_sel ) { + _getObjectsBySelectorRecursive(root, sel_eng, cur->simple_sel, objects); + } + } + return objects; +} + void SPDocument::bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object) { if (object) { @@ -1408,7 +1523,9 @@ static SPItem *find_group_at_point(unsigned int dkey, SPGroup *group, Geom::Poin if (SP_IS_GROUP(&o) && SP_GROUP(&o)->effectiveLayerMode(dkey) != SPGroup::LAYER ) { SPItem *child = SP_ITEM(&o); Inkscape::DrawingItem *arenaitem = child->get_arenaitem(dkey); - arenaitem->drawing().update(); + if (arenaitem) { + arenaitem->drawing().update(); + } // seen remembers the last (topmost) of groups pickable at this point if (arenaitem && arenaitem->pick(p, delta, 1) != NULL) { diff --git a/src/document.h b/src/document.h index 813d4ae49..db50f1717 100644 --- a/src/document.h +++ b/src/document.h @@ -30,6 +30,24 @@ #include <set> #include <deque> +// This variable is introduced with 0.92.1 +// with the introduction of automatic fix +// for files detected to have been created +// with previous versions to have a similar +// look in 0.92+. +extern bool sp_no_convert_text_baseline_spacing; + + + +// This variable is introduced with 0.92.1 +// with the introduction of automatic fix +// for files detected to have been created +// with previous versions to have a similar +// look in 0.92+. +extern bool sp_do_not_fix_pre_92; + + + namespace Avoid { class Router; } @@ -179,6 +197,10 @@ public: SPObject *getObjectById(char const *id) const; sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot); + std::vector<SPObject *> getObjectsByClass(Glib::ustring const &klass) const; + std::vector<SPObject *> getObjectsByElement(Glib::ustring const &element) const; + std::vector<SPObject *> getObjectsBySelector(Glib::ustring const &selector) const; + void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object); SPObject *getObjectByRepr(Inkscape::XML::Node *repr) const; diff --git a/src/extension/dbus/builddocs.sh b/src/extension/dbus/builddocs.sh index 2c16daf41..2c16daf41 100755..100644 --- a/src/extension/dbus/builddocs.sh +++ b/src/extension/dbus/builddocs.sh diff --git a/src/extension/dbus/document-interface.cpp b/src/extension/dbus/document-interface.cpp index d3a06b81c..435e347d0 100644 --- a/src/extension/dbus/document-interface.cpp +++ b/src/extension/dbus/document-interface.cpp @@ -606,11 +606,7 @@ document_interface_document_set_display_area (DocumentInterface *doc_interface, { SPDesktop *desk = doc_interface->target.getDesktop(); g_return_val_if_fail(ensure_desktop_valid(desk, error), FALSE); - desk->set_display_area (x0, - y0, - x1, - y1, - border, false); + desk->set_display_area (Geom::Rect( Geom::Point(x0,y0), Geom::Point(x1,y1)), border, false ); return TRUE; } diff --git a/src/extension/extension.cpp b/src/extension/extension.cpp index c6dee1b70..6c6e2d71e 100644 --- a/src/extension/extension.cpp +++ b/src/extension/extension.cpp @@ -1,6 +1,6 @@ /** \file * - * Inkscape::Extension::Extension: + * Inkscape::Extension::Extension: * the ability to have features that are more modular so that they * can be added and removed easily. This is the basis for defining * those actions. @@ -144,15 +144,15 @@ Extension::~Extension (void) delete timer; timer = NULL; /** \todo Need to do parameters here */ - - // delete parameters: + + // delete parameters: for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) { Parameter * param = reinterpret_cast<Parameter *>(list->data); delete param; } g_slist_free(parameters); - - + + for (unsigned int i = 0 ; i < _deps.size(); i++) { delete _deps[i]; } @@ -330,7 +330,7 @@ Extension::get_repr (void) } /** - \return bool + \return bool \brief Whether this extension should hide the "working, please wait" dialog */ bool @@ -444,7 +444,7 @@ Extension::get_param_enum (const gchar * name, const SPDocument * doc, const Ink /** * This is useful to find out, if a given string \c value is selectable in a ComboBox named \cname. - * + * * @param name The name of the enum parameter to get. * @param doc The document to look in for document specific parameters. * @param node The node to look in for a specific parameter. @@ -692,9 +692,14 @@ public: * @param widg Widget to add. * @param tooltip Tooltip for the widget. */ - void addWidget(Gtk::Widget *widg, gchar const *tooltip) { + void addWidget(Gtk::Widget *widg, gchar const *tooltip, int indent) { if (widg) { - this->pack_start(*widg, false, false, 2); +#if GTK_CHECK_VERSION(3,12,0) + widg->set_margin_start(indent * Parameter::GUI_INDENTATION); +#else + widg->set_margin_left(indent * Parameter::GUI_INDENTATION); +#endif + this->pack_start(*widg, false, false, 0); if (tooltip) { widg->set_tooltip_text(_(tooltip)); } else { @@ -713,7 +718,7 @@ public: a Gtk::VBox, which is then returned to the calling function. If there are no visible parameters, this function just returns NULL. - If all parameters are gui_visible = false NULL is returned as well. + If all parameters are gui_visible = false NULL is returned as well. */ Gtk::Widget * Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal) @@ -721,6 +726,8 @@ Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<v if (!_gui || param_visible_count() == 0) return NULL; AutoGUI * agui = Gtk::manage(new AutoGUI()); + agui->set_border_width(Parameter::GUI_BOX_MARGIN); + agui->set_spacing(Parameter::GUI_BOX_SPACING); //go through the list of parameters to see if there are any non-hidden ones for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) { @@ -728,9 +735,10 @@ Extension::autogui (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<v if (param->get_gui_hidden()) continue; //Ignore hidden parameters Gtk::Widget * widg = param->get_widget(doc, node, changeSignal); gchar const * tip = param->get_tooltip(); - agui->addWidget(widg, tip); - } - + int indent = param->get_indent(); + agui->addWidget(widg, tip, indent); + } + agui->show(); return agui; }; @@ -780,7 +788,7 @@ void Extension::add_val(Glib::ustring labelstr, Glib::ustring valuestr, Gtk::Gri Gtk::Label * label; Gtk::Label * value; - (*row)++; + (*row)++; label = Gtk::manage(new Gtk::Label(labelstr)); value = Gtk::manage(new Gtk::Label(valuestr)); @@ -824,13 +832,13 @@ Extension::get_params_widget(void) return retval; } -unsigned int Extension::param_visible_count ( ) +unsigned int Extension::param_visible_count ( ) { unsigned int _visible_count = 0; for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) { Parameter * param = reinterpret_cast<Parameter *>(list->data); if (!param->get_gui_hidden()) _visible_count++; - } + } return _visible_count; } diff --git a/src/extension/extension.h b/src/extension/extension.h index cd29e1636..bbd6d068b 100644 --- a/src/extension/extension.h +++ b/src/extension/extension.h @@ -176,7 +176,7 @@ public: private: void make_param (Inkscape::XML::Node * paramrepr); - + /** * This function looks through the linked list for a parameter * structure with the name of the passed in name. diff --git a/src/extension/find_extension_by_mime.h b/src/extension/find_extension_by_mime.h new file mode 100644 index 000000000..1686c62e4 --- /dev/null +++ b/src/extension/find_extension_by_mime.h @@ -0,0 +1,36 @@ +/** + * @file + * Find an extension by its mime type. + */ +/* Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Frank Felfe <innerspace@iname.com> + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * Abhishek Sharma + * Kris De Gussem <Kris.DeGussem@gmail.com> + * + * Copyright (C) 2012 Kris De Gussem + * Copyright (C) 2010 authors + * Copyright (C) 1999-2005 authors + * Copyright (C) 2004 David Turner + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +namespace Inkscape { +namespace Extension { +static inline Inkscape::Extension::Extension *find_by_mime(const char *const mime) +{ + + 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; + } + return *i; +} +} +} diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index d2319c2e0..b1058d415 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -963,6 +963,9 @@ void Script::checkStderr (const Glib::ustring &data, warning.run(); + delete textview; + delete scrollwindow; + return; } diff --git a/src/extension/internal/bitmap/imagemagick.cpp b/src/extension/internal/bitmap/imagemagick.cpp index 472c2db91..cc5b3d1bc 100644 --- a/src/extension/internal/bitmap/imagemagick.cpp +++ b/src/extension/internal/bitmap/imagemagick.cpp @@ -213,6 +213,7 @@ ImageMagick::effect (Inkscape::Extension::Effect *module, Inkscape::UI::View::Vi dc->_nodes[i]->setAttribute("xlink:href", dc->_caches[i], true); dc->_nodes[i]->setAttribute("sodipodi:absref", NULL, true); + delete blob; } catch (Magick::Exception &error_) { printf("Caught exception: %s \n", error_.what()); diff --git a/src/extension/internal/cdr-input.cpp b/src/extension/internal/cdr-input.cpp index 0435f1396..70bf84ef4 100644 --- a/src/extension/internal/cdr-input.cpp +++ b/src/extension/internal/cdr-input.cpp @@ -41,6 +41,7 @@ #endif #include <gtkmm/alignment.h> +#include <gtkmm/grid.h> #include <gtkmm/spinbutton.h> #include "extension/system.h" @@ -83,6 +84,8 @@ private: class Gtk::Widget * _previewArea; class Gtk::Button * cancelbutton; class Gtk::Button * okbutton; + + class Gtk::HBox * _page_selector_box; class Gtk::Label * _labelSelect; class Gtk::Label * _labelTotalPages; class Gtk::SpinButton * _pageNumberSpin; @@ -114,36 +117,38 @@ CdrImportDialog::CdrImportDialog(const std::vector<RVNGString> &vec) this->get_content_area()->pack_start(*vbox1); // CONTROLS + _page_selector_box = Gtk::manage(new Gtk::HBox()); - // Buttons - cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); - okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); - - // Labels + // "Select page:" label _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:"))); _labelTotalPages = Gtk::manage(new class Gtk::Label()); _labelSelect->set_line_wrap(false); _labelSelect->set_use_markup(false); _labelSelect->set_selectable(false); + _page_selector_box->pack_start(*_labelSelect, Gtk::PACK_SHRINK); + + // Adjustment + spinner + auto pageNumberSpin_adj = Gtk::Adjustment::create(1, 1, _vec.size(), 1, 10, 0); + _pageNumberSpin = Gtk::manage(new Gtk::SpinButton(pageNumberSpin_adj, 1, 0)); + _pageNumberSpin->set_can_focus(); + _pageNumberSpin->set_update_policy(Gtk::UPDATE_ALWAYS); + _pageNumberSpin->set_numeric(true); + _pageNumberSpin->set_wrap(false); + _page_selector_box->pack_start(*_pageNumberSpin, Gtk::PACK_SHRINK); + _labelTotalPages->set_line_wrap(false); _labelTotalPages->set_use_markup(false); _labelTotalPages->set_selectable(false); gchar *label_text = g_strdup_printf(_("out of %i"), num_pages); _labelTotalPages->set_label(label_text); g_free(label_text); + _page_selector_box->pack_start(*_labelTotalPages, Gtk::PACK_SHRINK); - // Adjustment + spinner - auto _pageNumberSpin_adj = Gtk::Adjustment::create(1, 1, _vec.size(), 1, 10, 0); - _pageNumberSpin = Gtk::manage(new Gtk::SpinButton(_pageNumberSpin_adj, 1, 0)); - _pageNumberSpin->set_can_focus(); - _pageNumberSpin->set_update_policy(Gtk::UPDATE_ALWAYS); - _pageNumberSpin->set_numeric(true); - _pageNumberSpin->set_wrap(false); + vbox1->pack_end(*_page_selector_box, Gtk::PACK_SHRINK); - this->get_action_area()->property_layout_style().set_value(Gtk::BUTTONBOX_END); - this->get_action_area()->add(*_labelSelect); - this->add_action_widget(*_pageNumberSpin, Gtk::RESPONSE_ACCEPT); - this->get_action_area()->add(*_labelTotalPages); + // Buttons + cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); + okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); this->add_action_widget(*cancelbutton, Gtk::RESPONSE_CANCEL); this->add_action_widget(*okbutton, Gtk::RESPONSE_OK); @@ -218,11 +223,13 @@ SPDocument *CdrInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows // therefore attempt to convert uri to the system codepage // even if this is not possible the alternate short (8.3) file name will be used if available - uri = g_win32_locale_filename_from_utf8(uri); + gchar * converted_uri = g_win32_locale_filename_from_utf8(uri); + RVNGFileStream input(converted_uri); + g_free(converted_uri); + #else + RVNGFileStream input(uri); #endif - RVNGFileStream input(uri); - if (!libcdr::CDRDocument::isSupported(&input)) { return NULL; } diff --git a/src/extension/internal/filter/color.h b/src/extension/internal/filter/color.h index a6b777d20..6324dafa9 100644 --- a/src/extension/internal/filter/color.h +++ b/src/extension/internal/filter/color.h @@ -1480,7 +1480,7 @@ public: static void init (void) { Inkscape::Extension::build_from_mem( "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n" - "<name>" N_("Quadritone fantasy") "</name>\n" + "<name>" N_("Quadritone Fantasy") "</name>\n" "<id>org.inkscape.effect.filter.Quadritone</id>\n" "<param name=\"dist\" _gui-text=\"" N_("Hue distribution (°)") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">280</param>\n" "<param name=\"colors\" _gui-text=\"" N_("Colors") "\" type=\"int\" appearance=\"full\" min=\"0\" max=\"360\">100</param>\n" diff --git a/src/extension/internal/filter/protrusions.h b/src/extension/internal/filter/protrusions.h index ad75d8896..d6fd315db 100644 --- a/src/extension/internal/filter/protrusions.h +++ b/src/extension/internal/filter/protrusions.h @@ -45,7 +45,7 @@ public: static void init (void) { Inkscape::Extension::build_from_mem( "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n" - "<name>" N_("Snow crest") "</name>\n" + "<name>" N_("Snow Crest") "</name>\n" "<id>org.inkscape.effect.filter.snow</id>\n" "<param name=\"drift\" _gui-text=\"" N_("Drift Size") "\" type=\"float\" appearance=\"full\" min=\"0.0\" max=\"20.0\">3.5</param>\n" "<effect>\n" diff --git a/src/extension/internal/metafile-print.cpp b/src/extension/internal/metafile-print.cpp index 061eb634d..11567ceb3 100644 --- a/src/extension/internal/metafile-print.cpp +++ b/src/extension/internal/metafile-print.cpp @@ -355,6 +355,27 @@ int PrintMetafile::hold_gradient(void *gr, int mode) return 1; } +/* behaves like snprintf() but makes sure decimal separators are formatted as dots + otherwise invalid strings can be produced depending on the set locale +*/ +int PrintMetafile::snprintf_dots(char * s, size_t n, const char * format, ...) +{ + int nChars; + + char *oldlocale = g_strdup(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "C"); + + va_list argptr; + va_start(argptr, format); + nChars = vsnprintf(s, n, format, argptr); + va_end(argptr); + + setlocale(LC_NUMERIC, oldlocale); + g_free(oldlocale); + + return nChars; +} + /* convert from center ellipse to SVGEllipticalArc ellipse From: @@ -390,7 +411,8 @@ Geom::PathVector PrintMetafile::center_ellipse_as_SVG_PathV(Geom::Point ctr, dou y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI); char text[256]; - sprintf(text, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1); + snprintf_dots(text, 256, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", + x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1); Geom::PathVector outres = Geom::parse_svg_path(text); return outres; } @@ -418,9 +440,9 @@ Geom::PathVector PrintMetafile::center_elliptical_ring_as_SVG_PathV(Geom::Point y22 = ctr[Y] + sin(F) * rx2 * cos(M_PI) + cos(F) * ry2 * sin(M_PI); char text[512]; - sprintf(text, " M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", - x11, y11, rx1, ry1, degrot, x12, y12, rx1, ry1, degrot, x11, y11, - x21, y21, rx2, ry2, degrot, x22, y22, rx2, ry2, degrot, x21, y21); + snprintf_dots(text, 512, " M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", + x11, y11, rx1, ry1, degrot, x12, y12, rx1, ry1, degrot, x11, y11, + x21, y21, rx2, ry2, degrot, x22, y22, rx2, ry2, degrot, x21, y21); Geom::PathVector outres = Geom::parse_svg_path(text); return outres; @@ -440,8 +462,8 @@ Geom::PathVector PrintMetafile::center_elliptical_hole_as_SVG_PathV(Geom::Point y2 = ctr[Y] + sin(F) * rx * cos(M_PI) + cos(F) * ry * sin(M_PI); char text[256]; - sprintf(text, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z", - x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1); + snprintf_dots(text, 256, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z", + x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1); Geom::PathVector outres = Geom::parse_svg_path(text); return outres; } diff --git a/src/extension/internal/metafile-print.h b/src/extension/internal/metafile-print.h index d184b72b7..9903cbb98 100644 --- a/src/extension/internal/metafile-print.h +++ b/src/extension/internal/metafile-print.h @@ -101,6 +101,7 @@ protected: static void swapRBinRGBA(char *px, int pixels); int hold_gradient(void *gr, int mode); + static int snprintf_dots(char * s, size_t n, const char * format, ...); static Geom::PathVector center_ellipse_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F); static Geom::PathVector center_elliptical_ring_as_SVG_PathV(Geom::Point ctr, double rx1, double ry1, double rx2, double ry2, double F); static Geom::PathVector center_elliptical_hole_as_SVG_PathV(Geom::Point ctr, double rx, double ry, double F); diff --git a/src/extension/internal/vsd-input.cpp b/src/extension/internal/vsd-input.cpp index 78783aa2d..b7277b99e 100644 --- a/src/extension/internal/vsd-input.cpp +++ b/src/extension/internal/vsd-input.cpp @@ -84,6 +84,8 @@ private: class Gtk::Widget * _previewArea; class Gtk::Button * cancelbutton; class Gtk::Button * okbutton; + + class Gtk::HBox * _page_selector_box; class Gtk::Label * _labelSelect; class Gtk::Label * _labelTotalPages; class Gtk::SpinButton * _pageNumberSpin; @@ -116,10 +118,7 @@ VsdImportDialog::VsdImportDialog(const std::vector<RVNGString> &vec) this->get_content_area()->pack_start(*vbox1); // CONTROLS - - // Buttons - cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); - okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); + _page_selector_box = Gtk::manage(new Gtk::HBox()); // Labels _labelSelect = Gtk::manage(new class Gtk::Label(_("Select page:"))); @@ -127,12 +126,7 @@ VsdImportDialog::VsdImportDialog(const std::vector<RVNGString> &vec) _labelSelect->set_line_wrap(false); _labelSelect->set_use_markup(false); _labelSelect->set_selectable(false); - _labelTotalPages->set_line_wrap(false); - _labelTotalPages->set_use_markup(false); - _labelTotalPages->set_selectable(false); - gchar *label_text = g_strdup_printf(_("out of %i"), num_pages); - _labelTotalPages->set_label(label_text); - g_free(label_text); + _page_selector_box->pack_start(*_labelSelect, Gtk::PACK_SHRINK); // Adjustment + spinner auto _pageNumberSpin_adj = Gtk::Adjustment::create(1, 1, _vec.size(), 1, 10, 0); @@ -141,11 +135,21 @@ VsdImportDialog::VsdImportDialog(const std::vector<RVNGString> &vec) _pageNumberSpin->set_update_policy(Gtk::UPDATE_ALWAYS); _pageNumberSpin->set_numeric(true); _pageNumberSpin->set_wrap(false); + _page_selector_box->pack_start(*_pageNumberSpin, Gtk::PACK_SHRINK); - this->get_action_area()->property_layout_style().set_value(Gtk::BUTTONBOX_END); - this->get_action_area()->add(*_labelSelect); - this->add_action_widget(*_pageNumberSpin, Gtk::RESPONSE_ACCEPT); - this->get_action_area()->add(*_labelTotalPages); + _labelTotalPages->set_line_wrap(false); + _labelTotalPages->set_use_markup(false); + _labelTotalPages->set_selectable(false); + gchar *label_text = g_strdup_printf(_("out of %i"), num_pages); + _labelTotalPages->set_label(label_text); + g_free(label_text); + _page_selector_box->pack_start(*_labelTotalPages, Gtk::PACK_SHRINK); + + vbox1->pack_end(*_page_selector_box, Gtk::PACK_SHRINK); + + // Buttons + cancelbutton = Gtk::manage(new Gtk::Button(_("_Cancel"), true)); + okbutton = Gtk::manage(new Gtk::Button(_("_OK"), true)); this->add_action_widget(*cancelbutton, Gtk::RESPONSE_CANCEL); this->add_action_widget(*okbutton, Gtk::RESPONSE_OK); @@ -220,11 +224,13 @@ SPDocument *VsdInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows // therefore attempt to convert uri to the system codepage // even if this is not possible the alternate short (8.3) file name will be used if available - uri = g_win32_locale_filename_from_utf8(uri); + gchar * converted_uri = g_win32_locale_filename_from_utf8(uri); + RVNGFileStream input(converted_uri); + g_free(converted_uri); + #else + RVNGFileStream input(uri); #endif - RVNGFileStream input(uri); - if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index a79af5ec1..42ec2f2bb 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -1765,11 +1765,20 @@ std::cout << "BEFORE DRAW" << " test0 " << ( d->mask & U_DRAW_VISIBLE) << " test1 " << ( d->mask & U_DRAW_FORCE) << " test2 " << (wmr_mask & U_DRAW_ALTERS) + << " test2.5 " << ((d->mask & U_DRAW_NOFILL) != (wmr_mask & U_DRAW_NOFILL) ) << " test3 " << (wmr_mask & U_DRAW_VISIBLE) << " test4 " << !(d->mask & U_DRAW_ONLYTO) << " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(wmr_mask & U_DRAW_ONLYTO) ) << std::endl; */ + /* spurious moveto records should not affect the drawing. However, they set the NOFILL + bit and that messes up the logic about when to emit a path. So prune out any + stray moveto records. That is those which were never followed by a lineto. + */ + if((d->mask & U_DRAW_NOFILL) && !(d->mask & U_DRAW_VISIBLE) && + !(wmr_mask & U_DRAW_ONLYTO) && (wmr_mask & U_DRAW_VISIBLE)){ + d->mask ^= U_DRAW_NOFILL; + } if( (wmr_mask != U_WMR_INVALID) && // next record is valid type @@ -1777,6 +1786,7 @@ std::cout << "BEFORE DRAW" ( (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH (wmr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way + ((d->mask & U_DRAW_NOFILL) != (wmr_mask & U_DRAW_NOFILL)) || // Fill<->!Fill requires a draw between ( (wmr_mask & U_DRAW_VISIBLE) && // Next record is visible... ( ( !(d->mask & U_DRAW_ONLYTO) ) || // Non *TO records cannot be followed by any Visible diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp index 3d913bf1e..6dd9d895e 100644 --- a/src/extension/internal/wmf-print.cpp +++ b/src/extension/internal/wmf-print.cpp @@ -598,7 +598,28 @@ int PrintWmf::create_pen(SPStyle const *style, const Geom::Affine &transform) if (!style->stroke_dasharray.values.empty()) { if (!FixPPTDashLine) { // if this is set code elsewhere will break dots/dashes into many smaller lines. - penstyle = U_PS_DASH;// userstyle not supported apparently, for now map all Inkscape dot/dash to just dash + int n_dash = style->stroke_dasharray.values.size(); + /* options are dash, dot, dashdot and dashdotdot. Try to pick the closest one. */ + int mark_short=INT_MAX; + int mark_long =0; + int i; + for (i=0;i<n_dash;i++) { + int mark = style->stroke_dasharray.values[i]; + if (mark>mark_long) { mark_long = mark; } + if (mark<mark_short) { mark_short = mark; } + } + if(mark_long == mark_short){ // only one mark size + penstyle = U_PS_DOT; + } + else if (n_dash==2) { + penstyle = U_PS_DASH; + } + else if (n_dash==4) { + penstyle = U_PS_DASHDOT; + } + else { + penstyle = U_PS_DASHDOTDOT; + } } } diff --git a/src/extension/internal/wpg-input.cpp b/src/extension/internal/wpg-input.cpp index 12457791b..299614c94 100644 --- a/src/extension/internal/wpg-input.cpp +++ b/src/extension/internal/wpg-input.cpp @@ -85,10 +85,13 @@ SPDocument *WpgInput::open(Inkscape::Extension::Input * /*mod*/, const gchar * u // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows // therefore attempt to convert uri to the system codepage // even if this is not possible the alternate short (8.3) file name will be used if available - uri = g_win32_locale_filename_from_utf8(uri); + gchar * converted_uri = g_win32_locale_filename_from_utf8(uri); + RVNGInputStream* input = new RVNGFileStream(converted_uri); + g_free(converted_uri); + #else + RVNGInputStream* input = new RVNGFileStream(uri); #endif - RVNGInputStream* input = new RVNGFileStream(uri); #if WITH_LIBWPG03 if (input->isStructured()) { RVNGInputStream* olestream = input->getSubStreamByName("PerfectOffice_MAIN"); diff --git a/src/extension/param/bool.cpp b/src/extension/param/bool.cpp index ca61d8c51..9f8d3dcc0 100644 --- a/src/extension/param/bool.cpp +++ b/src/extension/param/bool.cpp @@ -24,9 +24,17 @@ namespace Inkscape { namespace Extension { -ParamBool::ParamBool(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _value(false), _indent(0) +ParamBool::ParamBool(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _value(false) { const char * defaultval = NULL; if (xml->firstChild() != NULL) { @@ -39,11 +47,6 @@ ParamBool::ParamBool(const gchar * name, const gchar * guitext, const gchar * de _value = false; } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; - } - gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); _value = prefs->getBool(extension_pref_root + pref_name, _value); @@ -66,7 +69,7 @@ bool ParamBool::set( bool in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node bool ParamBool::get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { - return _value; + return _value; } /** @@ -130,7 +133,7 @@ Gtk::Widget *ParamBool::get_widget(SPDocument * doc, Inkscape::XML::Node * node, return NULL; } - auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4)); + auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, Parameter::GUI_PARAM_WIDGETS_SPACING)); hbox->set_homogeneous(false); Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START)); @@ -139,7 +142,7 @@ Gtk::Widget *ParamBool::get_widget(SPDocument * doc, Inkscape::XML::Node * node, ParamBoolCheckButton * checkbox = Gtk::manage(new ParamBoolCheckButton(this, doc, node, changeSignal)); checkbox->show(); - hbox->pack_start(*checkbox, false, false, _indent); + hbox->pack_start(*checkbox, false, false); hbox->show(); diff --git a/src/extension/param/bool.h b/src/extension/param/bool.h index 5d190b9d3..0af12b105 100644 --- a/src/extension/param/bool.h +++ b/src/extension/param/bool.h @@ -32,7 +32,15 @@ public: /** * Use the superclass' allocator and set the \c _value. */ - ParamBool(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + ParamBool(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml); /** * Returns the current state/value. @@ -69,7 +77,6 @@ public: private: /** Internal value. */ bool _value; - int _indent; }; } // namespace Extension diff --git a/src/extension/param/color.cpp b/src/extension/param/color.cpp index 0b58c5011..fd88e9adb 100644 --- a/src/extension/param/color.cpp +++ b/src/extension/param/color.cpp @@ -52,9 +52,17 @@ guint32 ParamColor::set( guint32 in, SPDocument * /*doc*/, Inkscape::XML::Node * return in; } -ParamColor::ParamColor (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _changeSignal(0) +ParamColor::ParamColor(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _changeSignal(0) { const char * defaulthex = NULL; if (xml->firstChild() != NULL) @@ -86,7 +94,7 @@ Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * { using Inkscape::UI::Widget::ColorNotebook; - if (_gui_hidden) return NULL; + if (_gui_hidden) return NULL; if (changeSignal) { _changeSignal = new sigc::signal<void>(*changeSignal); @@ -98,9 +106,9 @@ Gtk::Widget *ParamColor::get_widget( SPDocument * /*doc*/, Inkscape::XML::Node * _color_changed.block(false); } - Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, 4)); + Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING)); Gtk::Widget *selector = Gtk::manage(new ColorNotebook(_color)); - hbox->pack_start (*selector, true, true, 0); + hbox->pack_start(*selector, true, true, 0); selector->show(); hbox->show(); return hbox; diff --git a/src/extension/param/color.h b/src/extension/param/color.h index ed2e57ceb..d6e9d1fbe 100644 --- a/src/extension/param/color.h +++ b/src/extension/param/color.h @@ -31,8 +31,15 @@ private: Inkscape::UI::SelectedColor _color; sigc::connection _color_changed; public: - ParamColor(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); - + ParamColor(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml); virtual ~ParamColor(void); /** Returns \c _value, with a \i const to protect it. */ diff --git a/src/extension/param/description.cpp b/src/extension/param/description.cpp index 07aaa07cc..3c29b7c49 100644 --- a/src/extension/param/description.cpp +++ b/src/extension/param/description.cpp @@ -16,6 +16,7 @@ #include <gtkmm/box.h> #include <gtkmm/label.h> #include <glibmm/i18n.h> +#include <glibmm/markup.h> #include "xml/node.h" #include "extension/extension.h" @@ -26,17 +27,19 @@ namespace Extension { /** \brief Initialize the object, to do that, copy the data. */ -ParamDescription::ParamDescription (const gchar * name, - const gchar * guitext, - const gchar * desc, - const Parameter::_scope_t scope, - bool gui_hidden, - const gchar * gui_tip, - Inkscape::Extension::Extension * ext, - Inkscape::XML::Node * xml, - AppearanceMode mode) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _value(NULL), _mode(mode), _indent(0) +ParamDescription::ParamDescription(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml, + AppearanceMode mode) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _value(NULL) + , _mode(mode) { // printf("Building Description\n"); const char * defaultval = NULL; @@ -50,11 +53,6 @@ ParamDescription::ParamDescription (const gchar * name, _context = xml->attribute("msgctxt"); - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; - } - return; } @@ -76,23 +74,40 @@ ParamDescription::get_widget (SPDocument * /*doc*/, Inkscape::XML::Node * /*node } else { newguitext = _(_value); } - - Gtk::Label * label; - int padding = 12 + _indent; + + Gtk::Label * label = Gtk::manage(new Gtk::Label()); if (_mode == HEADER) { - label = Gtk::manage(new Gtk::Label(Glib::ustring("<b>") +newguitext + Glib::ustring("</b>"), Gtk::ALIGN_START)); - label->set_margin_top(5); - label->set_margin_bottom(5); - label->set_use_markup(true); - padding = _indent; + label->set_markup(Glib::ustring("<b>") + Glib::Markup::escape_text(newguitext) + Glib::ustring("</b>")); + label->set_margin_top(5); + label->set_margin_bottom(5); + } else if (_mode == URL) { + Glib::ustring escaped_url = Glib::Markup::escape_text(newguitext); + label->set_markup(Glib::ustring::compose("<a href='%1'>%1</a>", escaped_url)); } else { - label = Gtk::manage(new Gtk::Label(newguitext, Gtk::ALIGN_START)); + label->set_text(newguitext); } label->set_line_wrap(); +#if (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION >= 16) + label->set_xalign(0); +#else + label->set_alignment(Gtk::ALIGN_START); +#endif + + // TODO: Ugly "fix" for gtk3 width/height calculation of labels. + // - If not applying any limits long labels will make the window grow horizontally until it uses up + // most of the available space (i.e. most of the screen area) which is ridicously wide + // - By using "set_default_size(0,0)" in prefidalog.cpp we tell the window to shrink as much as possible, + // however this can result in a much to narrow dialog instead and much unnecessary wrapping + // - Here we set a lower limit of GUI_MAX_LINE_LENGTH characters per line that long texts will always use + // This means texts can not shrink anymore (they can still grow, though) and it's also necessary + // to prevent https://bugzilla.gnome.org/show_bug.cgi?id=773572 + int len = newguitext.length(); + label->set_width_chars(len > Parameter::GUI_MAX_LINE_LENGTH ? Parameter::GUI_MAX_LINE_LENGTH : len); + label->show(); - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); - hbox->pack_start(*label, true, true, padding); + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox()); + hbox->pack_start(*label, true, true); hbox->show(); return hbox; diff --git a/src/extension/param/description.h b/src/extension/param/description.h index 3a72c4112..c6c5f4013 100644 --- a/src/extension/param/description.h +++ b/src/extension/param/description.h @@ -27,23 +27,24 @@ namespace Extension { class ParamDescription : public Parameter { public: enum AppearanceMode { - DESC, HEADER + DESC, HEADER, URL }; ParamDescription(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, - const gchar * gui_tip, + const gchar * gui_tip, + int indent, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml, AppearanceMode mode); + Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal); private: /** \brief Internal value. */ gchar * _value; AppearanceMode _mode; - int _indent; const gchar* _context; }; diff --git a/src/extension/param/enum.cpp b/src/extension/param/enum.cpp index 8bc0fbda7..a26458c35 100644 --- a/src/extension/param/enum.cpp +++ b/src/extension/param/enum.cpp @@ -46,12 +46,17 @@ public: }; -ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gchar *desc, - const Parameter::_scope_t scope, bool gui_hidden, const gchar *gui_tip, - Inkscape::Extension::Extension *ext, Inkscape::XML::Node *xml) - : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext) +ParamComboBox::ParamComboBox(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) , _value(NULL) - , _indent(0) , choices(NULL) { const char *xmlval = NULL; // the value stored in XML @@ -94,17 +99,12 @@ ParamComboBox::ParamComboBox(const gchar *name, const gchar *guitext, const gcha } } } - + // Initialize _value with the default value from xml // for simplicity : default to the contents of the first xml-child if (xml->firstChild() && xml->firstChild()->firstChild()) { xmlval = xml->firstChild()->attribute("value"); } - - const char *indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; - } } gchar * pref_name = this->pref_name(); @@ -250,10 +250,10 @@ Gtk::Widget *ParamComboBox::get_widget(SPDocument * doc, Inkscape::XML::Node * n return NULL; } - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING)); Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START)); label->show(); - hbox->pack_start(*label, false, false, _indent); + hbox->pack_start(*label, false, false); ParamComboBoxEntry * combo = Gtk::manage(new ParamComboBoxEntry(this, doc, node, changeSignal)); // add choice strings: diff --git a/src/extension/param/enum.h b/src/extension/param/enum.h index 52e018469..f9d3d75f7 100644 --- a/src/extension/param/enum.h +++ b/src/extension/param/enum.h @@ -34,12 +34,20 @@ private: been allocated in memory. And should be free'd. It is the value of the current selected string */ gchar * _value; - int _indent; GSList * choices; /**< A table to store the choice strings */ public: - ParamComboBox(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + ParamComboBox(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml); virtual ~ParamComboBox(void); + Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal); // Explicitly call superclass version to avoid method being hidden. diff --git a/src/extension/param/float.cpp b/src/extension/param/float.cpp index 23a03ea8f..e3fdba826 100644 --- a/src/extension/param/float.cpp +++ b/src/extension/param/float.cpp @@ -27,17 +27,21 @@ namespace Extension { /** Use the superclass' allocator and set the \c _value. */ -ParamFloat::ParamFloat (const gchar * name, - const gchar * guitext, - const gchar * desc, - const Parameter::_scope_t scope, - bool gui_hidden, - const gchar * gui_tip, - Inkscape::Extension::Extension * ext, - Inkscape::XML::Node * xml, - AppearanceMode mode) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _value(0.0), _mode(mode), _indent(0), _min(0.0), _max(10.0) +ParamFloat::ParamFloat(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml, + AppearanceMode mode) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _value(0.0) + , _mode(mode) + , _min(0.0) + , _max(10.0) { const gchar * defaultval = NULL; if (xml->firstChild() != NULL) { @@ -69,11 +73,6 @@ ParamFloat::ParamFloat (const gchar * name, _min = 0.0; } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; - } - gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); _value = prefs->getDouble(extension_pref_root + pref_name, _value); @@ -175,24 +174,24 @@ Gtk::Widget * ParamFloat::get_widget(SPDocument * doc, Inkscape::XML::Node * nod return NULL; } - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING)); auto pfa = new ParamFloatAdjustment(this, doc, node, changeSignal); Glib::RefPtr<Gtk::Adjustment> fadjust(pfa); - + if (_mode == FULL) { UI::Widget::SpinScale *scale = new UI::Widget::SpinScale(_(_text), fadjust, _precision); scale->set_size_request(400, -1); scale->show(); - hbox->pack_start(*scale, false, false); + hbox->pack_start(*scale, true, true); } else if (_mode == MINIMAL) { Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START)); label->show(); - hbox->pack_start(*label, true, true, _indent); + hbox->pack_start(*label, true, true); auto spin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(fadjust, 0.1, _precision)); spin->show(); diff --git a/src/extension/param/float.h b/src/extension/param/float.h index 42b1698b1..7fd86cea7 100644 --- a/src/extension/param/float.h +++ b/src/extension/param/float.h @@ -29,15 +29,17 @@ public: enum AppearanceMode { FULL, MINIMAL }; - ParamFloat (const gchar * name, - const gchar * guitext, - const gchar * desc, - const Parameter::_scope_t scope, - bool gui_hidden, - const gchar * gui_tip, - Inkscape::Extension::Extension * ext, - Inkscape::XML::Node * xml, - AppearanceMode mode); + ParamFloat(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml, + AppearanceMode mode); + /** Returns \c _value. */ float get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; } @@ -60,7 +62,6 @@ private: /** Internal value. */ float _value; AppearanceMode _mode; - int _indent; float _min; float _max; int _precision; diff --git a/src/extension/param/int.cpp b/src/extension/param/int.cpp index 222d4f243..538ddc08d 100644 --- a/src/extension/param/int.cpp +++ b/src/extension/param/int.cpp @@ -27,17 +27,21 @@ namespace Extension { /** Use the superclass' allocator and set the \c _value. */ -ParamInt::ParamInt (const gchar * name, - const gchar * guitext, - const gchar * desc, - const Parameter::_scope_t scope, - bool gui_hidden, - const gchar * gui_tip, - Inkscape::Extension::Extension * ext, - Inkscape::XML::Node * xml, - AppearanceMode mode) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _value(0), _mode(mode), _indent(0), _min(0), _max(10) +ParamInt::ParamInt(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml, + AppearanceMode mode) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _value(0) + , _mode(mode) + , _min(0) + , _max(10) { const char * defaultval = NULL; if (xml->firstChild() != NULL) { @@ -62,11 +66,6 @@ ParamInt::ParamInt (const gchar * name, _min = 0; } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; - } - gchar *pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); _value = prefs->getInt(extension_pref_root + pref_name, _value); @@ -156,7 +155,7 @@ ParamInt::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal return NULL; } - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING)); auto pia = new ParamIntAdjustment(this, doc, node, changeSignal); Glib::RefPtr<Gtk::Adjustment> fadjust(pia); @@ -166,13 +165,13 @@ ParamInt::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal UI::Widget::SpinScale *scale = new UI::Widget::SpinScale(_(_text), fadjust, 0); scale->set_size_request(400, -1); scale->show(); - hbox->pack_start(*scale, false, false); + hbox->pack_start(*scale, true, true); } else if (_mode == MINIMAL) { - + Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START)); label->show(); - hbox->pack_start(*label, true, true, _indent); + hbox->pack_start(*label, true, true); auto spin = Gtk::manage(new Inkscape::UI::Widget::SpinButton(fadjust, 1.0, 0)); spin->show(); diff --git a/src/extension/param/int.h b/src/extension/param/int.h index 3fd6cea9b..40c0395ec 100644 --- a/src/extension/param/int.h +++ b/src/extension/param/int.h @@ -29,15 +29,16 @@ public: enum AppearanceMode { FULL, MINIMAL }; - ParamInt (const gchar * name, - const gchar * guitext, - const gchar * desc, - const Parameter::_scope_t scope, - bool gui_hidden, - const gchar * gui_tip, - Inkscape::Extension::Extension * ext, - Inkscape::XML::Node * xml, - AppearanceMode mode); + ParamInt(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml, + AppearanceMode mode); /** Returns \c _value. */ int get(const SPDocument * /*doc*/, const Inkscape::XML::Node * /*node*/) const { return _value; } @@ -59,7 +60,6 @@ private: /** Internal value. */ int _value; AppearanceMode _mode; - int _indent; int _min; int _max; }; diff --git a/src/extension/param/notebook.cpp b/src/extension/param/notebook.cpp index 957d12d06..de86d1005 100644 --- a/src/extension/param/notebook.cpp +++ b/src/extension/param/notebook.cpp @@ -54,8 +54,16 @@ private: public: static ParamNotebookPage * makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext); - ParamNotebookPage(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + ParamNotebookPage(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml); ~ParamNotebookPage(void); + Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal); void paramString (std::list <std::string> &list); gchar * get_guitext (void) {return _text;}; @@ -63,8 +71,15 @@ public: }; /* class ParamNotebookPage */ -ParamNotebookPage::ParamNotebookPage (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext) +ParamNotebookPage::ParamNotebookPage(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, /*indent*/ 0, ext) { parameters = NULL; @@ -199,16 +214,23 @@ Gtk::Widget * ParamNotebookPage::get_widget(SPDocument * doc, Inkscape::XML::Nod } Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox); - vbox->set_border_width(5); + vbox->set_border_width(Parameter::GUI_BOX_MARGIN); + vbox->set_spacing(Parameter::GUI_BOX_SPACING); // add parameters onto page (if any) for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) { Parameter * param = reinterpret_cast<Parameter *>(list->data); Gtk::Widget * widg = param->get_widget(doc, node, changeSignal); if (widg) { + int indent = param->get_indent(); +#if GTK_CHECK_VERSION(3,12,0) + widg->set_margin_start(indent * Parameter::GUI_INDENTATION); +#else + widg->set_margin_left(indent * Parameter::GUI_INDENTATION); +#endif + vbox->pack_start(*widg, false, false, 0); + gchar const * tip = param->get_tooltip(); - // printf("Tip: '%s'\n", tip); - vbox->pack_start(*widg, false, false, 2); if (tip) { widg->set_tooltip_text(_(tip)); } else { @@ -224,8 +246,16 @@ Gtk::Widget * ParamNotebookPage::get_widget(SPDocument * doc, Inkscape::XML::Nod } -ParamNotebook::ParamNotebook (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext) +ParamNotebook::ParamNotebook(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) { pages = NULL; diff --git a/src/extension/param/notebook.h b/src/extension/param/notebook.h index 3c90964fd..278c4de12 100644 --- a/src/extension/param/notebook.h +++ b/src/extension/param/notebook.h @@ -41,8 +41,17 @@ private: This only gets created if there are pages in this notebook */ public: - ParamNotebook(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + ParamNotebook(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml); virtual ~ParamNotebook(void); + Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal); /** diff --git a/src/extension/param/parameter.cpp b/src/extension/param/parameter.cpp index a5632a39a..0eb491078 100644 --- a/src/extension/param/parameter.cpp +++ b/src/extension/param/parameter.cpp @@ -50,10 +50,17 @@ Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Ex const char *name = in_repr->attribute("name"); const char *type = in_repr->attribute("type"); - // In this case we just don't have enough information - if (!name || !type) { + // we can't create a parameter without type + if (!type) { return NULL; } + // also require name unless it's a pure UI element that does not store its value + if (!name) { + static std::vector<std::string> ui_elements = {"description"}; + if (std::find(ui_elements.begin(), ui_elements.end(), type) == ui_elements.end()) { + return NULL; + } + } const char *guitext = in_repr->attribute("gui-text"); if (guitext == NULL) { @@ -78,6 +85,17 @@ Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Ex /* else stays false */ } } + int indent = 0; + { + const char *indent_attr = in_repr->attribute("indent"); + if (indent_attr != NULL) { + if (strcmp(indent_attr, "true") == 0) { + indent = 1; + } else { + indent = atoi(indent_attr); + } + } + } const gchar* appearance = in_repr->attribute("appearance"); Parameter::_scope_t scope = Parameter::SCOPE_USER; @@ -96,44 +114,48 @@ Parameter *Parameter::make(Inkscape::XML::Node *in_repr, Inkscape::Extension::Ex Parameter * param = NULL; if (!strcmp(type, "boolean")) { - param = new ParamBool(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr); + param = new ParamBool(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr); } else if (!strcmp(type, "int")) { if (appearance && !strcmp(appearance, "full")) { - param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamInt::FULL); + param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamInt::FULL); } else { - param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamInt::MINIMAL); + param = new ParamInt(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamInt::MINIMAL); } } else if (!strcmp(type, "float")) { if (appearance && !strcmp(appearance, "full")) { - param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamFloat::FULL); + param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamFloat::FULL); } else { - param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamFloat::MINIMAL); + param = new ParamFloat(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamFloat::MINIMAL); } } else if (!strcmp(type, "string")) { - param = new ParamString(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr); + param = new ParamString(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr); gchar const * max_length = in_repr->attribute("max_length"); if (max_length != NULL) { ParamString * ps = dynamic_cast<ParamString *>(param); ps->setMaxLength(atoi(max_length)); } } else if (!strcmp(type, "description")) { - if (appearance && !strcmp(appearance, "header")) { - param = new ParamDescription(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamDescription::HEADER); - } else { - param = new ParamDescription(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamDescription::DESC); - } + ParamDescription::AppearanceMode appearance_mode = ParamDescription::DESC; + if (appearance) { + if (!strcmp(appearance, "header")) { + appearance_mode = ParamDescription::HEADER; + } else if (!strcmp(appearance, "url")) { + appearance_mode = ParamDescription::URL; + } + } + param = new ParamDescription(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, appearance_mode); } else if (!strcmp(type, "enum")) { - param = new ParamComboBox(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr); + param = new ParamComboBox(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr); } else if (!strcmp(type, "notebook")) { - param = new ParamNotebook(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr); + param = new ParamNotebook(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr); } else if (!strcmp(type, "optiongroup")) { if (appearance && !strcmp(appearance, "minimal")) { - param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamRadioButton::MINIMAL); + param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamRadioButton::MINIMAL); } else { - param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr, ParamRadioButton::FULL); + param = new ParamRadioButton(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr, ParamRadioButton::FULL); } } else if (!strcmp(type, "color")) { - param = new ParamColor(name, guitext, desc, scope, gui_hidden, gui_tip, in_ext, in_repr); + param = new ParamColor(name, guitext, desc, scope, gui_hidden, gui_tip, indent, in_ext, in_repr); } // Note: param could equal NULL @@ -280,12 +302,13 @@ Parameter::set_color (guint32 in, SPDocument * doc, Inkscape::XML::Node * node) /** Oop, now that we need a parameter, we need it's name. */ -Parameter::Parameter(gchar const * name, gchar const * guitext, gchar const * desc, const Parameter::_scope_t scope, bool gui_hidden, gchar const * gui_tip, Inkscape::Extension::Extension * ext) : +Parameter::Parameter(gchar const * name, gchar const * guitext, gchar const * desc, const Parameter::_scope_t scope, bool gui_hidden, gchar const * gui_tip, int indent, Inkscape::Extension::Extension * ext) : _desc(0), _scope(scope), _text(0), _gui_hidden(gui_hidden), _gui_tip(0), + _indent(indent), extension(ext), _name(0) { @@ -316,6 +339,7 @@ Parameter::Parameter (gchar const * name, gchar const * guitext, Inkscape::Exten _text(0), _gui_hidden(false), _gui_tip(0), + _indent(0), extension(ext), _name(0) { diff --git a/src/extension/param/parameter.h b/src/extension/param/parameter.h index 5e1e3897f..c107f24f9 100644 --- a/src/extension/param/parameter.h +++ b/src/extension/param/parameter.h @@ -37,7 +37,7 @@ class Extension; */ extern Glib::ustring const extension_pref_root; -/** +/** * A class to represent the parameter of an extension. * * This is really a super class that allows them to abstract all @@ -62,6 +62,7 @@ public: const Parameter::_scope_t scope, bool gui_hidden, gchar const *gui_tip, + int indent, Inkscape::Extension::Extension * ext); Parameter(gchar const *name, @@ -144,6 +145,9 @@ public: /** Indicates if the GUI for this parameter is hidden or not */ bool get_gui_hidden() const { return _gui_hidden; } + /** Indentation level of the parameter */ + int get_indent() const { return _indent; } + virtual void string(std::list <std::string> &list) const; /** @@ -155,6 +159,19 @@ public: /** All the code in Notebook::get_param to get the notebook content. */ virtual Parameter *get_param(gchar const *name); + + /** Recommended margin of boxes containing multiple Parameters (in px) */ + const static int GUI_BOX_MARGIN = 10; + /** Recommended spacing between multiple Parameters packed into a box (in px) */ + const static int GUI_BOX_SPACING = 4; + /** Recommended spacing between the widgets making up a signle Parameter (e.g. label and input) (in px) */ + const static int GUI_PARAM_WIDGETS_SPACING = 4; + /** Recommended indentation width of parameters (in px) */ + const static int GUI_INDENTATION = 12; + /** Recommended maximum line lenght for wrapping textual parameters (in chars) */ + const static int GUI_MAX_LINE_LENGTH = 60; + + protected: /** Description of the parameter. */ gchar * _desc; @@ -171,6 +188,9 @@ protected: /** A tip for the GUI if there is one. */ gchar * _gui_tip; + /** Indentation level of the parameter. */ + int _indent; + /* **** funcs **** */ diff --git a/src/extension/param/radiobutton.cpp b/src/extension/param/radiobutton.cpp index c54cc0ec3..178dc20e8 100644 --- a/src/extension/param/radiobutton.cpp +++ b/src/extension/param/radiobutton.cpp @@ -57,17 +57,20 @@ public: Glib::ustring * guitext; }; -ParamRadioButton::ParamRadioButton (const gchar * name, - const gchar * guitext, - const gchar * desc, - const Parameter::_scope_t scope, - bool gui_hidden, - const gchar * gui_tip, - Inkscape::Extension::Extension * ext, - Inkscape::XML::Node * xml, - AppearanceMode mode) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _value(0), _mode(mode), _indent(0), choices(0) +ParamRadioButton::ParamRadioButton(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml, + AppearanceMode mode) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _value(0) + , _mode(mode) + , choices(0) { // Read XML tree to add enumeration items: // printf("Extension Constructor: "); @@ -118,11 +121,6 @@ ParamRadioButton::ParamRadioButton (const gchar * name, defaultval = (static_cast<optionentry*> (choices->data))->value->c_str(); } - const char *indent = xml ? xml->attribute("indent") : NULL; - if (indent != NULL) { - _indent = atoi(indent) * 12; - } - gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name); @@ -303,14 +301,14 @@ Gtk::Widget * ParamRadioButton::get_widget(SPDocument * doc, Inkscape::XML::Node return NULL; } - auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4)); + auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, Parameter::GUI_PARAM_WIDGETS_SPACING)); hbox->set_homogeneous(false); auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0)); vbox->set_homogeneous(false); Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START, Gtk::ALIGN_START)); label->show(); - hbox->pack_start(*label, false, false, _indent); + hbox->pack_start(*label, false, false); Gtk::ComboBoxText* cbt = 0; bool comboSet = false; diff --git a/src/extension/param/radiobutton.h b/src/extension/param/radiobutton.h index 0e0f643c9..5e15dcb57 100644 --- a/src/extension/param/radiobutton.h +++ b/src/extension/param/radiobutton.h @@ -40,10 +40,12 @@ public: const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, + int indent, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml, AppearanceMode mode); virtual ~ParamRadioButton(void); + Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal); // Explicitly call superclass version to avoid method being hidden. @@ -63,7 +65,6 @@ private: It is the value of the current selected string */ gchar * _value; AppearanceMode _mode; - int _indent; GSList * choices; /**< A table to store the choice strings */ }; /* class ParamRadioButton */ diff --git a/src/extension/param/string.cpp b/src/extension/param/string.cpp index 1d9205502..0c5238f99 100644 --- a/src/extension/param/string.cpp +++ b/src/extension/param/string.cpp @@ -24,7 +24,7 @@ namespace Inkscape { namespace Extension { - + /** Free the allocated data. */ ParamString::~ParamString(void) @@ -76,20 +76,23 @@ void ParamString::string(std::string &string) const } /** Initialize the object, to do that, copy the data. */ -ParamString::ParamString (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) : - Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext), - _value(NULL), _indent(0) +ParamString::ParamString(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml) + : Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, indent, ext) + , _value(NULL) { const char * defaultval = NULL; if (xml->firstChild() != NULL) { defaultval = xml->firstChild()->content(); } - const char * indent = xml->attribute("indent"); - if (indent != NULL) { - _indent = atoi(indent) * 12; - } - gchar * pref_name = this->pref_name(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name); @@ -165,10 +168,10 @@ Gtk::Widget * ParamString::get_widget(SPDocument * doc, Inkscape::XML::Node * no return NULL; } - Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4)); + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, Parameter::GUI_PARAM_WIDGETS_SPACING)); Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_START)); label->show(); - hbox->pack_start(*label, false, false, _indent); + hbox->pack_start(*label, false, false); ParamStringEntry * textbox = new ParamStringEntry(this, doc, node, changeSignal); textbox->show(); diff --git a/src/extension/param/string.h b/src/extension/param/string.h index 3b137aeac..f5412ebcc 100644 --- a/src/extension/param/string.h +++ b/src/extension/param/string.h @@ -19,12 +19,19 @@ private: /** \brief Internal value. This should point to a string that has been allocated in memory. And should be free'd. */ gchar * _value; - /** \brief Internal value. This indicates the maximum leght of the string. Zero meaning unlimited. + /** \brief Internal value. This indicates the maximum lenth of the string. Zero meaning unlimited. */ - int _indent; gint _max_length; public: - ParamString(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml); + ParamString(const gchar * name, + const gchar * guitext, + const gchar * desc, + const Parameter::_scope_t scope, + bool gui_hidden, + const gchar * gui_tip, + int indent, + Inkscape::Extension::Extension * ext, + Inkscape::XML::Node * xml); virtual ~ParamString(void); /** \brief Returns \c _value, with a \i const to protect it. */ diff --git a/src/extension/prefdialog.cpp b/src/extension/prefdialog.cpp index fcc88853d..2bdbb081e 100644 --- a/src/extension/prefdialog.cpp +++ b/src/extension/prefdialog.cpp @@ -50,6 +50,8 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co _effect(effect), _exEnv(NULL) { + this->set_default_size(0,0); // we want the window to be as small as possible instead of clobbering up space + Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox()); if (controls == NULL) { if (_effect == NULL) { @@ -59,11 +61,10 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co controls = _effect->get_imp()->prefs_effect(_effect, SP_ACTIVE_DESKTOP, &_signal_param_change, NULL); _signal_param_change.connect(sigc::mem_fun(this, &PrefDialog::param_change)); } - - hbox->pack_start(*controls, true, true, 6); + hbox->pack_start(*controls, true, true, 0); hbox->show(); - this->get_content_area()->pack_start(*hbox, true, true, 6); + this->get_content_area()->pack_start(*hbox, true, true, 0); /* Gtk::Button * help_button = add_button(Gtk::Stock::HELP, Gtk::RESPONSE_HELP); @@ -88,15 +89,16 @@ PrefDialog::PrefDialog (Glib::ustring name, gchar const * help, Gtk::Widget * co auto sep = Gtk::manage(new Gtk::Separator()); sep->show(); - this->get_content_area()->pack_start(*sep, true, true, 4); + this->get_content_area()->pack_start(*sep, false, false, Parameter::GUI_BOX_SPACING); hbox = Gtk::manage(new Gtk::HBox()); + hbox->set_border_width(Parameter::GUI_BOX_MARGIN); _button_preview = _param_preview->get_widget(NULL, NULL, &_signal_preview); _button_preview->show(); - hbox->pack_start(*_button_preview, true, true,6); + hbox->pack_start(*_button_preview, true, true, 0); hbox->show(); - this->get_content_area()->pack_start(*hbox, true, true, 6); + this->get_content_area()->pack_start(*hbox, false, false, 0); Gtk::Box * hbox = dynamic_cast<Gtk::Box *>(_button_preview); if (hbox != NULL) { diff --git a/src/file-update.cpp b/src/file-update.cpp new file mode 100644 index 000000000..2275ab1c3 --- /dev/null +++ b/src/file-update.cpp @@ -0,0 +1,176 @@ +/** + * @file-update + * Operations to bump files from the pre-0.92 era into the 0.92+ era + * (90dpi vs 96dpi, line height problems, and related bugs) + */ +/* Authors: + * Tavmjong Bah + * Marc Jeanmougin + * su_v + */ +#include "file.h" +#include "sp-root.h" +#include "sp-text.h" +#include "sp-tspan.h" +#include "sp-flowdiv.h" +#include "sp-flowtext.h" +#include "sp-object.h" +#include "sp-item.h" +#include "style.h" +#include "document.h" +#include <string> +#include <clocale> +#include "text-editing.h" + +using namespace std; + +bool is_line(SPObject *i) +{ + if (!(i->getAttribute("sodipodi:role"))) + return false; + return !strcmp(i->getAttribute("sodipodi:role"), "line"); +} + + +void fix_blank_line(SPObject *o) +{ + if (SP_IS_TEXT(o)) + ((SPText *)o)->rebuildLayout(); + else if (SP_IS_FLOWTEXT(o)) + ((SPFlowtext *)o)->rebuildLayout(); + + SPIFontSize fontsize = o->style->font_size; + SPILengthOrNormal lineheight = o->style->line_height; + vector<SPObject *> cl = o->childList(false); + bool beginning = true; + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) { + SPObject *i = *ci; + if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) { + if (sp_text_get_length((SPItem *)i) <= 1) { // empty line + Inkscape::Text::Layout::iterator pos = te_get_layout((SPItem*)(o))->charIndexToIterator( + ((SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i))?0:((ci==cl.begin())?0:1)) + sp_text_get_length_upto(o,i) ); + sp_te_insert((SPItem *)o, pos, "\u00a0"); //"\u00a0" + gchar *l = g_strdup_printf("%f", lineheight.value); + gchar *f = g_strdup_printf("%f", fontsize.value); + i->style->line_height.readIfUnset(l); + if (!beginning) + i->style->font_size.read(f); + else + i->style->font_size.readIfUnset(f); + g_free(l); + g_free(f); + } else { + beginning = false; + fontsize = i->style->font_size; + lineheight = o->style->line_height; + } + } + } +} + +void fix_line_spacing(SPObject *o) +{ + SPILengthOrNormal lineheight = o->style->line_height; + bool inner = false; + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) { + SPObject *i = *ci; + if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) { + // if no line-height attribute, set it + gchar *l = g_strdup_printf("%f", lineheight.value); + i->style->line_height.readIfUnset(l); + g_free(l); + } + inner = true; + } + if (inner) { + if (SP_IS_TEXT(o)) { + o->style->line_height.read("0.00%"); + } else { + o->style->line_height.read("0.01%"); + } + } +} + +void fix_font_name(SPObject *o) +{ + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) + fix_font_name(*ci); + string prev = o->style->font_family.value ? o->style->font_family.value : o->style->font_family.value_default; + if (prev == "Sans") + o->style->font_family.read("sans-serif"); + else if (prev == "Serif") + o->style->font_family.read("serif"); + else if (prev == "Monospace") + o->style->font_family.read("monospace"); +} + + +void fix_font_size(SPObject *o) +{ + SPIFontSize fontsize = o->style->font_size; + if (!fontsize.set) + return; + bool inner = false; + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) { + SPObject *i = *ci; + fix_font_size(i); + if ((SP_IS_TSPAN(i) && is_line(i)) || SP_IS_FLOWPARA(i) || SP_IS_FLOWDIV(i)) { + inner = true; + gchar *s = g_strdup_printf("%f", fontsize.value); + if (fontsize.set) + i->style->font_size.readIfUnset(s); + g_free(s); + } + } + if (inner && (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o))) + o->style->font_size.clear(); +} + + + +// helper function +void sp_file_text_run_recursive(void (*f)(SPObject *), SPObject *o) +{ + if (SP_IS_TEXT(o) || SP_IS_FLOWTEXT(o)) + f(o); + else { + vector<SPObject *> cl = o->childList(false); + for (vector<SPObject *>::const_iterator ci = cl.begin(); ci != cl.end(); ++ci) + sp_file_text_run_recursive(f, *ci); + } +} + +void fix_update(SPObject *o) { + o->style->write(); + o->updateRepr(); +} + +void sp_file_convert_text_baseline_spacing(SPDocument *doc) +{ + setlocale(LC_NUMERIC,"C"); + sp_file_text_run_recursive(fix_blank_line, doc->getRoot()); + sp_file_text_run_recursive(fix_line_spacing, doc->getRoot()); + sp_file_text_run_recursive(fix_font_size, doc->getRoot()); + sp_file_text_run_recursive(fix_update, doc->getRoot()); + setlocale(LC_NUMERIC,""); +} + +void sp_file_convert_font_name(SPDocument *doc) +{ + sp_file_text_run_recursive(fix_font_name, doc->getRoot()); + sp_file_text_run_recursive(fix_update, doc->getRoot()); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/file.cpp b/src/file.cpp index 6613b1e65..fc9ce6b19 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -908,7 +908,7 @@ void sp_file_revert_dialog() reverted = sp_file_open(uri,NULL); if (reverted) { // restore zoom and view - desktop->zoom_absolute(c[Geom::X], c[Geom::Y], zoom); + desktop->zoom_absolute_center_point(c, zoom); } } else { reverted = false; diff --git a/src/file.h b/src/file.h index 4ffbc8ec0..2fc4ea910 100644 --- a/src/file.h +++ b/src/file.h @@ -203,7 +203,8 @@ void sp_file_print (Gtk::Window& parentWindow); * clean unused defs out of file */ void sp_file_vacuum (SPDocument *doc); - +void sp_file_convert_text_baseline_spacing(SPDocument *doc); +void sp_file_convert_font_name(SPDocument *doc); #endif // SEEN_SP_FILE_H diff --git a/src/filters/image.cpp b/src/filters/image.cpp index 887201eb3..3dedb28ad 100644 --- a/src/filters/image.cpp +++ b/src/filters/image.cpp @@ -14,12 +14,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "image.h" + #include <sigc++/bind.h> + +#include "bad-uri-exception.h" #include "display/nr-filter-image.h" #include "uri.h" #include "uri-references.h" #include "attributes.h" -#include "image.h" #include "xml/repr.h" #include "display/nr-filter.h" diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index a928cac0d..006e2f790 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -1067,7 +1067,7 @@ static void gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const & -static void gr_knot_grabbed_handler(SPKnot */*knot*/, unsigned int /*state*/, gpointer data) +static void gr_knot_mousedown_handler(SPKnot */*knot*/, unsigned int /*state*/, gpointer data) { GrDragger *dragger = (GrDragger *) data; GrDrag *drag = dragger->parent; @@ -1688,7 +1688,7 @@ GrDragger::GrDragger(GrDrag *parent, Geom::Point p, GrDraggable *draggable) this->sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged(sigc::mem_fun(*this, &GrDragger::updateControlSizes)); this->_clicked_connection = this->knot->click_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_clicked_handler), this)); this->_doubleclicked_connection = this->knot->doubleclicked_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_doubleclicked_handler), this)); - this->_grabbed_connection = this->knot->grabbed_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_grabbed_handler), this)); + this->_mousedown_connection = this->knot->mousedown_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_mousedown_handler), this)); this->_ungrabbed_connection = this->knot->ungrabbed_signal.connect(sigc::bind(sigc::ptr_fun(gr_knot_ungrabbed_handler), this)); // add the initial draggable @@ -1712,7 +1712,7 @@ GrDragger::~GrDragger() this->_moved_connection.disconnect(); this->_clicked_connection.disconnect(); this->_doubleclicked_connection.disconnect(); - this->_grabbed_connection.disconnect(); + this->_mousedown_connection.disconnect(); this->_ungrabbed_connection.disconnect(); /* unref should call destroy */ diff --git a/src/gradient-drag.h b/src/gradient-drag.h index b4a64c78d..1fe2fb3f0 100644 --- a/src/gradient-drag.h +++ b/src/gradient-drag.h @@ -130,7 +130,7 @@ private: sigc::connection _moved_connection; sigc::connection _clicked_connection; sigc::connection _doubleclicked_connection; - sigc::connection _grabbed_connection; + sigc::connection _mousedown_connection; sigc::connection _ungrabbed_connection; }; diff --git a/src/id-clash.cpp b/src/id-clash.cpp index c284843b8..162cb9e89 100644 --- a/src/id-clash.cpp +++ b/src/id-clash.cpp @@ -373,9 +373,9 @@ void rename_id(SPObject *elem, Glib::ustring const &new_name) gchar *id = g_strdup(new_name.c_str()); //id is not empty here as new_name is check to be not empty g_strcanon (id, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.:", '_'); Glib::ustring new_name2 = id; //will not fail as id can not be NULL, see length check on new_name - g_free (id); if (!isalnum (new_name2[0])) { g_message("Invalid Id, will not change."); + g_free (id); return; } @@ -396,7 +396,7 @@ void rename_id(SPObject *elem, Glib::ustring const &new_name) break; } } - + g_free (id); // Change to the new ID elem->getRepr()->setAttribute("id", new_name2); // Make a note of this change, if we need to fix up refs to it diff --git a/src/inkgc/gc-core.h b/src/inkgc/gc-core.h index d9d0bf4ff..a27510f50 100644 --- a/src/inkgc/gc-core.h +++ b/src/inkgc/gc-core.h @@ -18,7 +18,7 @@ #include <new> #include <cstdlib> -#include <cstddef> + #ifdef HAVE_GC_GC_H # include <gc/gc.h> #else @@ -137,7 +137,6 @@ inline void *operator new(std::size_t size, Inkscape::GC::CollectionPolicy collect, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { using namespace Inkscape::GC; @@ -168,7 +167,6 @@ inline void *operator new(std::size_t size, Inkscape::GC::ScanPolicy scan, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { return operator new(size, scan, Inkscape::GC::AUTO, cleanup, data); } @@ -178,7 +176,6 @@ inline void *operator new[](std::size_t size, Inkscape::GC::CollectionPolicy collect, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { return operator new(size, scan, collect, cleanup, data); } @@ -187,7 +184,6 @@ inline void *operator new[](std::size_t size, Inkscape::GC::ScanPolicy scan, Inkscape::GC::CleanupFunc cleanup=NULL, void *data=NULL) -throw(std::bad_alloc) { return operator new[](size, scan, Inkscape::GC::AUTO, cleanup, data); } diff --git a/src/inkgc/gc-managed.h b/src/inkgc/gc-managed.h index f435d7802..97b2b816e 100644 --- a/src/inkgc/gc-managed.h +++ b/src/inkgc/gc-managed.h @@ -30,7 +30,6 @@ public: void *operator new(std::size_t size, ScanPolicy scan=default_scan, CollectionPolicy collect=default_collect) - throw (std::bad_alloc) { return ::operator new(size, scan, collect); } @@ -38,7 +37,6 @@ public: void *operator new[](std::size_t size, ScanPolicy scan=default_scan, CollectionPolicy collect=default_collect) - throw (std::bad_alloc) { return ::operator new[](size, scan, collect); } diff --git a/src/inkgc/gc.cpp b/src/inkgc/gc.cpp index ffa94ea2a..b1bd07af6 100644 --- a/src/inkgc/gc.cpp +++ b/src/inkgc/gc.cpp @@ -173,7 +173,7 @@ public: {} }; -Ops const &get_ops() throw (InvalidGCModeError) { +Ops const &get_ops() { char *mode_string=std::getenv("_INKSCAPE_GC"); if (mode_string) { if (!std::strcmp(mode_string, "enable")) { diff --git a/src/isinf.h b/src/isinf.h deleted file mode 100644 index 8d590b972..000000000 --- a/src/isinf.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ISINF_H__ -#define __ISINF_H__ - -/* - * Fix for missing std::isinf with SOLARIS8/GCC3.2 - */ - -#if defined (SOLARIS) - - #include <ieeefp.h> - #define isinf(x) ((fpclass(x) == FP_NINF) || (fpclass(x) == FP_PINF)) - -#elif defined(__APPLE__) && __GNUC__ == 3 -#define isinf(x) __isinf(x) -#elif __cplusplus >= 201103L -# include <cmath> -# define isinf std::isinf -#endif - -#endif /* __ISINF_H__ */ diff --git a/src/knot.cpp b/src/knot.cpp index c04206dee..50fa73391 100644 --- a/src/knot.cpp +++ b/src/knot.cpp @@ -214,7 +214,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot if ((event->button.button == 1) && knot->desktop && knot->desktop->event_context && !knot->desktop->event_context->space_panning) { Geom::Point const p = knot->desktop->w2d(Geom::Point(event->button.x, event->button.y)); knot->startDragging(p, (gint) event->button.x, (gint) event->button.y, event->button.time); - knot->grabbed_signal.emit(knot, event->button.state); + knot->mousedown_signal.emit(knot, event->button.state); consumed = TRUE; } break; @@ -241,7 +241,6 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot if (moved) { knot->setFlag(SP_KNOT_DRAGGING, FALSE); - knot->ungrabbed_signal.emit(knot, event->button.state); } else { knot->click_signal.emit(knot, event->button.state); @@ -277,6 +276,7 @@ static int sp_knot_handler(SPCanvasItem */*item*/, GdkEvent *event, SPKnot *knot if (!moved) { knot->setFlag(SP_KNOT_DRAGGING, TRUE); + knot->grabbed_signal.emit(knot, event->button.state); } sp_event_context_snap_delay_handler(knot->desktop->event_context, NULL, knot, (GdkEventMotion *)event, Inkscape::UI::Tools::DelayedSnapEvent::KNOT_HANDLER); diff --git a/src/knot.h b/src/knot.h index 954ddd8a6..0a0ce73d2 100644 --- a/src/knot.h +++ b/src/knot.h @@ -79,6 +79,7 @@ public: // be able to figure that out sigc::signal<void, SPKnot *, unsigned int> click_signal; sigc::signal<void, SPKnot*, unsigned int> doubleclicked_signal; + sigc::signal<void, SPKnot*, unsigned int> mousedown_signal; sigc::signal<void, SPKnot*, unsigned int> grabbed_signal; sigc::signal<void, SPKnot *, unsigned int> ungrabbed_signal; sigc::signal<void, SPKnot *, Geom::Point const &, unsigned int> moved_signal; diff --git a/src/libcola/gradient_projection.cpp b/src/libcola/gradient_projection.cpp index 3e41aceac..c079c3149 100644 --- a/src/libcola/gradient_projection.cpp +++ b/src/libcola/gradient_projection.cpp @@ -18,7 +18,6 @@ #include "gradient_projection.h" #include <iostream> #include <2geom/math-utils.h> -#include "isinf.h" #include <math.h> using namespace std; @@ -51,7 +50,7 @@ unsigned GradientProjection::solve(double * b) { //cerr << "in gradient projection: n=" << n << endl; for (i=0;i<n;i++) { assert(!IS_NAN(place[i])); - assert(!isinf(place[i])); + assert(IS_FINITE(place[i])); vars[i]->desiredPosition=place[i]; } try { @@ -99,7 +98,7 @@ unsigned GradientProjection::solve(double * b) { for (i=0; i<n; i++) { place[i]-=alpha*g[i]; assert(!IS_NAN(place[i])); - assert(!isinf(place[i])); + assert(IS_FINITE(place[i])); vars[i]->desiredPosition=place[i]; } for (DummyVars::iterator it=dummy_vars.begin();it!=dummy_vars.end();++it){ diff --git a/src/libcroco/cr-parser.c b/src/libcroco/cr-parser.c index 544b35ab0..4e50b5402 100644 --- a/src/libcroco/cr-parser.c +++ b/src/libcroco/cr-parser.c @@ -2002,7 +2002,7 @@ cr_parser_parse_simple_sels (CRParser * a_this, *Returns CR_OK upon successful completion, an error *code otherwise. */ -static enum CRStatus +enum CRStatus cr_parser_parse_selector (CRParser * a_this, CRSelector ** a_selector) { diff --git a/src/libcroco/cr-parser.h b/src/libcroco/cr-parser.h index 6dce9439e..24cf5dfe8 100644 --- a/src/libcroco/cr-parser.h +++ b/src/libcroco/cr-parser.h @@ -97,6 +97,8 @@ enum CRStatus cr_parser_set_default_sac_handler (CRParser *a_this) ; enum CRStatus cr_parser_parse_term (CRParser *a_this, CRTerm **a_term) ; +enum CRStatus cr_parser_parse_selector (CRParser * a_this, CRSelector ** a_selector) ; + enum CRStatus cr_parser_parse_expr (CRParser *a_this, CRTerm **a_expr) ; enum CRStatus cr_parser_parse_prio (CRParser *a_this, CRString **a_prio) ; diff --git a/src/libcroco/cr-selector.c b/src/libcroco/cr-selector.c index c56c43fda..f9193feda 100644 --- a/src/libcroco/cr-selector.c +++ b/src/libcroco/cr-selector.c @@ -51,6 +51,19 @@ cr_selector_new (CRSimpleSel * a_simple_sel) return result; } +/** + * cr_selector_parse_from_buf: + * + *@a_char_buf: the buffer to parse. + *@a_enc: the encoding of the input buffer a_char_buf. + * + *Parses a buf for selectors. + * + *Fix Me: parsing will fail for some cases if buf does not end with '{'. + * + *Returns the newly built instance of #CRSelector, or + *NULL in case of failure. + */ CRSelector * cr_selector_parse_from_buf (const guchar * a_char_buf, enum CREncoding a_enc) { @@ -62,7 +75,17 @@ cr_selector_parse_from_buf (const guchar * a_char_buf, enum CREncoding a_enc) a_enc, FALSE); g_return_val_if_fail (parser, NULL); - return NULL; + CRSelector *selector = NULL; + enum CRStatus status = CR_OK; + status = cr_parser_parse_selector (parser, &selector); + + if (status != CR_OK) { + if (selector) { + cr_selector_unref (selector); + selector = NULL; + } + } + return selector; } /** diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index e6da9ba63..8e173a7c7 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -210,6 +210,7 @@ class Layout::Calculator */ static void dumpPangoItemsOut(ParagraphInfo *para){ std::cout << "Pango items: " << para->pango_items.size() << std::endl; + font_factory * factory = font_factory::Default(); for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){ std::cout << "idx: " << pidx @@ -217,6 +218,8 @@ static void dumpPangoItemsOut(ParagraphInfo *para){ << para->pango_items[pidx].item->offset << " length: " << para->pango_items[pidx].item->length + << " font: " + << factory->ConstructFontSpecification( para->pango_items[pidx].font ) << std::endl; } } @@ -1483,11 +1486,18 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, UnbrokenSpanPosition span_pos; for( ; ; ) { + + // Get regions where one can place one line of text (can be more than one, if filling a + // donut for example). std::vector<ScanlineMaker::ScanRun> scan_runs; scan_runs = _scanline_maker->makeScanline(*line_box_height); // Only one line with "InfiniteScanlineMaker + + // If scan_runs is empty, we must have reached the bottom of a shape. Go to next shape. while (scan_runs.empty()) { // Only used by ShapeScanlineMaker if (!_goToNextWrapShape()) return false; // no more shapes to wrap in to + + *line_box_height = *strut_height; scan_runs = _scanline_maker->makeScanline(*line_box_height); } @@ -1498,13 +1508,21 @@ bool Layout::Calculator::_findChunksForLine(ParagraphInfo const ¶, unsigned scan_run_index; span_pos = *start_span_pos; for (scan_run_index = 0 ; scan_run_index < scan_runs.size() ; scan_run_index++) { - if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height)) + + // Returns false if some text in line requires a taller line_box_height. + // (We try again with a larger line_box_height.) + if (!_buildChunksInScanRun(para, span_pos, scan_runs[scan_run_index], chunk_info, line_box_height, strut_height)) { break; + } + if (!chunk_info->empty() && !chunk_info->back().broken_spans.empty()) span_pos = chunk_info->back().broken_spans.back().end; } + if (scan_run_index == scan_runs.size()) break; // ie when buildChunksInScanRun() succeeded - } + + } // End for loop + *start_span_pos = span_pos; TRACE((" final line_box_height: %f\n", line_box_height->emSize() )); TRACE((" end _findChunksForLine: chunks: %lu\n", chunk_info->size())); @@ -1578,6 +1596,8 @@ bool Layout::Calculator::_buildChunksInScanRun(ParagraphInfo const ¶, new_span_height.descent > line_height->descent + std::numeric_limits<float>::epsilon() ) { // Take larger of each of the two ascents and two descents per CSS line_height->max(new_span_height); + + // Currently always true for flowed text and false for Inkscape multiline text. if (!_scanline_maker->canExtendCurrentScanline(*line_height)) { return false; } @@ -1752,12 +1772,20 @@ bool Layout::Calculator::calculate() span_pos.char_byte = 0; span_pos.char_index = 0; + bool keep_going = true; do { // for each line in the paragraph TRACE(("begin line\n")); + std::vector<ChunkInfo> line_chunk_info; - if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height, &strut_height )) + if (!_findChunksForLine(para, &span_pos, &line_chunk_info, &line_box_height, &strut_height )) { + keep_going = false; break; // out of shapes to wrap in to + } + if (line_box_height.emSize() < 0.001 && line_chunk_info.empty()) { + keep_going = false; + break; // No room for text and not useful to try again at same place. + } _outputLine(para, line_box_height, line_chunk_info); _scanline_maker->setLineHeight( line_box_height ); _scanline_maker->completeLine(); // Increments y by line height @@ -1765,7 +1793,7 @@ bool Layout::Calculator::calculate() } while (span_pos.iter_span != para.unbroken_spans.end()); TRACE(("para %lu end\n\n", _flow._paragraphs.size() - 1)); - if (_scanline_maker != NULL) { + if (keep_going) { bool is_empty_para = _flow._characters.empty() || _flow._characters.back().line(&_flow).in_paragraph != _flow._paragraphs.size() - 1; if ((is_empty_para && para_end_input_index + 1 >= _flow._input_stream.size()) || para_end_input_index + 1 < _flow._input_stream.size()) { diff --git a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp index 0d6112d19..a57aaa3e4 100644 --- a/src/libnrtype/Layout-TNG-Scanline-Makers.cpp +++ b/src/libnrtype/Layout-TNG-Scanline-Makers.cpp @@ -124,7 +124,7 @@ std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScan FloatLigne line_rasterization; FloatLigne line_decent_length_runs; float line_text_height = (float)(line_height.emSize()); - if (line_text_height == 0.0) + if (line_text_height < 0.001) line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do _current_line_height = (float)line_height.emSize(); diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index e06b8392f..c39315106 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -194,7 +194,7 @@ public: /** For expressing paragraph alignment. These values are rotated in the case of vertical text, but are not dependent on whether the paragraph is rtl or ltr, thus LEFT is always either left or top. */ - enum Alignment {LEFT, CENTER, RIGHT, FULL}; + enum Alignment {LEFT, CENTER, RIGHT, FULL, NONE}; /** The CSS spec allows line-height:normal to be whatever the user agent thinks will look good. This is our value, as a multiple of font-size. */ @@ -1144,7 +1144,7 @@ inline unsigned Layout::paragraphIndex(iterator const &it) const {return it._char_index == _characters.size() ? _paragraphs.size() - 1 : _characters[it._char_index].line(this).in_paragraph;} inline Layout::Alignment Layout::paragraphAlignment(iterator const &it) const - {return _paragraphs[paragraphIndex(it)].alignment;} + {return (_paragraphs.size() == 0) ? NONE : _paragraphs[paragraphIndex(it)].alignment;} inline bool Layout::iterator::nextGlyph() { diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index a02f62ac9..07e431376 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -469,6 +469,9 @@ void Effect::processObjects(LpeAction lpe_action) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } for (std::vector<const char *>::iterator el_it = items.begin(); el_it != items.end(); ++el_it) { const char * id = *el_it; diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp index e70575986..440af6f9c 100644 --- a/src/live_effects/lpe-clone-original.cpp +++ b/src/live_effects/lpe-clone-original.cpp @@ -64,6 +64,9 @@ void LPECloneOriginal::cloneAttrbutes(SPObject *origin, SPObject *dest, bool live, const char * attributes, const char * style_attributes, bool root) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { std::vector< SPObject * > childs = origin->childList(true); size_t index = 0; @@ -258,28 +261,18 @@ LPECloneOriginal::doBeforeEffect (SPLPEItem const* lpeitem){ style_attr.erase (style_attr.size()-1, 1); } style_attr.append(Glib::ustring(style_attributes.param_getSVGValue()).append(",")); - if (inverse) { - cloneAttrbutes(SP_OBJECT(sp_lpe_item), linked_item.getObject(), true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true); - Geom::OptRect bbox = SP_ITEM(sp_lpe_item)->geometricBounds(); - if (bbox && preserve_position && origin != Geom::Point(0,0)) { - origin = (*bbox).corner(0) - origin; - SP_ITEM(linked_item.getObject())->transform *= Geom::Translate(origin); - } - bbox = SP_ITEM(sp_lpe_item)->geometricBounds(); - if (bbox && preserve_position) { - origin = (*bbox).corner(0); - } - } else { - cloneAttrbutes(linked_item.getObject(), SP_OBJECT(sp_lpe_item), true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true); - Geom::OptRect bbox = SP_ITEM(linked_item.getObject())->geometricBounds(); - if (bbox && preserve_position && origin != Geom::Point(0,0)) { - origin = (*bbox).corner(0) - origin; - SP_ITEM(sp_lpe_item)->transform *= Geom::Translate(origin); - } - bbox = SP_ITEM(linked_item.getObject())->geometricBounds(); - if (bbox && preserve_position) { - origin = (*bbox).corner(0); - } + + SPItem * from = inverse ? SP_ITEM(sp_lpe_item) : SP_ITEM(linked_item.getObject()); + SPItem * to = !inverse ? SP_ITEM(sp_lpe_item) : SP_ITEM(linked_item.getObject()); + cloneAttrbutes(from, to, true, g_strdup(attr.c_str()), g_strdup(style_attr.c_str()), true); + Geom::OptRect bbox = from->geometricBounds(); + if (bbox && preserve_position && origin != Geom::Point(0,0)) { + origin = (*bbox).corner(0) - origin; + to->transform *= Geom::Translate(origin); + } + bbox = from->geometricBounds(); + if (bbox && preserve_position) { + origin = (*bbox).corner(0); } } } @@ -354,7 +347,6 @@ void LPECloneOriginal::transform_multiply(Geom::Affine const& postmul, bool set) { if (linked_item.linksToItem()) { - bool changed = false; linked_item.getObject()->requestModified(SP_OBJECT_MODIFIED_FLAG); } } diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp index 42e055062..9ba680cd4 100644 --- a/src/live_effects/lpe-copy_rotate.cpp +++ b/src/live_effects/lpe-copy_rotate.cpp @@ -92,11 +92,13 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem) { if (split_items) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } items.clear(); container = dynamic_cast<SPObject *>(sp_lpe_item->parent); - SPDocument * doc = SP_ACTIVE_DOCUMENT; Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); - Inkscape::XML::Node *root_origin = doc->getReprRoot(); + Inkscape::XML::Node *root_origin = document->getReprRoot(); if (root_origin != root) { return; } @@ -168,15 +170,15 @@ LPECopyRotate::doAfterEffect (SPLPEItem const* lpeitem) processObjects(LPE_ERASE); items.clear(); } - - std::cout << previous_num_copies << "previous_num_copies\n"; - std::cout << num_copies << "num_copies\n"; } void LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Document *xml_doc = document->getReprDoc(); if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { std::vector< SPObject * > childs = origin->childList(true); @@ -190,7 +192,7 @@ LPECopyRotate::cloneD(SPObject *origin, SPObject *dest, bool root, bool reset) } SPShape * shape = SP_SHAPE(origin); SPPath * path = SP_PATH(dest); - if (!path && !SP_IS_GROUP(dest)) { + if (shape && !path) { Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); path = SP_PATH(dest); @@ -219,6 +221,9 @@ void LPECopyRotate::toItem(Geom::Affine transform, size_t i, bool reset) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Document *xml_doc = document->getReprDoc(); const char * elemref_id = g_strdup(Glib::ustring("rotated-").append(std::to_string(i)).append("-").append(sp_lpe_item->getRepr()->attribute("id")).c_str()); items.push_back(elemref_id); @@ -377,11 +382,11 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) num_copies.param_set_increments(1.0, 10.0); } - if (dist_angle_handle < 1.0) { - dist_angle_handle = 1.0; - } A = Point(boundingbox_X.min(), boundingbox_Y.middle()); B = Point(boundingbox_X.middle(), boundingbox_Y.middle()); + if (Geom::are_near(A, B, 0.01)) { + B += Geom::Point(1.0, 0.0); + } dir = unit_vector(B - A); // I first suspected the minus sign to be a bug in 2geom but it is // likely due to SVG's choice of coordinate system orientation (max) @@ -394,6 +399,9 @@ LPECopyRotate::doBeforeEffect (SPLPEItem const* lpeitem) dist_angle_handle = L2(starting_point - origin); } } + if (dist_angle_handle < 1.0) { + dist_angle_handle = 1.0; + } start_pos = origin + dir * Rotate(-rad_from_deg(starting_angle)) * dist_angle_handle; rot_pos = origin + dir * Rotate(-rad_from_deg(rotation_angle+starting_angle)) * dist_angle_handle; near = Geom::are_near(start_pos, (Geom::Point)starting_point, 0.01); diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp index 38cbeaac0..f2fc00aeb 100644 --- a/src/live_effects/lpe-curvestitch.cpp +++ b/src/live_effects/lpe-curvestitch.cpp @@ -54,6 +54,7 @@ LPECurveStitch::LPECurveStitch(LivePathEffectObject *lpeobject) : prop_scale.param_set_digits(3); prop_scale.param_set_increments(0.01, 0.10); + transformed = false; } LPECurveStitch::~LPECurveStitch() @@ -106,8 +107,9 @@ LPECurveStitch::doEffect_path (Geom::PathVector const & path_in) if (!Geom::are_near(start,end)) { gdouble scaling_y = 1.0; - if (scale_y_rel.get_value()) { + if (scale_y_rel.get_value() || transformed) { scaling_y = (L2(end-start)/scaling)*prop_scale; + transformed = false; } else { scaling_y = prop_scale; } @@ -193,12 +195,8 @@ LPECurveStitch::transform_multiply(Geom::Affine const& postmul, bool set) if (postmul.isTranslation()) { strokepath.param_transform_multiply(postmul, set); } else if (!scale_y_rel.get_value()) { - // this basically means that for this transformation, the result should be the same as normal scaling the result path - // don't know how to do this yet. -// Geom::Affine new_postmul; - //new_postmul.setIdentity(); -// new_postmul.setTranslation(postmul.translation()); -// Effect::transform_multiply(new_postmul, set); + transformed = true; + strokepath.param_transform_multiply(postmul, set); } } diff --git a/src/live_effects/lpe-curvestitch.h b/src/live_effects/lpe-curvestitch.h index c6ea66f6c..0a48046e0 100644 --- a/src/live_effects/lpe-curvestitch.h +++ b/src/live_effects/lpe-curvestitch.h @@ -43,6 +43,7 @@ private: RandomParam endpoint_spacing_variation; ScalarParam prop_scale; BoolParam scale_y_rel; + bool transformed; LPECurveStitch(const LPECurveStitch&); LPECurveStitch& operator=(const LPECurveStitch&); diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp index 1e2eadfdb..2e1fe0dc1 100644 --- a/src/live_effects/lpe-fill-between-many.cpp +++ b/src/live_effects/lpe-fill-between-many.cpp @@ -4,13 +4,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <gtkmm/box.h> #include "live_effects/lpe-fill-between-many.h" #include "display/curve.h" #include "sp-shape.h" #include "sp-text.h" +#include "svg/svg.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -19,10 +19,18 @@ namespace LivePathEffect { LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) : Effect(lpeobject), - linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this) + linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this), + fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this, false), + allow_transforms(_("Allow transforms"), _("Allow transforms"), "allow_transforms", &wr, this, false), + join(_("Join subpaths"), _("Join subpaths"), "join", &wr, this, true), + close(_("Close"), _("Close path"), "close", &wr, this, true) { registerParameter( dynamic_cast<Parameter *>(&linked_paths) ); - //perceived_path = true; + registerParameter( dynamic_cast<Parameter *>(&fuse) ); + registerParameter( dynamic_cast<Parameter *>(&allow_transforms) ); + registerParameter( dynamic_cast<Parameter *>(&join) ); + registerParameter( dynamic_cast<Parameter *>(&close) ); + transformmultiply = false; } LPEFillBetweenMany::~LPEFillBetweenMany() @@ -44,25 +52,50 @@ void LPEFillBetweenMany::doEffect (SPCurve * curve) linked_path = (*iter)->_pathvector.front(); } - if (!res_pathv.empty()) { + if (!res_pathv.empty() && join) { linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj); - res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint()); + if (!are_near(res_pathv.front().finalPoint(), linked_path.initialPoint(), 0.01) || !fuse) { + res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint()); + } else { + linked_path.setInitial(res_pathv.front().finalPoint()); + } res_pathv.front().append(linked_path); } else { firstObj = SP_ITEM(obj); + if (close && !join) { + linked_path.close(); + } res_pathv.push_back(linked_path); } } } - if (!res_pathv.empty()) { + if (!res_pathv.empty() && close) { res_pathv.front().close(); } if (res_pathv.empty()) { res_pathv = curve->get_pathvector(); } + if(!allow_transforms && !transformmultiply) { + Geom::Affine affine = Geom::identity(); + sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &affine); + res_pathv *= affine.inverse(); + } + if(transformmultiply) { + transformmultiply = false; + } curve->set_pathvector(res_pathv); } +void +LPEFillBetweenMany::transform_multiply(Geom::Affine const& postmul, bool set) +{ + if(!allow_transforms && sp_lpe_item) { + SP_ITEM(sp_lpe_item)->transform *= postmul.inverse(); + transformmultiply = true; + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); + } +} + } // namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-fill-between-many.h b/src/live_effects/lpe-fill-between-many.h index 99ee8b15f..fe824e936 100644 --- a/src/live_effects/lpe-fill-between-many.h +++ b/src/live_effects/lpe-fill-between-many.h @@ -19,12 +19,16 @@ class LPEFillBetweenMany : public Effect { public: LPEFillBetweenMany(LivePathEffectObject *lpeobject); virtual ~LPEFillBetweenMany(); - + virtual void transform_multiply(Geom::Affine const& postmul, bool set); virtual void doEffect (SPCurve * curve); private: OriginalPathArrayParam linked_paths; - + BoolParam fuse; + BoolParam allow_transforms; + BoolParam join; + BoolParam close; + bool transformmultiply; private: LPEFillBetweenMany(const LPEFillBetweenMany&); LPEFillBetweenMany& operator=(const LPEFillBetweenMany&); diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp index 0dbebdf26..43fef4288 100644 --- a/src/live_effects/lpe-fill-between-strokes.cpp +++ b/src/live_effects/lpe-fill-between-strokes.cpp @@ -8,6 +8,7 @@ #include "display/curve.h" #include "sp-shape.h" #include "sp-text.h" +#include "svg/svg.h" // TODO due to internal breakage in glibmm headers, this must be last: #include <glibmm/i18n.h> @@ -18,12 +19,20 @@ LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) : Effect(lpeobject), linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this), second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this), - reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this) + reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this), + fuse(_("Fuse coincident points"), _("Fuse coincident points"), "fuse", &wr, this, false), + allow_transforms(_("Allow transforms"), _("Allow transforms"), "allow_transforms", &wr, this, false), + join(_("Join subpaths"), _("Join subpaths"), "join", &wr, this, true), + close(_("Close"), _("Close path"), "close", &wr, this, true) { registerParameter( dynamic_cast<Parameter *>(&linked_path) ); registerParameter( dynamic_cast<Parameter *>(&second_path) ); registerParameter( dynamic_cast<Parameter *>(&reverse_second) ); - //perceived_path = true; + registerParameter( dynamic_cast<Parameter *>(&fuse) ); + registerParameter( dynamic_cast<Parameter *>(&allow_transforms) ); + registerParameter( dynamic_cast<Parameter *>(&join) ); + registerParameter( dynamic_cast<Parameter *>(&close) ); + transformmultiply = false; } LPEFillBetweenStrokes::~LPEFillBetweenStrokes() @@ -34,6 +43,13 @@ LPEFillBetweenStrokes::~LPEFillBetweenStrokes() void LPEFillBetweenStrokes::doEffect (SPCurve * curve) { if (curve) { + Geom::Affine affine = Geom::identity(); + if(!allow_transforms && !transformmultiply) { + sp_svg_transform_read(SP_ITEM(sp_lpe_item)->getAttribute("transform"), &affine); + } + if(transformmultiply) { + transformmultiply = false; + } if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) { Geom::PathVector linked_pathv = linked_path.get_pathvector(); Geom::PathVector second_pathv = second_path.get_pathvector(); @@ -51,22 +67,33 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) } if ( !result_linked_pathv.empty() && !result_second_pathv.empty() && !result_linked_pathv.front().closed() ) { - if (reverse_second.get_value()) - { - result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint()); - result_linked_pathv.front().append(result_second_pathv.front().reversed()); + if (reverse_second.get_value()) { + result_second_pathv.front() = result_second_pathv.front().reversed(); } - else - { - result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint()); + if (join) { + if (!are_near(result_linked_pathv.front().finalPoint(), result_second_pathv.front().initialPoint(),0.01) || !fuse) { + result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint()); + } else { + result_second_pathv.front().setInitial(result_linked_pathv.front().finalPoint()); + } result_linked_pathv.front().append(result_second_pathv.front()); + if (close) { + result_linked_pathv.front().close(); + } + } else { + if (close) { + result_linked_pathv.front().close(); + result_second_pathv.front().close(); + } + result_linked_pathv.push_back(result_second_pathv.front()); } + result_linked_pathv *= affine.inverse(); curve->set_pathvector(result_linked_pathv); - } - else if ( !result_linked_pathv.empty() ) { + } else if ( !result_linked_pathv.empty() ) { + result_linked_pathv *= affine.inverse(); curve->set_pathvector(result_linked_pathv); - } - else if ( !result_second_pathv.empty() ) { + } else if ( !result_second_pathv.empty() ) { + result_second_pathv *= affine.inverse(); curve->set_pathvector(result_second_pathv); } } @@ -79,6 +106,10 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) result_pathv.push_back((*iter)); } if ( !result_pathv.empty() ) { + result_pathv *= affine.inverse(); + if (close) { + result_pathv.front().close(); + } curve->set_pathvector(result_pathv); } } @@ -91,12 +122,26 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve) result_pathv.push_back((*iter)); } if ( !result_pathv.empty() ) { + result_pathv *= affine.inverse(); + if (close) { + result_pathv.front().close(); + } curve->set_pathvector(result_pathv); } } } } +void +LPEFillBetweenStrokes::transform_multiply(Geom::Affine const& postmul, bool set) +{ + if(!allow_transforms && sp_lpe_item) { + SP_ITEM(sp_lpe_item)->transform *= postmul.inverse(); + transformmultiply = true; + sp_lpe_item_update_patheffect(sp_lpe_item, false, false); + } +} + } // namespace LivePathEffect } /* namespace Inkscape */ diff --git a/src/live_effects/lpe-fill-between-strokes.h b/src/live_effects/lpe-fill-between-strokes.h index ec57b1852..5bbd6e7da 100644 --- a/src/live_effects/lpe-fill-between-strokes.h +++ b/src/live_effects/lpe-fill-between-strokes.h @@ -19,13 +19,18 @@ class LPEFillBetweenStrokes : public Effect { public: LPEFillBetweenStrokes(LivePathEffectObject *lpeobject); virtual ~LPEFillBetweenStrokes(); - + virtual void transform_multiply(Geom::Affine const& postmul, bool set); virtual void doEffect (SPCurve * curve); private: OriginalPathParam linked_path; OriginalPathParam second_path; BoolParam reverse_second; + BoolParam fuse; + BoolParam allow_transforms; + BoolParam join; + BoolParam close; + bool transformmultiply; private: LPEFillBetweenStrokes(const LPEFillBetweenStrokes&); diff --git a/src/live_effects/lpe-lattice.h b/src/live_effects/lpe-lattice.h index 5eb48909b..8720af138 100644 --- a/src/live_effects/lpe-lattice.h +++ b/src/live_effects/lpe-lattice.h @@ -9,7 +9,7 @@ * Authors: * Johan Engelen * Steren Giannini - * Noé Falzon + * Noé Falzon * Victor Navez * * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl> diff --git a/src/live_effects/lpe-lattice2.h b/src/live_effects/lpe-lattice2.h index 59a0350d3..95c5285fb 100644 --- a/src/live_effects/lpe-lattice2.h +++ b/src/live_effects/lpe-lattice2.h @@ -9,7 +9,7 @@ * Authors: * Johan Engelen * Steren Giannini - * Noé Falzon + * Noé Falzon * Victor Navez * ~suv * Jabiertxo Arraiza diff --git a/src/live_effects/lpe-measure-line.cpp b/src/live_effects/lpe-measure-line.cpp index ef87be81c..83ae60fc4 100644 --- a/src/live_effects/lpe-measure-line.cpp +++ b/src/live_effects/lpe-measure-line.cpp @@ -171,6 +171,9 @@ void LPEMeasureLine::createArrowMarker(const char * mode) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Document *xml_doc = document->getReprDoc(); SPObject *elemref = NULL; Inkscape::XML::Node *arrow = NULL; @@ -232,6 +235,9 @@ void LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angle, bool remove, bool valid) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Document *xml_doc = document->getReprDoc(); Inkscape::XML::Node *rtext = NULL; double doc_w = document->getRoot()->width.value; @@ -323,19 +329,20 @@ LPEMeasureLine::createTextLabel(Geom::Point pos, double length, Geom::Coord angl Inkscape::GC::release(rtspan); } length = Inkscape::Util::Quantity::convert(length / doc_scale, display_unit.c_str(), unit.get_abbreviation()); - std::stringstream length_str; - length_str.precision(precision); - length_str.setf(std::ios::fixed, std::ios::floatfield); + char *oldlocale = g_strdup (setlocale(LC_NUMERIC, NULL)); if (local_locale) { - length_str.imbue(std::locale("")); + setlocale (LC_NUMERIC, ""); } else { - length_str.imbue(std::locale::classic()); + setlocale (LC_NUMERIC, "C"); } - length_str << std::fixed << length; + gchar length_str[64]; + g_snprintf(length_str, 64, "%.*f", (int)precision, length); + setlocale (LC_NUMERIC, oldlocale); + g_free (oldlocale); Glib::ustring label_value = Glib::ustring(format.param_getSVGValue()); size_t s = label_value.find(Glib::ustring("{measure}"),0); if(s < label_value.length()) { - label_value.replace(s,s+9,length_str.str()); + label_value.replace(s,s+9,length_str); } s = label_value.find(Glib::ustring("{unit}"),0); if(s < label_value.length()) { @@ -376,6 +383,9 @@ void LPEMeasureLine::createLine(Geom::Point start,Geom::Point end, const char * id, bool main, bool overflow, bool remove, bool arrows) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Document *xml_doc = document->getReprDoc(); SPObject *elemref = NULL; Inkscape::XML::Node *line = NULL; @@ -500,6 +510,9 @@ LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem) SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem); sp_lpe_item->parent = dynamic_cast<SPObject *>(splpeitem->parent); SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Node *root = splpeitem->document->getReprRoot(); Inkscape::XML::Node *root_origin = document->getReprRoot(); if (root_origin != root) { @@ -643,9 +656,11 @@ LPEMeasureLine::doBeforeEffect (SPLPEItem const* lpeitem) } SPCSSAttr *css = sp_repr_css_attr_new(); sp_repr_css_attr_add_from_string(css, dimline_format.param_getSVGValue()); - setlocale(LC_NUMERIC, std::locale::classic().name().c_str()); + char *oldlocale = g_strdup (setlocale(LC_NUMERIC, NULL)); + setlocale (LC_NUMERIC, "C"); double width_line = atof(sp_repr_css_property(css,"stroke-width","-1")); - setlocale(LC_NUMERIC, std::locale("").name().c_str()); + setlocale (LC_NUMERIC, oldlocale); + g_free (oldlocale); if (width_line > -0.0001) { arrow_gap = 8 * Inkscape::Util::Quantity::convert(width_line/ doc_scale, "mm", display_unit.c_str()); } diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp index 7f0a93c52..c31ce7c01 100644 --- a/src/live_effects/lpe-mirror_symmetry.cpp +++ b/src/live_effects/lpe-mirror_symmetry.cpp @@ -87,11 +87,14 @@ LPEMirrorSymmetry::~LPEMirrorSymmetry() void LPEMirrorSymmetry::doAfterEffect (SPLPEItem const* lpeitem) { + SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } if (split_items && !discard_orig_path) { container = dynamic_cast<SPObject *>(sp_lpe_item->parent); - SPDocument * doc = SP_ACTIVE_DOCUMENT; Inkscape::XML::Node *root = sp_lpe_item->document->getReprRoot(); - Inkscape::XML::Node *root_origin = doc->getReprRoot(); + Inkscape::XML::Node *root_origin = document->getReprRoot(); if (root_origin != root) { return; } @@ -161,20 +164,24 @@ LPEMirrorSymmetry::doBeforeEffect (SPLPEItem const* lpeitem) } } else if ( mode == MT_V){ SPDocument * document = SP_ACTIVE_DOCUMENT; - Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse(); - Geom::Point sp = Geom::Point(document->getWidth().value("px")/2.0, 0) * transform; - start_point.param_setValue(sp, true); - Geom::Point ep = Geom::Point(document->getWidth().value("px")/2.0, document->getHeight().value("px")) * transform; - end_point.param_setValue(ep, true); - center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); + if (document) { + Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse(); + Geom::Point sp = Geom::Point(document->getWidth().value("px")/2.0, 0) * transform; + start_point.param_setValue(sp, true); + Geom::Point ep = Geom::Point(document->getWidth().value("px")/2.0, document->getHeight().value("px")) * transform; + end_point.param_setValue(ep, true); + center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); + } } else { //horizontal page SPDocument * document = SP_ACTIVE_DOCUMENT; - Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse(); - Geom::Point sp = Geom::Point(0, document->getHeight().value("px")/2.0) * transform; - start_point.param_setValue(sp, true); - Geom::Point ep = Geom::Point(document->getWidth().value("px"), document->getHeight().value("px")/2.0) * transform; - end_point.param_setValue(ep, true); - center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); + if (document) { + Geom::Affine transform = i2anc_affine(SP_OBJECT(lpeitem), NULL).inverse(); + Geom::Point sp = Geom::Point(0, document->getHeight().value("px")/2.0) * transform; + start_point.param_setValue(sp, true); + Geom::Point ep = Geom::Point(document->getWidth().value("px"), document->getHeight().value("px")/2.0) * transform; + end_point.param_setValue(ep, true); + center_point.param_setValue(Geom::middle_point((Geom::Point)start_point, (Geom::Point)end_point), true); + } } previous_center = center_point; } @@ -183,6 +190,9 @@ void LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root) { SPDocument * document = SP_ACTIVE_DOCUMENT; + if (!document) { + return; + } Inkscape::XML::Document *xml_doc = document->getReprDoc(); if ( SP_IS_GROUP(origin) && SP_IS_GROUP(dest) && SP_GROUP(origin)->getItemCount() == SP_GROUP(dest)->getItemCount() ) { std::vector< SPObject * > childs = origin->childList(true); @@ -196,7 +206,7 @@ LPEMirrorSymmetry::cloneD(SPObject *origin, SPObject *dest, bool live, bool root } SPShape * shape = SP_SHAPE(origin); SPPath * path = SP_PATH(dest); - if (!path && !SP_IS_GROUP(dest)) { + if (shape && !path) { Inkscape::XML::Node *dest_node = sp_selected_item_to_curved_repr(SP_ITEM(dest), 0); dest->updateRepr(xml_doc, dest_node, SP_OBJECT_WRITE_ALL); path = SP_PATH(dest); @@ -226,67 +236,69 @@ void LPEMirrorSymmetry::toMirror(Geom::Affine transform) { SPDocument * document = SP_ACTIVE_DOCUMENT; - Inkscape::XML::Document *xml_doc = document->getReprDoc(); - const char * id_origin_char = id_origin.param_getSVGValue(); - const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str()); - items.clear(); - items.push_back(elemref_id); - SPObject *elemref= NULL; - Inkscape::XML::Node *phantom = NULL; - if (elemref = document->getObjectById(elemref_id)) { - phantom = elemref->getRepr(); - } else { - phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); - std::vector<const char *> attrs; - attrs.push_back("inkscape:path-effect"); - attrs.push_back("inkscape:original-d"); - attrs.push_back("sodipodi:type"); - attrs.push_back("sodipodi:rx"); - attrs.push_back("sodipodi:ry"); - attrs.push_back("sodipodi:cx"); - attrs.push_back("sodipodi:cy"); - attrs.push_back("sodipodi:end"); - attrs.push_back("sodipodi:start"); - attrs.push_back("inkscape:flatsided"); - attrs.push_back("inkscape:randomized"); - attrs.push_back("inkscape:rounded"); - attrs.push_back("sodipodi:arg1"); - attrs.push_back("sodipodi:arg2"); - attrs.push_back("sodipodi:r1"); - attrs.push_back("sodipodi:r2"); - attrs.push_back("sodipodi:sides"); - attrs.push_back("inkscape:randomized"); - attrs.push_back("sodipodi:argument"); - attrs.push_back("sodipodi:expansion"); - attrs.push_back("sodipodi:radius"); - attrs.push_back("sodipodi:revolution"); - attrs.push_back("sodipodi:t0"); - attrs.push_back("inkscape:randomized"); - attrs.push_back("inkscape:randomized"); - attrs.push_back("inkscape:randomized"); - attrs.push_back("x"); - attrs.push_back("y"); - attrs.push_back("rx"); - attrs.push_back("ry"); - attrs.push_back("width"); - attrs.push_back("height"); - for(const char * attr : attrs) { - phantom->setAttribute(attr, NULL); + if (document) { + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + const char * id_origin_char = id_origin.param_getSVGValue(); + const char * elemref_id = g_strdup(Glib::ustring("mirror-").append(id_origin_char).c_str()); + items.clear(); + items.push_back(elemref_id); + SPObject *elemref= NULL; + Inkscape::XML::Node *phantom = NULL; + if (elemref = document->getObjectById(elemref_id)) { + phantom = elemref->getRepr(); + } else { + phantom = sp_lpe_item->getRepr()->duplicate(xml_doc); + std::vector<const char *> attrs; + attrs.push_back("inkscape:path-effect"); + attrs.push_back("inkscape:original-d"); + attrs.push_back("sodipodi:type"); + attrs.push_back("sodipodi:rx"); + attrs.push_back("sodipodi:ry"); + attrs.push_back("sodipodi:cx"); + attrs.push_back("sodipodi:cy"); + attrs.push_back("sodipodi:end"); + attrs.push_back("sodipodi:start"); + attrs.push_back("inkscape:flatsided"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:rounded"); + attrs.push_back("sodipodi:arg1"); + attrs.push_back("sodipodi:arg2"); + attrs.push_back("sodipodi:r1"); + attrs.push_back("sodipodi:r2"); + attrs.push_back("sodipodi:sides"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("sodipodi:argument"); + attrs.push_back("sodipodi:expansion"); + attrs.push_back("sodipodi:radius"); + attrs.push_back("sodipodi:revolution"); + attrs.push_back("sodipodi:t0"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("inkscape:randomized"); + attrs.push_back("x"); + attrs.push_back("y"); + attrs.push_back("rx"); + attrs.push_back("ry"); + attrs.push_back("width"); + attrs.push_back("height"); + for(const char * attr : attrs) { + phantom->setAttribute(attr, NULL); + } + } + phantom->setAttribute("id", elemref_id); + if (!elemref) { + elemref = container->appendChildRepr(phantom); + Inkscape::GC::release(phantom); + } + cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true); + elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); + if (elemref->parent != container) { + Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); + copy->setAttribute("id", elemref_id); + container->appendChildRepr(copy); + Inkscape::GC::release(copy); + elemref->deleteObject(); } - } - phantom->setAttribute("id", elemref_id); - if (!elemref) { - elemref = container->appendChildRepr(phantom); - Inkscape::GC::release(phantom); - } - cloneD(SP_OBJECT(sp_lpe_item), elemref, true, true); - elemref->getRepr()->setAttribute("transform" , sp_svg_transform_write(transform)); - if (elemref->parent != container) { - Inkscape::XML::Node *copy = phantom->duplicate(xml_doc); - copy->setAttribute("id", elemref_id); - container->appendChildRepr(copy); - Inkscape::GC::release(copy); - elemref->deleteObject(); } } diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp index 18b5b724d..365ff5389 100644 --- a/src/live_effects/lpe-perspective-envelope.cpp +++ b/src/live_effects/lpe-perspective-envelope.cpp @@ -369,12 +369,12 @@ LPEPerspectiveEnvelope::newWidget() Gtk::Label* handles = Gtk::manage(new Gtk::Label(Glib::ustring(_("Handles:")),Gtk::ALIGN_START)); vbox->pack_start(*handles, false, false, 2); hbox_up_handles->pack_start(*widg, true, true, 2); - hbox_up_handles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); + hbox_up_handles->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_EXPAND_WIDGET); } else if(param->param_key == "up_right_point") { hbox_up_handles->pack_start(*widg, true, true, 2); } else if(param->param_key == "down_left_point") { hbox_down_handles->pack_start(*widg, true, true, 2); - hbox_down_handles->pack_start(*Gtk::manage(new Gtk::VSeparator()), Gtk::PACK_EXPAND_WIDGET); + hbox_down_handles->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_EXPAND_WIDGET); } else { hbox_down_handles->pack_start(*widg, true, true, 2); } @@ -403,8 +403,8 @@ LPEPerspectiveEnvelope::newWidget() } vbox->pack_start(*hbox_up_handles,true, true, 2); Gtk::HBox * hbox_middle = Gtk::manage(new Gtk::HBox(true,2)); - hbox_middle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); - hbox_middle->pack_start(*Gtk::manage(new Gtk::HSeparator()), Gtk::PACK_EXPAND_WIDGET); + hbox_middle->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_EXPAND_WIDGET); + hbox_middle->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_EXPAND_WIDGET); vbox->pack_start(*hbox_middle, false, true, 2); vbox->pack_start(*hbox_down_handles, true, true, 2); Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false,0)); diff --git a/src/live_effects/lpe-perspective_path.cpp b/src/live_effects/lpe-perspective_path.cpp index 979b6dea5..b83fb67d7 100644 --- a/src/live_effects/lpe-perspective_path.cpp +++ b/src/live_effects/lpe-perspective_path.cpp @@ -226,15 +226,28 @@ LPEPerspectivePath::newWidget() ++it; } Gtk::HBox * perspectiveId = Gtk::manage(new Gtk::HBox(true,0)); + +#if WITH_GTKMM_3_10 + Gtk::Label* labelPerspective = Gtk::manage(new Gtk::Label("Perspective ID:", Gtk::ALIGN_START, Gtk::ALIGN_START)); +#else Gtk::Label* labelPerspective = Gtk::manage(new Gtk::Label("Perspective ID:", 0., 0.)); +#endif + Gtk::Entry* perspective = Gtk::manage(new Gtk::Entry()); perspective->set_text(perspectiveID); perspective->set_tooltip_text("Set the perspective ID to apply"); perspectiveId->pack_start(*labelPerspective, true, true, 2); perspectiveId->pack_start(*perspective, true, true, 2); vbox->pack_start(*perspectiveId, true, true, 2); - Gtk::Button* apply3D = Gtk::manage(new Gtk::Button(Glib::ustring(_("Refresh perspective")))); - apply3D->set_alignment(0.0, 0.5); + Gtk::Button* apply3D = Gtk::manage(new Gtk::Button()); + +#if WITH_GTKMM_3_10 + Gtk::Label *apply3DLabel = Gtk::manage(new Gtk::Label(_("Refresh perspective"), Gtk::ALIGN_START, Gtk::ALIGN_CENTER)); +#else + Gtk::Label *apply3DLabel = Gtk::manage(new Gtk::Label(_("Refresh perspective"), 0.0, 0.5)); +#endif + + apply3D->add(*apply3DLabel); apply3D->signal_clicked().connect(sigc::bind<Gtk::Entry*>(sigc::mem_fun(*this,&LPEPerspectivePath::refresh),perspective)); Gtk::Widget* apply3DWidget = dynamic_cast<Gtk::Widget *>(apply3D); apply3DWidget->set_tooltip_text("Refresh perspective"); diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp index c6edffd9b..e847494a2 100644 --- a/src/live_effects/lpe-roughen.cpp +++ b/src/live_effects/lpe-roughen.cpp @@ -120,7 +120,7 @@ Gtk::Widget *LPERoughen::newWidget() Gtk::ALIGN_START)); method_label->set_use_markup(true); vbox->pack_start(*method_label, false, false, 2); - vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + vbox->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_EXPAND_WIDGET); } if (param->param_key == "displace_x") { @@ -129,7 +129,7 @@ Gtk::Widget *LPERoughen::newWidget() Gtk::ALIGN_START)); displace_x_label->set_use_markup(true); vbox->pack_start(*displace_x_label, false, false, 2); - vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + vbox->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_EXPAND_WIDGET); } if (param->param_key == "global_randomize") { @@ -138,7 +138,7 @@ Gtk::Widget *LPERoughen::newWidget() Gtk::ALIGN_START)); global_rand->set_use_markup(true); vbox->pack_start(*global_rand, false, false, 2); - vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + vbox->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_EXPAND_WIDGET); } if (param->param_key == "handles") { @@ -147,7 +147,7 @@ Gtk::Widget *LPERoughen::newWidget() Gtk::ALIGN_START)); options->set_use_markup(true); vbox->pack_start(*options, false, false, 2); - vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()), + vbox->pack_start(*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_EXPAND_WIDGET); } Glib::ustring *tip = param->param_getTooltip(); diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp index 1940806bd..83cd6623c 100644 --- a/src/live_effects/lpeobject-reference.cpp +++ b/src/live_effects/lpeobject-reference.cpp @@ -6,9 +6,11 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include "live_effects/lpeobject-reference.h" + #include <string.h> -#include "live_effects/lpeobject-reference.h" +#include "bad-uri-exception.h" #include "live_effects/lpeobject.h" #include "uri.h" diff --git a/src/live_effects/parameter/item.cpp b/src/live_effects/parameter/item.cpp index 8caea4e26..93cf2b15f 100644 --- a/src/live_effects/parameter/item.cpp +++ b/src/live_effects/parameter/item.cpp @@ -5,15 +5,20 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "ui/widget/point.h" +#include "live_effects/parameter/item.h" + #include <glibmm/i18n.h> -#include "live_effects/parameter/item.h" +#include <gtkmm/button.h> +#include <gtkmm/label.h> + +#include "bad-uri-exception.h" +#include "ui/widget/point.h" + #include "live_effects/effect.h" #include "svg/svg.h" #include "widgets/icon.h" -#include <gtk/gtk.h> #include "selection-chemistry.h" #include "xml/repr.h" #include "desktop.h" @@ -25,8 +30,6 @@ // required for linking to other paths #include "uri.h" -#include <gtkmm/button.h> -#include <gtkmm/label.h> #include "ui/icon-names.h" namespace Inkscape { diff --git a/src/live_effects/parameter/originalpath.cpp b/src/live_effects/parameter/originalpath.cpp index f7eb48b7a..1e78f7fe1 100644 --- a/src/live_effects/parameter/originalpath.cpp +++ b/src/live_effects/parameter/originalpath.cpp @@ -89,7 +89,7 @@ OriginalPathParam::linked_modified_callback(SPObject *linked_obj, guint /*flags* { SPCurve *curve = NULL; if (SP_IS_SHAPE(linked_obj)) { - curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE(); + curve = SP_SHAPE(linked_obj)->getCurve(); } if (SP_IS_TEXT(linked_obj)) { curve = SP_TEXT(linked_obj)->getNormalizedBpath(); diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp index 083abc94c..693821ed2 100644 --- a/src/live_effects/parameter/originalpatharray.cpp +++ b/src/live_effects/parameter/originalpatharray.cpp @@ -386,7 +386,7 @@ void OriginalPathArrayParam::setPathVector(SPObject *linked_obj, guint /*flags*/ } SPCurve *curve = NULL; if (SP_IS_SHAPE(linked_obj)) { - curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE(); + curve = SP_SHAPE(linked_obj)->getCurve(); } if (SP_IS_TEXT(linked_obj)) { curve = SP_TEXT(linked_obj)->getNormalizedBpath(); diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp index f0c494267..dafc6d406 100644 --- a/src/live_effects/parameter/path.cpp +++ b/src/live_effects/parameter/path.cpp @@ -5,10 +5,16 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include "ui/widget/point.h" +#include "live_effects/parameter/path.h" + #include <glibmm/i18n.h> -#include "live_effects/parameter/path.h" +#include <gtkmm/button.h> +#include <gtkmm/label.h> + +#include "bad-uri-exception.h" +#include "ui/widget/point.h" + #include "live_effects/effect.h" #include "svg/svg.h" #include <2geom/svg-path-parser.h> @@ -17,7 +23,6 @@ #include <2geom/d2.h> #include "widgets/icon.h" -#include <gtk/gtk.h> #include "selection-chemistry.h" #include "xml/repr.h" #include "desktop.h" @@ -44,8 +49,6 @@ #include "ui/tool/multi-path-manipulator.h" #include "ui/tool/shape-record.h" -#include <gtkmm/button.h> -#include <gtkmm/label.h> #include "ui/icon-names.h" namespace Inkscape { diff --git a/src/main.cpp b/src/main.cpp index 605c1207e..8be9e2e44 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -166,6 +166,7 @@ enum { SP_ARG_SHELL, SP_ARG_VERSION, SP_ARG_VACUUM_DEFS, + SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING, #ifdef WITH_DBUS SP_ARG_DBUS_LISTEN, SP_ARG_DBUS_NAME, @@ -235,7 +236,6 @@ static gchar *sp_xverbs_yaml_utf8 = NULL; static gchar *sp_xverbs_yaml = NULL; #endif // WITH_YAML - /** * Reset variables to default values. */ @@ -274,6 +274,7 @@ static void resetCommandlineGlobals() { sp_query_all = FALSE; sp_query_id = NULL; sp_vacuum_defs = FALSE; + sp_no_convert_text_baseline_spacing = FALSE; #ifdef WITH_DBUS sp_dbus_listen = FALSE; sp_dbus_name = NULL; @@ -525,6 +526,11 @@ struct poptOption options[] = { N_("Start Inkscape in interactive shell mode."), NULL}, + {"no-convert-text-baseline-spacing", 0, + POPT_ARG_NONE, &sp_no_convert_text_baseline_spacing, SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING, + N_("Do not fix legacy (pre-0.92) files' text baseline spacing on opening."), + NULL}, + POPT_AUTOHELP POPT_TABLEEND }; @@ -685,18 +691,22 @@ main(int argc, char **argv) RegistryTool rt; rt.setPathInfo(); } -#elif defined(ENABLE_NLS) -# ifdef ENABLE_BINRELOC + + // disable "client side decorations" as they prevent window borders and titlebars to be drawn with native theming + // see also https://bugzilla.gnome.org/show_bug.cgi?id=778791 + g_setenv("GTK_CSD", "0", FALSE); +#endif + +#ifdef ENABLE_NLS +# ifndef WIN32 +# ifdef ENABLE_BINRELOC bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR("")); -# else +# else bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR); // needed by Python/Gettext g_setenv("PACKAGE_LOCALE_DIR", PACKAGE_LOCALE_DIR, TRUE); +# endif # endif -#endif - - // the bit below compiles regardless of platform -#ifdef ENABLE_NLS // Allow the user to override the locale directory by setting // the environment variable INKSCAPE_LOCALEDIR. char const *inkscape_localedir = g_getenv("INKSCAPE_LOCALEDIR"); @@ -1636,7 +1646,6 @@ static int sp_do_export_png(SPDocument *doc) return retcode; } - /** * Perform a PDF/PS/EPS export * diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index 75302806b..7d00ffa86 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -184,6 +184,7 @@ static char const menus_skeleton[] = " <submenu name=\"" N_("_Object") "\">\n" " <verb verb-id=\"DialogObjects\" />\n" " <verb verb-id=\"DialogTags\" />\n" +" <verb verb-id=\"DialogStyle\" />\n" " <separator/>\n" " <verb verb-id=\"DialogFillStroke\" />\n" " <verb verb-id=\"DialogObjectProperties\" />\n" @@ -224,6 +225,7 @@ static char const menus_skeleton[] = " <verb verb-id=\"DialogTransform\" />\n" " <verb verb-id=\"DialogAlignDistribute\" />\n" " <verb verb-id=\"DialogArrange\" />\n" +" <verb verb-id=\"DialogPrototype\" />\n" " </submenu>\n" " <submenu name=\"" N_("_Path") "\">\n" " <verb verb-id=\"ObjectToPath\" />\n" diff --git a/src/object-set.cpp b/src/object-set.cpp index 65e7d45d6..8fcb4215e 100644 --- a/src/object-set.cpp +++ b/src/object-set.cpp @@ -20,7 +20,7 @@ namespace Inkscape { -bool ObjectSet::add(SPObject* object) { +bool ObjectSet::add(SPObject* object, bool nosignal) { g_return_val_if_fail(object != NULL, false); g_return_val_if_fail(SP_IS_OBJECT(object), false); @@ -37,7 +37,8 @@ bool ObjectSet::add(SPObject* object) { _removeDescendantsFromSet(object); _add(object); - _emitSignals(); + if (!nosignal) + _emitSignals(); return true; } @@ -246,9 +247,10 @@ void ObjectSet::setReprList(std::vector<XML::Node*> const &list) { for (auto iter = list.rbegin(); iter != list.rend(); ++iter) { SPObject *obj = document()->getObjectById((*iter)->attribute("id")); if (obj) { - add(obj); + add(obj, true); } } + _emitSignals(); if(dynamic_cast<Inkscape::Selection*>(this)) return dynamic_cast<Inkscape::Selection*>(this)->_emitChanged();// } diff --git a/src/object-set.h b/src/object-set.h index 7c224f640..02259824c 100644 --- a/src/object-set.h +++ b/src/object-set.h @@ -143,8 +143,9 @@ public: * Add an SPObject to the set of selected objects. * * @param obj the SPObject to add + * @param nosignal true if no signals should be sent */ - bool add(SPObject* object); + bool add(SPObject* object, bool nosignal = false); /** * Add an XML node's SPObject to the set of selected objects. @@ -300,9 +301,10 @@ public: addList(const std::vector<T*> &objs) { for (auto obj: objs) { if (!includes(obj)) { - add(obj); + add(obj, true); } } + _emitSignals(); } /** Returns the bounding rectangle of the selection. */ diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 741f433f2..b66bcf368 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -498,7 +498,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) /* Whole text's style */ Glib::ustring style_str = - item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility + item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility g_repr->setAttribute("style", style_str.c_str()); Inkscape::Text::Layout::iterator iter = te_get_layout(item)->begin(); @@ -519,7 +519,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) pos_obj = pos_obj->parent; // SPStrings don't have style } Glib::ustring style_str = - pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility + pos_obj->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, pos_obj->parent ? pos_obj->parent->style : NULL); // TODO investigate posibility // get path from iter to iter_next: SPCurve *curve = te_get_layout(item)->convertToCurves(iter, iter_next); @@ -579,7 +579,7 @@ sp_selected_item_to_curved_repr(SPItem *item, guint32 /*text_grouping_policy*/) /* Style */ Glib::ustring style_str = - item->style->write( SP_STYLE_FLAG_IFDIFF, item->parent ? item->parent->style : NULL); // TODO investigate posibility + item->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, item->parent ? item->parent->style : NULL); // TODO investigate posibility repr->setAttribute("style", style_str.c_str()); /* Mask */ diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp index 188b7d9c8..4901cf424 100644 --- a/src/resource-manager.cpp +++ b/src/resource-manager.cpp @@ -126,8 +126,22 @@ public: */ std::map<Glib::ustring, Glib::ustring> locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks); + + /** + * Try to parse href into a local filename using standard methods. + * + * @return true if successfull. + */ bool extractFilepath( Glib::ustring const &href, std::string &uri ); + /** + * Try to parse href into a local filename using some non-standard methods. + * This means the href is likely invalid and should be rewritten. + * + * @return true if successfull. + */ + bool reconstructFilepath( Glib::ustring const &href, std::string &uri ); + bool searchUpwards( std::string const &base, std::string const &subpath, std::string &dest ); protected: @@ -156,9 +170,13 @@ bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::strin // TODO debug g_message("--- is a file URI [%s]", href.c_str()); // throws Glib::ConvertError: - uri = Glib::filename_from_uri(href); // TODO see if we can get this to throw - // TODO debug g_message(" [%s]", uri.c_str()); - isFile = true; + try { + uri = Glib::filename_from_uri(href); + // TODO debug g_message(" [%s]", uri.c_str()); + isFile = true; + } catch(Glib::ConvertError e) { + g_warning("%s", e.what().c_str()); + } } } else { // No scheme. Assuming it is a file path (absolute or relative). @@ -170,6 +188,26 @@ bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::strin return isFile; } +bool ResourceManagerImpl::reconstructFilepath( Glib::ustring const &href, std::string &uri ) +{ + bool isFile = false; + + uri.clear(); + + std::string scheme = Glib::uri_parse_scheme(href); + if ( !scheme.empty() ) { + if ( scheme == "file" ) { + // try to build a relative filename for URIs like "file:image.png" + // they're not standard conformant but not uncommon + Glib::ustring href_new = Glib::ustring(href, 5); + uri = Glib::filename_from_utf8(href_new); + // TODO debug g_message("reconstructed path for '%s' into '%s'", href.c_str(), uri.c_str()); + isFile = true; + } + } + return isFile; +} + std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc ) { @@ -192,11 +230,14 @@ std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc } } else { std::string combined = Glib::build_filename(doc->getBase(), uri); - if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { + if ( !Glib::file_test(combined, Glib::FILE_TEST_EXISTS) ) { result.push_back(href); uniques.insert(href); } } + } else if ( reconstructFilepath( href, uri ) ) { + result.push_back(href); + uniques.insert(href); } } } @@ -228,7 +269,7 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us priorLocations.push_back(path); } } catch (Glib::ConvertError e) { - g_warning("Bad URL ignored [%s]", uri.c_str()); + g_warning("%s", e.what().c_str()); } } } @@ -238,7 +279,7 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us // TODO debug g_message("========{%s}", it->c_str()); std::string uri; - if ( extractFilepath( *it, uri ) ) { + if ( extractFilepath( *it, uri ) || reconstructFilepath( *it, uri ) ) { // We were able to get some path. Check it std::string origPath = uri; @@ -247,32 +288,31 @@ std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::us // TODO debug g_message(" not absolute. Fixing up as [%s]", uri.c_str()); } - if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { - // TODO debug g_message(" DOES NOT EXIST."); - std::string remainder; - bool exists = searchUpwards( docbase, origPath, remainder ); + bool exists = Glib::file_test(uri, Glib::FILE_TEST_EXISTS); - if ( !exists ) { - // TODO debug g_message("Expanding the search..."); + // search in parent folders + if (!exists) { + exists = searchUpwards( docbase, origPath, uri ); + } - // Check if the MRU bases point us to it. - if ( !Glib::path_is_absolute(origPath) ) { - for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) { - exists = searchUpwards( *it, origPath, remainder ); - } + // Check if the MRU bases point us to it. + if ( !exists ) { + if ( !Glib::path_is_absolute(origPath) ) { + for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) { + exists = searchUpwards( *it, origPath, uri ); } } + } - if ( exists ) { - if ( Glib::path_is_absolute( remainder ) ) { - // TODO debug g_message("Need to convert to relative if possible [%s]", remainder.c_str()); - remainder = convertPathToRelative( remainder, docbase ); - } - - bool isAbsolute = Glib::path_is_absolute( remainder ); - Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( remainder ) : Glib::filename_to_utf8( remainder ); - result[*it] = replacement; + if ( exists ) { + if ( Glib::path_is_absolute( uri ) ) { + // TODO debug g_message("Need to convert to relative if possible [%s]", uri.c_str()); + uri = convertPathToRelative( uri, docbase ); } + + bool isAbsolute = Glib::path_is_absolute( uri ); + Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( uri ) : Glib::filename_to_utf8( uri ); + result[*it] = replacement; } } } diff --git a/src/round.h b/src/round.h deleted file mode 100644 index 16d280566..000000000 --- a/src/round.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SEEN_ROUND_H -#define SEEN_ROUND_H - -#include <cmath> - -namespace Inkscape { - -/** Returns x rounded to the nearest integer. It is unspecified what happens - if x is half way between two integers: we may in future use rint/round - on platforms that have them. If you depend on a particular rounding - behaviour, then please change this documentation accordingly. -**/ -inline double -round(double const x) -{ - return std::floor( x + .5 ); -} - -} - -#endif /* !SEEN_ROUND_H */ - -/* - Local Variables: - mode:c++ - c-file-style:"stroustrup" - c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) - indent-tabs-mode:nil - fill-column:99 - End: -*/ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 67972cabb..5d585ab5d 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -1618,7 +1618,7 @@ void ObjectSet::applyAffine(Geom::Affine const &affine, bool set_i2d, bool compe for (auto& itm: region.children) { SPUse *use = dynamic_cast<SPUse *>(&itm); if ( use ) { - use->doWriteTransform(use->getRepr(), use->transform.inverse(), NULL, compensate); + use->doWriteTransform(use->getRepr(), item->transform.inverse(), NULL, compensate); } } } @@ -2554,9 +2554,7 @@ void scroll_to_show_item(SPDesktop *desktop, SPItem *item) Geom::Point const d_dt = dbox.midpoint(); Geom::Point const d_w = desktop->d2w(d_dt); Geom::Point const moved_w( d_w - s_w ); - gint const dx = (gint) moved_w[X]; - gint const dy = (gint) moved_w[Y]; - desktop->scroll_world(dx, dy); + desktop->scroll_relative(moved_w); } } @@ -3905,6 +3903,7 @@ void ObjectSet::setClipGroup() || apply_to_layer){ Geom::Affine oldtr=(*i)->transform; + oldtr *= SP_ITEM((*i)->parent)->i2doc_affine().inverse(); (*i)->doWriteTransform((*i)->getRepr(), (*i)->i2doc_affine()); Inkscape::XML::Node *dup = (*i)->getRepr()->duplicate(xml_doc); (*i)->doWriteTransform((*i)->getRepr(), oldtr); diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 7a2a3777f..10b5b2837 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -480,6 +480,8 @@ void Inkscape::SelTrans::ungrab() DocumentUndo::done(_desktop->getDocument(), SP_VERB_CONTEXT_SELECT, _("Skew")); } + } else { + _updateHandles(); } } else { diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 9ce1a3b56..3c3b8abb0 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -1,11 +1,12 @@ +#include "sp-conn-end.h" #include <cstring> #include <string> #include <limits> +#include "bad-uri-exception.h" #include "display/curve.h" #include "xml/repr.h" -#include "sp-conn-end.h" #include "sp-path.h" #include "uri.h" #include "document.h" diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index ed1e2b504..5dfc60f1a 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -39,7 +39,7 @@ SPGenericEllipse::SPGenericEllipse() , start(0) , end(SP_2PI) , type(SP_GENERIC_ELLIPSE_UNDEFINED) - , _closed(true) + , arc_type(SP_GENERIC_ELLIPSE_ARC_TYPE_ARC) { } @@ -47,14 +47,6 @@ SPGenericEllipse::~SPGenericEllipse() { } -void SPGenericEllipse::setClosed(bool value) { - _closed = value; -} - -bool SPGenericEllipse::closed() { - return _closed; -} - void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr) { // std::cout << "SPGenericEllipse::build: Entrance: " << this->type @@ -69,6 +61,7 @@ void SPGenericEllipse::build(SPDocument *document, Inkscape::XML::Node *repr) this->readAttr("sodipodi:start"); this->readAttr("sodipodi:end"); this->readAttr("sodipodi:open"); + this->readAttr("sodipodi:arc-type"); break; case SP_GENERIC_ELLIPSE_CIRCLE: @@ -166,7 +159,25 @@ void SPGenericEllipse::set(unsigned int key, gchar const *value) break; case SP_ATTR_SODIPODI_OPEN: - this->_closed = (!value); + // This is only for reading in old files so rely on constructor to set default. + if (!value) { // Only set if not "true" + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE; + } + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + break; + + case SP_ATTR_SODIPODI_ARC_TYPE: + // To read in old files that use 'open', we need to not set if value is null. + // We could also check inkscape version. + if (value) { + if (!strcmp(value,"arc")) { + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_ARC; + } else if (!strcmp(value,"chord")) { + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD; + } else { + this->arc_type = SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE; + } + } this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); break; @@ -285,11 +296,28 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I sp_repr_set_svg_double(repr, "sodipodi:start", start); sp_repr_set_svg_double(repr, "sodipodi:end", end); - repr->setAttribute("sodipodi:open", (!_closed) ? "true" : NULL); + switch ( arc_type ) { + case SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE: + repr->setAttribute("sodipodi:open", NULL); // For backwards compat. + repr->setAttribute("sodipodi:arc-type", "slice"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD: + // A chord's path isn't "open" but its fill most closely resembles an arc. + repr->setAttribute("sodipodi:open", "true"); // For backwards compat. + repr->setAttribute("sodipodi:arc-type", "chord"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_ARC: + repr->setAttribute("sodipodi:open", "true"); // For backwards compat. + repr->setAttribute("sodipodi:arc-type", "arc"); + break; + default: + std::cerr << "SPGenericEllipse::write: unknown arc-type." << std::endl; + } } else { repr->setAttribute("sodipodi:end", NULL); repr->setAttribute("sodipodi:start", NULL); repr->setAttribute("sodipodi:open", NULL); + repr->setAttribute("sodipodi:arc-type", NULL); } } @@ -310,6 +338,7 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I repr->setAttribute("sodipodi:end", NULL ); repr->setAttribute("sodipodi:start", NULL ); repr->setAttribute("sodipodi:open", NULL ); + repr->setAttribute("sodipodi:arc-type", NULL); repr->setAttribute("sodipodi:type", NULL ); repr->setAttribute("d", NULL ); break; @@ -327,6 +356,7 @@ Inkscape::XML::Node *SPGenericEllipse::write(Inkscape::XML::Document *xml_doc, I repr->setAttribute("sodipodi:end", NULL ); repr->setAttribute("sodipodi:start", NULL ); repr->setAttribute("sodipodi:open", NULL ); + repr->setAttribute("sodipodi:arc-type", NULL); repr->setAttribute("sodipodi:type", NULL ); repr->setAttribute("d", NULL ); break; @@ -350,10 +380,16 @@ const char *SPGenericEllipse::displayName() const case SP_GENERIC_ELLIPSE_ARC: if (_isSlice()) { - if (_closed) { - return _("Segment"); - } else { - return _("Arc"); + switch ( arc_type ) { + case SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE: + return _("Slice"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD: + return _("Chord"); + break; + case SP_GENERIC_ELLIPSE_ARC_TYPE_ARC: + return _("Arc"); + break; } } else { return _("Ellipse"); @@ -419,10 +455,10 @@ void SPGenericEllipse::set_shape() } Geom::PathBuilder pb; pb.append(path); - if (this->_isSlice() && this->_closed) { + if (this->_isSlice() && this->arc_type == SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE) { pb.lineTo(Geom::Point(0, 0)); } - if (this->_closed) { + if ( !(this->arc_type == SP_GENERIC_ELLIPSE_ARC_TYPE_ARC) ) { pb.closePath(); } else { pb.flush(); @@ -543,7 +579,8 @@ void SPGenericEllipse::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, bool slice = this->_isSlice(); // Add the centre, if we have a closed slice or when explicitly asked for - if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && this->_closed) { + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_NODE_CUSP) && slice && + this->arc_type == SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE) { Geom::Point pt = Geom::Point(cx, cy) * i2dt; p.push_back(Inkscape::SnapCandidatePoint(pt, Inkscape::SNAPSOURCE_NODE_CUSP, Inkscape::SNAPTARGET_NODE_CUSP)); } @@ -654,7 +691,7 @@ void SPGenericEllipse::position_set(gdouble x, gdouble y, gdouble rx, gdouble ry this->end = Geom::Angle::from_degrees(prefs->getDouble("/tools/shapes/arc/end", 0.0)).radians0(); } - this->_closed = !prefs->getBool("/tools/shapes/arc/open"); + this->arc_type = (GenericEllipseArcType)prefs->getInt("/tools/shapes/arc/arc_type", 0); this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/sp-ellipse.h b/src/sp-ellipse.h index 09768fd6d..dafece625 100644 --- a/src/sp-ellipse.h +++ b/src/sp-ellipse.h @@ -30,6 +30,12 @@ enum GenericEllipseType { SP_GENERIC_ELLIPSE_ELLIPSE }; +enum GenericEllipseArcType { + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE, // Default + SP_GENERIC_ELLIPSE_ARC_TYPE_CHORD, + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC +}; + class SPGenericEllipse : public SPShape { public: SPGenericEllipse(); @@ -42,14 +48,13 @@ public: SVGLength rx; SVGLength ry; - /** - * If we have a slice, returns whether the shape is closed ("pizza slice") or not (arc only). - */ - bool closed(); - void setClosed(bool value); + // Return slice, chord, or arc. + GenericEllipseArcType arcType() { return arc_type; }; + void setArcType(GenericEllipseArcType type) { arc_type = type; }; double start, end; GenericEllipseType type; + GenericEllipseArcType arc_type; virtual void build(SPDocument *document, Inkscape::XML::Node *repr); @@ -83,8 +88,6 @@ protected: * @brief Determines whether the shape is a part of an ellipse. */ bool _isSlice() const; - - bool _closed; }; #endif diff --git a/src/sp-filter.cpp b/src/sp-filter.cpp index 26ea03c6c..052fa7fe2 100644 --- a/src/sp-filter.cpp +++ b/src/sp-filter.cpp @@ -17,21 +17,25 @@ #include <config.h> #endif +#include "sp-filter.h" + #include <map> #include <string.h> -using std::map; -using std::pair; #include <glibmm.h> + +#include "bad-uri-exception.h" #include "attributes.h" #include "display/nr-filter.h" #include "document.h" -#include "sp-filter.h" #include "sp-filter-reference.h" #include "sp-filter-primitive.h" #include "uri.h" #include "xml/repr.h" +using std::map; +using std::pair; + #define SP_MACROS_SILENT static void filter_ref_changed(SPObject *old_ref, SPObject *ref, SPFilter *filter); diff --git a/src/sp-flowtext.cpp b/src/sp-flowtext.cpp index 264558014..d79652bf4 100644 --- a/src/sp-flowtext.cpp +++ b/src/sp-flowtext.cpp @@ -535,7 +535,7 @@ Inkscape::XML::Node *SPFlowtext::getAsText() this->layout.getSourceOfCharacter(it, &rawptr, &span_text_start_iter); SPObject *source_obj = reinterpret_cast<SPObject *>(rawptr); - Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, this->style); + Glib::ustring style_text = (dynamic_cast<SPString *>(source_obj) ? source_obj->parent : source_obj)->style->write( SP_STYLE_FLAG_IFDIFF, SP_STYLE_SRC_UNSET, this->style); if (!style_text.empty()) { span_tspan->setAttribute("style", style_text.c_str()); } diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp index f02149cbb..c1934dd92 100644 --- a/src/sp-gradient.cpp +++ b/src/sp-gradient.cpp @@ -24,6 +24,8 @@ #define noSP_GRADIENT_VERBOSE //#define OBJECT_TRACE +#include "sp-gradient.h" + #include <cstring> #include <string> @@ -34,12 +36,12 @@ #include <sigc++/functors/ptr_fun.h> #include <sigc++/adaptors/bind.h> +#include "bad-uri-exception.h" #include "display/cairo-utils.h" #include "svg/svg.h" #include "svg/css-ostringstream.h" #include "attributes.h" #include "document-private.h" -#include "sp-gradient.h" #include "gradient-chemistry.h" #include "sp-gradient-reference.h" #include "sp-linear-gradient.h" diff --git a/src/sp-hatch.cpp b/src/sp-hatch.cpp index 02d95c75c..f1958a53b 100644 --- a/src/sp-hatch.cpp +++ b/src/sp-hatch.cpp @@ -12,11 +12,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "sp-hatch.h" + #include <cstring> #include <string> + #include <2geom/transforms.h> #include <sigc++/functors/mem_fun.h> +#include "bad-uri-exception.h" #include "svg/svg.h" #include "display/cairo-utils.h" #include "display/drawing-context.h" @@ -25,7 +29,6 @@ #include "display/drawing-pattern.h" #include "attributes.h" #include "document-private.h" -#include "sp-hatch.h" #include "sp-hatch-path.h" SPHatch::SPHatch() diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 5d02020c6..e2f678957 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -17,6 +17,10 @@ #endif #include "sp-item.h" + +#include <glibmm/i18n.h> + +#include "bad-uri-exception.h" #include "svg/svg.h" #include "print.h" #include "display/drawing-item.h" @@ -25,10 +29,7 @@ #include "uri.h" #include "inkscape.h" #include "desktop.h" - - #include "style.h" -#include <glibmm/i18n.h> #include "sp-root.h" #include "sp-clippath.h" #include "sp-mask.h" diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 00671b936..ca0f78a00 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -15,10 +15,11 @@ #ifdef HAVE_CONFIG_H #endif -#include "ui/tool/multi-path-manipulator.h" - #include <glibmm/i18n.h> +#include "bad-uri-exception.h" +#include "ui/tool/multi-path-manipulator.h" + #include "live_effects/effect.h" #include "live_effects/lpe-path_length.h" #include "live_effects/lpeobject.h" diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp index f192d0e44..c89cfcc6f 100644 --- a/src/sp-mesh-array.cpp +++ b/src/sp-mesh-array.cpp @@ -1525,18 +1525,6 @@ double hermite( const double p0, const double p1, const double m0, const double class SPMeshSmoothCorner { - enum { - AMP, - DX_LEFT, - DX_RIGHT, - DY_TOP, - DY_BOTTOM, - DXY_LT, - DXY_RT, - DXY_LB, - DXY_RB - }; - public: SPMeshSmoothCorner() { for( unsigned i = 0; i < 3; ++i ) { diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index c9e71955f..403b8a41b 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -754,7 +754,7 @@ void SPNamedView::show(SPDesktop *desktop) namespace { -gint const MIN_ONSCREEN_DISTANCE = 50; +gint const MIN_ONSCREEN_DISTANCE = 100; gdouble const NEWDOC_X_SCALE = 0.75; gdouble const NEWDOC_Y_SCALE = NEWDOC_X_SCALE; @@ -774,12 +774,6 @@ Geom::Point calcAnchorPoint(gint const x, gint const y, } // namespace -void SPNamedView::writeNewGrid(SPDocument *document,int gridtype) -{ - g_assert(this->getRepr() != NULL); - Inkscape::CanvasGrid::writeNewGridToRepr(this->getRepr(),document,static_cast<Inkscape::GridType>(gridtype)); -} - /* * Restores window geometry from the document settings or defaults in prefs */ @@ -788,22 +782,42 @@ void sp_namedview_window_from_document(SPDesktop *desktop) SPNamedView *nv = desktop->namedview; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool geometry_from_file = (1 == prefs->getInt("/options/savewindowgeometry/value", 0)); + bool geometry_from_last = (2 == prefs->getInt("/options/savewindowgeometry/value", 0)); gint default_geometry = prefs->getInt("/options/defaultwindowsize/value", 1); bool new_document = (nv->window_width <= 0) || (nv->window_height <= 0); bool show_dialogs = true; // restore window size and position stored with the document - bool sizeSet = false; - - if ((geometry_from_file && nv->window_maximized) || (new_document && (default_geometry == 2))) { + if (geometry_from_last) { + // do nothing, as we already have code for that in interface.cpp + // TODO: Probably should not do similar things in two places + } else if ((geometry_from_file && nv->window_maximized) || (new_document && (default_geometry == 2))) { Gtk::Window *win = desktop->getToplevel(); if (win) { win->maximize(); } - sizeSet = true; - } else if (geometry_from_file && !nv->window_maximized) { - gint w = MIN(gdk_screen_width(), nv->window_width); - gint h = MIN(gdk_screen_height(), nv->window_height); + } else { + // gdk_screen_width() / gdk_screen_height() return the dimensions of all displays combined + // therefore we have to get the dimensions of one monitor explicitly (currently the primary monitor) + // TODO: account for multi-monitor setups (i.e. on which monitor do we want to display Inkscape?) + gint monitor_number; + GdkRectangle monitor_geometry; + monitor_number = gdk_screen_get_primary_monitor(gdk_screen_get_default()); + gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor_number, &monitor_geometry); + + gint w = monitor_geometry.width; + gint h = monitor_geometry.height; + bool move_to_screen = false; + if (geometry_from_file and !new_document) { + w = MIN(w, nv->window_width); + h = MIN(h, nv->window_height); + move_to_screen = true; + } else if (default_geometry == 1) { + w *= NEWDOC_X_SCALE; + h *= NEWDOC_Y_SCALE; + } else if (default_geometry == 0) { + w = h = 0; // use the smallest possible window size; could be a factor like NEWDOC_X_SCALE in future + } if ((w > 0) && (h > 0)) { #ifndef WIN32 gint dx= 0; @@ -817,39 +831,37 @@ void sp_namedview_window_from_document(SPDesktop *desktop) show_dialogs = FALSE; } #endif - Geom::Point origin = calcAnchorPoint(nv->window_x, nv->window_y, w, h, MIN_ONSCREEN_DISTANCE); desktop->setWindowSize(w, h); - desktop->setWindowPosition(origin); - sizeSet = true; + if (move_to_screen) { + Geom::Point origin = calcAnchorPoint(nv->window_x, nv->window_y, w, h, MIN_ONSCREEN_DISTANCE); + desktop->setWindowPosition(origin); + } } } - if (!sizeSet && new_document && (default_geometry == 1)) - { - gint w = gdk_screen_width() * NEWDOC_X_SCALE; - gint h = gdk_screen_height() * NEWDOC_Y_SCALE; - Geom::Point origin = calcAnchorPoint(nv->window_x, nv->window_y, w, h, MIN_ONSCREEN_DISTANCE); - desktop->setWindowSize(w, h); - desktop->setWindowPosition(origin); - } + // Cancel any history of transforms up to this point (must be before call to zoom). + desktop->clear_transform_history(); // restore zoom and view if (nv->zoom != 0 && nv->zoom != HUGE_VAL && !IS_NAN(nv->zoom) && nv->cx != HUGE_VAL && !IS_NAN(nv->cx) && nv->cy != HUGE_VAL && !IS_NAN(nv->cy)) { - desktop->zoom_absolute(nv->cx, nv->cy, nv->zoom); + desktop->zoom_absolute_center_point( Geom::Point(nv->cx, nv->cy), nv->zoom ); } else if (desktop->getDocument()) { // document without saved zoom, zoom to its page desktop->zoom_page(); } - // cancel any history of zooms up to this point - desktop->zooms_past.clear(); - if (show_dialogs) { desktop->show_dialogs(); } } +void SPNamedView::writeNewGrid(SPDocument *document,int gridtype) +{ + g_assert(this->getRepr() != NULL); + Inkscape::CanvasGrid::writeNewGridToRepr(this->getRepr(),document,static_cast<Inkscape::GridType>(gridtype)); +} + bool SPNamedView::getSnapGlobal() const { return this->snap_manager.snapprefs.getSnapEnabledGlobally(); @@ -958,26 +970,26 @@ static void sp_namedview_lock_guides(SPNamedView *nv) } } -void sp_namedview_doc_rotate_guides(SPNamedView *nv) -{ - bool saved = DocumentUndo::getUndoSensitive(nv->document); - DocumentUndo::setUndoSensitive(nv->document, false); - SPRoot * root = nv->document->getRoot(); - Geom::Point page_center = root->viewBox.midpoint() * root->vbt; - Geom::Affine rot = Geom::identity(); - rot *= Geom::Translate(page_center).inverse(); - rot *= Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); - rot *= Geom::Translate(page_center); - for(std::vector<SPGuide *>::iterator it=nv->guides.begin();it!=nv->guides.end();++it ) { - Geom::Point const on_line = (*it)->getPoint() * rot ; - (*it)->moveto(on_line, true); - Geom::Affine rot_normal_affine = Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); - Geom::Point const rot_normal = (*it)->getNormal() * rot_normal_affine; - (*it)->set_normal(rot_normal, true); - } - DocumentUndo::setUndoSensitive(nv->document, saved); - nv->document->setModifiedSinceSave(); -} +//void sp_namedview_doc_rotate_guides(SPNamedView *nv) +//{ +// bool saved = DocumentUndo::getUndoSensitive(nv->document); +// DocumentUndo::setUndoSensitive(nv->document, false); +// SPRoot * root = nv->document->getRoot(); +// Geom::Point page_center = root->viewBox.midpoint() * root->vbt; +// Geom::Affine rot = Geom::identity(); +// rot *= Geom::Translate(page_center).inverse(); +// rot *= Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); +// rot *= Geom::Translate(page_center); +// for(std::vector<SPGuide *>::iterator it=nv->guides.begin();it!=nv->guides.end();++it ) { +// Geom::Point const on_line = (*it)->getPoint() * rot ; +// (*it)->moveto(on_line, true); +// Geom::Affine rot_normal_affine = Geom::Rotate(Geom::rad_from_deg((nv->document_rotation - root->get_rotation()) * -1)); +// Geom::Point const rot_normal = (*it)->getNormal() * rot_normal_affine; +// (*it)->set_normal(rot_normal, true); +// } +// DocumentUndo::setUndoSensitive(nv->document, saved); +// nv->document->setModifiedSinceSave(); +//} void sp_namedview_set_document_rotation(SPNamedView *nv) { @@ -1005,7 +1017,7 @@ void sp_namedview_set_document_rotation(SPNamedView *nv) sp_canvas_item_affine_absolute(canvas_border, rot * root->vbt); nv->page_border_rotated = desktop->add_temporary_canvasitem(canvas_border, 0); } - sp_namedview_doc_rotate_guides(nv); + //sp_namedview_doc_rotate_guides(nv); nv->document->getRoot()->set_rotation(nv->document_rotation); c->unref(); } @@ -1268,7 +1280,7 @@ void SPNamedView::translateGrids(Geom::Translate const &tr) { void SPNamedView::scrollAllDesktops(double dx, double dy, bool is_scrolling) { for(std::vector<SPDesktop *>::iterator it=this->views.begin();it!=this->views.end();++it ) { - (*it)->scroll_world_in_svg_coords(dx, dy, is_scrolling); + (*it)->scroll_relative_in_svg_coords(dx, dy, is_scrolling); } } diff --git a/src/sp-object.cpp b/src/sp-object.cpp index 0dc301c49..9f15935ac 100644 --- a/src/sp-object.cpp +++ b/src/sp-object.cpp @@ -1061,7 +1061,9 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML } if (style) { - Glib::ustring s = style->write(SP_STYLE_FLAG_IFSET); + // Write if property set by style attribute in this object + Glib::ustring s = + style->write(SP_STYLE_FLAG_IFSET | SP_STYLE_FLAG_IFSRC, SP_STYLE_SRC_STYLE_PROP); // Check for valid attributes. This may be time consuming. // It is useful, though, for debugging Inkscape code. @@ -1103,7 +1105,10 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML * possibly we should write property attributes instead of a style * attribute. */ - sp_style_unset_property_attrs (this); + // With the changes to preserves style source this is no longer needed + // and the above comment no longer applies. I leave it here until these + // change are well tested. + // sp_style_unset_property_attrs (this); } #ifdef OBJECT_TRACE diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp index 9e2264d76..e5b1b5d69 100644 --- a/src/sp-offset.cpp +++ b/src/sp-offset.cpp @@ -18,13 +18,17 @@ #include <config.h> #endif +#include "sp-offset.h" + #include <cstring> #include <string> +#include <glibmm/i18n.h> + +#include "bad-uri-exception.h" #include "svg/svg.h" #include "attributes.h" #include "display/curve.h" -#include <glibmm/i18n.h> #include "livarot/Path.h" #include "livarot/Shape.h" @@ -32,7 +36,6 @@ #include "enums.h" #include "preferences.h" #include "sp-text.h" -#include "sp-offset.h" #include "sp-use-reference.h" #include "uri.h" diff --git a/src/sp-path.cpp b/src/sp-path.cpp index b593b7937..c6ec5559e 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -292,9 +292,14 @@ Geom::Affine SPPath::set_transform(Geom::Affine const &transform) { // Transform the original-d path if this is a valid LPE this, other else the (ordinary) path if (_curve_before_lpe && hasPathEffectRecursive()) { - if (this->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL) || this->hasPathEffectOfType(Inkscape::LivePathEffect::BEND_PATH)) { + if (this->hasPathEffectOfType(Inkscape::LivePathEffect::CLONE_ORIGINAL) || + this->hasPathEffectOfType(Inkscape::LivePathEffect::BEND_PATH) || + this->hasPathEffectOfType(Inkscape::LivePathEffect::FILL_BETWEEN_MANY) || + this->hasPathEffectOfType(Inkscape::LivePathEffect::FILL_BETWEEN_STROKES) ) + { // if path has the CLONE_ORIGINAL LPE applied, don't write the transform to the pathdata, but write it 'unoptimized' // also if the effect is type BEND PATH to fix bug #179842 + this->adjust_livepatheffect(transform); return transform; } else { _curve_before_lpe->transform(transform); diff --git a/src/sp-pattern.cpp b/src/sp-pattern.cpp index 9d6296a0d..377c035fc 100644 --- a/src/sp-pattern.cpp +++ b/src/sp-pattern.cpp @@ -16,11 +16,14 @@ #include <config.h> #endif +#include "sp-pattern.h" + #include <cstring> #include <string> #include <glibmm.h> #include <2geom/transforms.h> +#include "bad-uri-exception.h" #include "svg/svg.h" #include "display/cairo-utils.h" #include "display/drawing-context.h" @@ -29,7 +32,6 @@ #include "display/drawing-group.h" #include "attributes.h" #include "document-private.h" -#include "sp-pattern.h" #include "sp-factory.h" diff --git a/src/sp-style-elem.cpp b/src/sp-style-elem.cpp index 80e45677c..c15f810e9 100644 --- a/src/sp-style-elem.cpp +++ b/src/sp-style-elem.cpp @@ -3,6 +3,7 @@ #include "xml/repr.h" #include "document.h" #include "sp-style-elem.h" +#include "sp-root.h" #include "attributes.h" #include "style.h" using Inkscape::XML::TEXT_NODE; @@ -64,6 +65,7 @@ content_changed_cb(Inkscape::XML::Node *, gchar const *, gchar const *, SPObject *obj = reinterpret_cast<SPObject *>(data); g_assert(data != NULL); obj->read_content(); + obj->document->getRoot()->emitModified( SP_OBJECT_MODIFIED_CASCADE ); } static void @@ -249,6 +251,19 @@ property_cb(CRDocHandler *const a_handler, g_return_if_fail(append_status == CR_OK); } +void update_style_recursively( SPObject *object ) { + if (object) { + // std::cout << "update_style_recursively: " + // << (object->getId()?object->getId():"null") << std::endl; + if (object->style) { + object->style->readFromObject( object ); + } + for (auto& child : object->children) { + update_style_recursively( &child ); + } + } +} + void SPStyleElem::read_content() { /* fixme: If there's more than one <style> element in a document, then the document stylesheet * will be set to a random one of them, even switching between them. @@ -315,9 +330,13 @@ void SPStyleElem::read_content() { // the <style> is a child of the object that uses a style from it. It just forces the parent of // <style> to reread its style as soon as the stylesheet is fully loaded. Naturally, this won't // work if the user of the stylesheet is its grandparent or precedent. - if ( parent ) { - parent->style->readFromObject( parent ); - } + // if ( parent ) { + // parent->style->readFromObject( parent ); + // } + + // If style sheet has changed, we need to cascade the entire object tree, top down + // Get root, read style, loop through children + update_style_recursively( (SPObject *)document->getRoot() ); } /** diff --git a/src/sp-tag-use-reference.cpp b/src/sp-tag-use-reference.cpp index cca24ed85..bb03c120a 100644 --- a/src/sp-tag-use-reference.cpp +++ b/src/sp-tag-use-reference.cpp @@ -6,11 +6,12 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include "sp-tag-use-reference.h" + #include <cstring> #include <string> -#include "sp-tag-use-reference.h" - +#include "bad-uri-exception.h" #include "livarot/Path.h" #include "preferences.h" #include "sp-shape.h" diff --git a/src/sp-tag-use.cpp b/src/sp-tag-use.cpp index 935f7429e..1312b923f 100644 --- a/src/sp-tag-use.cpp +++ b/src/sp-tag-use.cpp @@ -10,10 +10,14 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "sp-tag-use.h" + #include <cstring> #include <string> #include <glibmm/i18n.h> + +#include "bad-uri-exception.h" #include "display/drawing-group.h" #include "attributes.h" #include "document.h" @@ -23,7 +27,6 @@ #include "style.h" #include "sp-factory.h" #include "sp-symbol.h" -#include "sp-tag-use.h" #include "sp-tag-use-reference.h" SPTagUse::SPTagUse() diff --git a/src/sp-tag-use.h b/src/sp-tag-use.h index 3f238d654..651c8f045 100644 --- a/src/sp-tag-use.h +++ b/src/sp-tag-use.h @@ -22,6 +22,7 @@ #define SP_TAG_USE(obj) (dynamic_cast<SPTagUse*> (obj)) #define SP_IS_TAG_USE(obj) (dynamic_cast<SPTagUse*> (obj) != NULL) +class SPItem; class SPTagUse; class SPTagUseReference; diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp index e25ddb5a4..014876478 100644 --- a/src/sp-tref.cpp +++ b/src/sp-tref.cpp @@ -16,13 +16,15 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "sp-tref.h" + #include <glibmm/i18n.h> +#include "bad-uri-exception.h" #include "attributes.h" #include "document.h" #include "sp-factory.h" #include "sp-text.h" -#include "sp-tref.h" #include "style.h" #include "text-editing.h" diff --git a/src/sp-use-reference.cpp b/src/sp-use-reference.cpp index 3dd63df40..ea8bc5e86 100644 --- a/src/sp-use-reference.cpp +++ b/src/sp-use-reference.cpp @@ -7,11 +7,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information. */ +#include "sp-use-reference.h" + #include <cstring> #include <string> +#include "bad-uri-exception.h" #include "enums.h" -#include "sp-use-reference.h" #include "display/curve.h" #include "livarot/Path.h" diff --git a/src/sp-use.cpp b/src/sp-use.cpp index 6ba03dad9..648852961 100644 --- a/src/sp-use.cpp +++ b/src/sp-use.cpp @@ -19,6 +19,8 @@ #include <2geom/transforms.h> #include <glibmm/i18n.h> #include <glibmm/markup.h> + +#include "bad-uri-exception.h" #include "display/drawing-group.h" #include "attributes.h" #include "document.h" diff --git a/src/splivarot.cpp b/src/splivarot.cpp index 531a48e44..22efe38ed 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -1196,13 +1196,19 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) // remember old stroke style, to be set on fill SPStyle *i_style = item->style; //Stroke - and markers + gchar const *opacity; + gchar const *filter; + SPCSSAttr *ncss = 0; { ncss = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS); gchar const *s_val = sp_repr_css_property(ncss, "stroke", NULL); gchar const *s_opac = sp_repr_css_property(ncss, "stroke-opacity", NULL); - + opacity = sp_repr_css_property(ncss, "opacity", NULL); + filter = sp_repr_css_property(ncss, "filter", NULL); sp_repr_css_set_property(ncss, "stroke", "none"); + sp_repr_css_set_property(ncss, "filter", NULL); + sp_repr_css_set_property(ncss, "opacity", NULL); sp_repr_css_set_property(ncss, "stroke-opacity", "1.0"); sp_repr_css_set_property(ncss, "fill", s_val); if ( s_opac ) { @@ -1220,6 +1226,8 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) ncsf = sp_css_attr_from_style(i_style, SP_STYLE_FLAG_ALWAYS); sp_repr_css_set_property(ncsf, "stroke", "none"); sp_repr_css_set_property(ncsf, "stroke-opacity", "1.0"); + sp_repr_css_set_property(ncsf, "filter", NULL); + sp_repr_css_set_property(ncsf, "opacity", NULL); sp_repr_css_unset_property(ncsf, "marker-start"); sp_repr_css_unset_property(ncsf, "marker-mid"); sp_repr_css_unset_property(ncsf, "marker-end"); @@ -1504,7 +1512,11 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) gchar const *paint_order = sp_repr_css_property(ncss, "paint-order", NULL); SPIPaintOrder temp; temp.read( paint_order ); - if (temp.layer[0] != SP_CSS_PAINT_ORDER_NORMAL && !legacy) { + bool unique = false; + if ((!fill && !markers) || (!fill && !stroke) || (!markers && !stroke)) { + unique = true; + } + if (temp.layer[0] != SP_CSS_PAINT_ORDER_NORMAL && !legacy && !unique) { if (temp.layer[0] == SP_CSS_PAINT_ORDER_FILL) { if (temp.layer[1] == SP_CSS_PAINT_ORDER_STROKE) { @@ -1574,7 +1586,7 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) } } - } else { + } else if (!unique) { if ( fill ) { g_repr->appendChild(fill); } @@ -1588,35 +1600,41 @@ sp_item_path_outline(SPItem *item, SPDesktop *desktop, bool legacy) if( fill || stroke || markers ) { did = true; } + Inkscape::XML::Node *out = NULL; - if (!fill && !markers) { + if (!fill && !markers && did) { out = stroke; - parent->mergeFrom(g_repr, ""); - parent->removeChild(g_repr); - } else if (!fill && !stroke) { + } else if (!fill && !stroke && did) { out = markers; - parent->mergeFrom(g_repr, ""); - parent->removeChild(g_repr); - } else if (!markers && !stroke) { + } else if (!markers && !stroke && did) { out = fill; - parent->mergeFrom(g_repr, ""); - parent->removeChild(g_repr); - } else { + } else if(did) { out = g_repr; } - + SPCSSAttr *r_style = sp_repr_css_attr_new(); + sp_repr_css_set_property(r_style, "opacity", opacity); + sp_repr_css_set_property(r_style, "filter", filter); + sp_repr_css_change(out, r_style, "style"); + sp_repr_css_attr_unref(r_style); + if (unique) { + parent->appendChild(out); + parent->removeChild(g_repr); + out->setPosition(pos > 0 ? pos : 0); + } + out->setAttribute("transform", item->getRepr()->attribute("transform")); //bug lp:1290573 : completely destroy the old object first curve->unref(); //Check for recursive markers to path - if( selection->includes(item) ){ - selection->remove(item); - item->deleteObject(false); - selection->add(out); - } else { - item->deleteObject(false); + if (did) { + if( selection->includes(item) ){ + selection->remove(item); + item->deleteObject(false); + selection->add(out); + } else { + item->deleteObject(false); + } + Inkscape::GC::release(g_repr); } - Inkscape::GC::release(g_repr); - } } diff --git a/src/style-internal.cpp b/src/style-internal.cpp index 5a2a2f913..54d1a0867 100644 --- a/src/style-internal.cpp +++ b/src/style-internal.cpp @@ -27,6 +27,10 @@ #endif #include "style-internal.h" + +#include <glibmm/regex.h> + +#include "bad-uri-exception.h" #include "style.h" #include "svg/svg.h" @@ -40,8 +44,6 @@ #include "svg/css-ostringstream.h" #include "util/units.h" -#include <glibmm/regex.h> - // TODO REMOVE OR MAKE MEMBER FUNCTIONS void sp_style_fill_paint_server_ref_changed( SPObject *old_ref, SPObject *ref, SPStyle *style); void sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style); @@ -53,6 +55,20 @@ using Inkscape::CSSOStringStream; // SPIBase -------------------------------------------------------------- +// Standard criteria for writing a property +// dfp == different from parent +inline bool should_write( guint const flags, bool set, bool dfp, bool src) { + + bool should_write = false; + if ( ((flags & SP_STYLE_FLAG_ALWAYS)) || + ((flags & SP_STYLE_FLAG_IFSET) && set && src) || + ((flags & SP_STYLE_FLAG_IFDIFF) && set && src && dfp)) { + should_write = true; + } + return should_write; +} + + // SPIFloat ------------------------------------------------------------- @@ -75,14 +91,12 @@ SPIFloat::read( gchar const *str ) { } const Glib::ustring -SPIFloat::write( guint const flags, SPIBase const *const base) const { +SPIFloat::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIFloat const *const my_base = dynamic_cast<const SPIFloat*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -151,14 +165,12 @@ SPIScale24::read( gchar const *str ) { } const Glib::ustring -SPIScale24::write( guint const flags, SPIBase const *const base) const { +SPIScale24::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIScale24 const *const my_base = dynamic_cast<const SPIScale24*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -303,14 +315,12 @@ SPILength::read( gchar const *str ) { } const Glib::ustring -SPILength::write( guint const flags, SPIBase const *const base) const { +SPILength::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPILength const *const my_base = dynamic_cast<const SPILength*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -460,18 +470,16 @@ SPILengthOrNormal::read( gchar const *str ) { }; const Glib::ustring -SPILengthOrNormal::write( guint const flags, SPIBase const *const base) const { +SPILengthOrNormal::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPILength const *const my_base = dynamic_cast<const SPILength*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->normal) { return (name + ":normal;"); } else { - return SPILength::write(flags, base); + return SPILength::write(flags, style_src_req, base); } } return Glib::ustring(""); @@ -547,14 +555,12 @@ SPIEnum::read( gchar const *str ) { } const Glib::ustring -SPIEnum::write( guint const flags, SPIBase const *const base) const { +SPIEnum::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -686,14 +692,12 @@ SPIEnumBits::read( gchar const *str ) { } const Glib::ustring -SPIEnumBits::write( guint const flags, SPIBase const *const base) const { +SPIEnumBits::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -757,14 +761,12 @@ SPILigatures::read( gchar const *str ) { } const Glib::ustring -SPILigatures::write( guint const flags, SPIBase const *const base) const { +SPILigatures::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -858,14 +860,12 @@ SPINumeric::read( gchar const *str ) { } const Glib::ustring -SPINumeric::write( guint const flags, SPIBase const *const base) const { +SPINumeric::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } @@ -929,14 +929,12 @@ SPIString::read( gchar const *str ) { // This routine is actually rarely used. Writing is done usually // in sp_repr_css_write_string... const Glib::ustring -SPIString::write( guint const flags, SPIBase const *const base) const { +SPIString::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIString const *const my_base = dynamic_cast<const SPIString*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this) ); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else { @@ -1040,14 +1038,12 @@ void SPIColor::read( gchar const *str ) { } const Glib::ustring -SPIColor::write( guint const flags, SPIBase const *const base) const { +SPIColor::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIColor const *const my_base = dynamic_cast<const SPIColor*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->currentcolor) { @@ -1269,14 +1265,12 @@ SPIPaint::read( gchar const *str, SPStyle &style_in, SPDocument *document_in ) { } const Glib::ustring -SPIPaint::write( guint const flags, SPIBase const *const base) const { +SPIPaint::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIPaint const *const my_base = dynamic_cast<const SPIPaint*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -1541,14 +1535,12 @@ SPIPaintOrder::read( gchar const *str ) { } const Glib::ustring -SPIPaintOrder::write( guint const flags, SPIBase const *const base) const { +SPIPaintOrder::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIPaintOrder const *const my_base = dynamic_cast<const SPIPaintOrder*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -1689,14 +1681,14 @@ SPIFilter::read( gchar const *str ) { } } -const Glib::ustring SPIFilter::write( guint const flags, SPIBase const *const /*base*/) const +const Glib::ustring SPIFilter::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const /*base*/) const { // TODO: fix base //SPILength const *const my_base = dynamic_cast<const SPILength*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set)) - { + // bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool dfp = true; + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else if(this->href && this->href->getURI()) { @@ -1828,14 +1820,12 @@ SPIDashArray::read( gchar const *str ) { } const Glib::ustring -SPIDashArray::write( guint const flags, SPIBase const *const base) const { +SPIDashArray::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIDashArray const *const my_base = dynamic_cast<const SPIDashArray*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { if (this->inherit) { return (name + ":inherit;"); } else if (this->values.empty() ) { @@ -1945,14 +1935,12 @@ SPIFontSize::read( gchar const *str ) { } const Glib::ustring -SPIFontSize::write( guint const flags, SPIBase const *const base) const { +SPIFontSize::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIFontSize const *const my_base = dynamic_cast<const SPIFontSize*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -2239,7 +2227,7 @@ SPIFont::read( gchar const *str ) { } } -const Glib::ustring SPIFont::write( guint const /*flags*/, SPIBase const *const /*base*/) const +const Glib::ustring SPIFont::write( guint const /*flags*/, SPStyleSrc const & /*style_src_req*/, SPIBase const *const /*base*/) const { // At the moment, do nothing. We could add a preference to write out // 'font' shorthand rather than longhand properties. @@ -2316,14 +2304,12 @@ SPIBaselineShift::read( gchar const *str ) { } const Glib::ustring -SPIBaselineShift::write( guint const flags, SPIBase const *const base) const { +SPIBaselineShift::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPIBaselineShift const *const my_base = dynamic_cast<const SPIBaselineShift*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || !this->isZero() ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { CSSOStringStream css; if (this->inherit) { @@ -2507,13 +2493,11 @@ SPITextDecorationLine::read( gchar const *str ) { } const Glib::ustring -SPITextDecorationLine::write( guint const flags, SPIBase const *const base) const { +SPITextDecorationLine::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPITextDecorationLine const *const my_base = dynamic_cast<const SPITextDecorationLine*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { Inkscape::CSSOStringStream os; os << name << ":"; if( inherit ) { @@ -2639,13 +2623,11 @@ SPITextDecorationStyle::read( gchar const *str ) { } const Glib::ustring -SPITextDecorationStyle::write( guint const flags, SPIBase const *const base) const { +SPITextDecorationStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPITextDecorationStyle const *const my_base = dynamic_cast<const SPITextDecorationStyle*>(base); - if ( (flags & SP_STYLE_FLAG_ALWAYS) || - ((flags & SP_STYLE_FLAG_IFSET) && this->set) || - ((flags & SP_STYLE_FLAG_IFDIFF) && this->set - && (!my_base->set || this != my_base ))) - { + bool dfp = (!inherits || !my_base || (my_base != this)); // Different from parent + bool src = (style_src_req == style_src || !(flags & SP_STYLE_FLAG_IFSRC)); + if (should_write(flags, set, dfp, src)) { Inkscape::CSSOStringStream os; os << name << ":"; if( inherit ) { @@ -2795,7 +2777,7 @@ SPITextDecoration::read( gchar const *str ) { // Returns CSS2 'text-decoration' (using settings in SPTextDecorationLine) // This is required until all SVG renderers support CSS3 'text-decoration' const Glib::ustring -SPITextDecoration::write( guint const flags, SPIBase const *const base) const { +SPITextDecoration::write( guint const flags, SPStyleSrc const &style_src_req, SPIBase const *const base) const { SPITextDecoration const *const my_base = dynamic_cast<const SPITextDecoration*>(base); if ( (flags & SP_STYLE_FLAG_ALWAYS) || ((flags & SP_STYLE_FLAG_IFSET) && style->text_decoration_line.set) || diff --git a/src/style-internal.h b/src/style-internal.h index 6cc8c6c4e..154538833 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -35,12 +35,13 @@ struct SPStyleEnum; static const unsigned SP_STYLE_FLAG_ALWAYS (1 << 2); static const unsigned SP_STYLE_FLAG_IFSET (1 << 0); static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1); +static const unsigned SP_STYLE_FLAG_IFSRC (1 << 3); // If source matches enum SPStyleSrc { SP_STYLE_SRC_UNSET, - SP_STYLE_SRC_STYLE_PROP, - SP_STYLE_SRC_STYLE_SHEET, - SP_STYLE_SRC_ATTRIBUTE + SP_STYLE_SRC_ATTRIBUTE, // fill="red" + SP_STYLE_SRC_STYLE_PROP, // style="fill:red" + SP_STYLE_SRC_STYLE_SHEET, // .red { fill:red; } }; /* General comments: @@ -77,6 +78,7 @@ enum SPStyleSrc { * IFSET: Write a property if 'set' flag is true, otherwise return empty string. * IFDIFF: Write a property if computed values are different, otherwise return empty string, * This is only used for text!! + * IFSRC Write a property if the source matches the requested source (style sheet, etc.). * * read(): Set a property value from a string. * clear(): Set a property to its default value and set the 'set' flag to false. @@ -119,7 +121,7 @@ public: inherits(inherits), set(false), inherit(false), - style_src(SP_STYLE_SRC_UNSET), + style_src(SP_STYLE_SRC_STYLE_PROP), // Default to property, see bug 1662285. style(NULL) {} @@ -127,17 +129,21 @@ public: {} virtual void read( gchar const *str ) = 0; - virtual void readIfUnset( gchar const *str ) { + virtual void readIfUnset( gchar const *str, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ) { if ( !set ) { read( str ); + if ( set ) { + style_src = source; + } } } virtual void readAttribute( Inkscape::XML::Node *repr ) { - readIfUnset( repr->attribute( name.c_str() ) ); + readIfUnset( repr->attribute( name.c_str() ), SP_STYLE_SRC_ATTRIBUTE ); } virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const = 0; virtual void clear() { set = false, inherit = false; @@ -176,7 +182,7 @@ public: unsigned inherits : 1; // Property inherits by default from parent. unsigned set : 1; // Property has been explicitly set (vs. inherited). unsigned inherit : 1; // Property value set to 'inherit'. - SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME + SPStyleSrc style_src : 2; // Source (attribute, style attribute, style-sheet). // To do: make private after g_asserts removed public: @@ -202,6 +208,7 @@ public: virtual ~SPIFloat() {} virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -283,6 +290,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -355,6 +363,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -411,6 +420,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPILength::clear(); @@ -474,6 +484,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -528,6 +539,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; }; @@ -554,6 +566,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; }; @@ -577,6 +590,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; }; @@ -606,6 +620,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear(); // TODO check about value and value_default virtual void cascade( const SPIBase* const parent ); @@ -653,6 +668,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -734,6 +750,7 @@ public: virtual void read( gchar const *str ); virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear(); virtual void reset( bool init ); // Used internally when reading or cascading @@ -833,6 +850,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -885,6 +903,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -924,6 +943,7 @@ public: virtual ~SPIFilter(); virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear(); virtual void cascade( const SPIBase* const parent ); @@ -968,6 +988,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1024,6 +1045,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1068,6 +1090,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1124,6 +1147,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1171,6 +1195,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); @@ -1227,6 +1252,7 @@ public: virtual void read( gchar const *str ); virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPIBase const *const base = NULL ) const; virtual void clear() { SPIBase::clear(); diff --git a/src/style.cpp b/src/style.cpp index c513b735d..0b0358bb2 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -23,10 +23,15 @@ #include <config.h> #endif +#include "style.h" + #include <cstring> #include <string> #include <algorithm> +#include <glibmm/regex.h> + +#include "bad-uri-exception.h" #include "libcroco/cr-sel-eng.h" #include "xml/croco-node-iface.h" @@ -38,14 +43,11 @@ #include "uri-references.h" #include "uri.h" #include "sp-paint-server.h" -#include "style.h" #include "svg/css-ostringstream.h" #include "xml/simple-document.h" #include "util/units.h" #include "preferences.h" -#include <glibmm/regex.h> - using Inkscape::CSSOStringStream; using std::vector; @@ -678,22 +680,22 @@ SPStyle::readFromPrefs(Glib::ustring const &path) { // Matches sp_style_merge_property(SPStyle *style, gint id, gchar const *val) void -SPStyle::readIfUnset( gint id, gchar const *val ) { +SPStyle::readIfUnset( gint id, gchar const *val, SPStyleSrc const &source ) { - // std::cout << "SPStyle::readIfUnset: Entrance: " << (val?val:"null") << std::endl; + // std::cout << "SPStyle::readIfUnset: Entrance: " << id << ": " << (val?val:"null") << std::endl; // To Do: If it is not too slow, use std::map instead of std::vector inorder to remove switch() // (looking up SP_PROP_xxxx already uses a hash). g_return_if_fail(val != NULL); switch (id) { case SP_PROP_INKSCAPE_FONT_SPEC: - font_specification.readIfUnset( val ); + font_specification.readIfUnset( val, source ); break; case SP_PROP_FONT_FAMILY: - font_family.readIfUnset( val ); + font_family.readIfUnset( val, source ); break; case SP_PROP_FONT_SIZE: - font_size.readIfUnset( val ); + font_size.readIfUnset( val, source ); break; case SP_PROP_FONT_SIZE_ADJUST: if (strcmp(val, "none") != 0) { @@ -701,105 +703,105 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { } break; case SP_PROP_FONT_STYLE: - font_style.readIfUnset( val ); + font_style.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT: - font_variant.readIfUnset( val ); + font_variant.readIfUnset( val, source ); break; case SP_PROP_FONT_WEIGHT: - font_weight.readIfUnset( val ); + font_weight.readIfUnset( val, source ); break; case SP_PROP_FONT_STRETCH: - font_stretch.readIfUnset( val ); + font_stretch.readIfUnset( val, source ); break; case SP_PROP_FONT: - font.readIfUnset( val ); + font.readIfUnset( val, source ); break; /* Font Variants CSS 3 */ case SP_PROP_FONT_VARIANT_LIGATURES: - font_variant_ligatures.readIfUnset( val ); + font_variant_ligatures.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_POSITION: - font_variant_position.readIfUnset( val ); + font_variant_position.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_CAPS: - font_variant_caps.readIfUnset( val ); + font_variant_caps.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_NUMERIC: - font_variant_numeric.readIfUnset( val ); + font_variant_numeric.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_ALTERNATES: - font_variant_alternates.readIfUnset( val ); + font_variant_alternates.readIfUnset( val, source ); break; case SP_PROP_FONT_VARIANT_EAST_ASIAN: - font_variant_east_asian.readIfUnset( val ); + font_variant_east_asian.readIfUnset( val, source ); break; case SP_PROP_FONT_FEATURE_SETTINGS: - font_feature_settings.readIfUnset( val ); + font_feature_settings.readIfUnset( val, source ); break; /* Text */ case SP_PROP_TEXT_INDENT: - text_indent.readIfUnset( val ); + text_indent.readIfUnset( val, source ); break; case SP_PROP_TEXT_ALIGN: - text_align.readIfUnset( val ); + text_align.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION: - text_decoration.readIfUnset( val ); + text_decoration.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION_LINE: - text_decoration_line.readIfUnset( val ); + text_decoration_line.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION_STYLE: - text_decoration_style.readIfUnset( val ); + text_decoration_style.readIfUnset( val, source ); break; case SP_PROP_TEXT_DECORATION_COLOR: - text_decoration_color.readIfUnset( val ); + text_decoration_color.readIfUnset( val, source ); break; case SP_PROP_LINE_HEIGHT: - line_height.readIfUnset( val ); + line_height.readIfUnset( val, source ); break; case SP_PROP_LETTER_SPACING: - letter_spacing.readIfUnset( val ); + letter_spacing.readIfUnset( val, source ); break; case SP_PROP_WORD_SPACING: - word_spacing.readIfUnset( val ); + word_spacing.readIfUnset( val, source ); break; case SP_PROP_TEXT_TRANSFORM: - text_transform.readIfUnset( val ); + text_transform.readIfUnset( val, source ); break; /* Text (css3) */ case SP_PROP_DIRECTION: - direction.readIfUnset( val ); + direction.readIfUnset( val, source ); break; case SP_PROP_WRITING_MODE: - writing_mode.readIfUnset( val ); + writing_mode.readIfUnset( val, source ); break; case SP_PROP_TEXT_ORIENTATION: - text_orientation.readIfUnset( val ); + text_orientation.readIfUnset( val, source ); break; case SP_PROP_TEXT_ANCHOR: - text_anchor.readIfUnset( val ); + text_anchor.readIfUnset( val, source ); break; case SP_PROP_WHITE_SPACE: - white_space.readIfUnset( val ); + white_space.readIfUnset( val, source ); break; case SP_PROP_SHAPE_INSIDE: - shape_inside.readIfUnset( val ); + shape_inside.readIfUnset( val, source ); break; case SP_PROP_SHAPE_PADDING: - shape_padding.readIfUnset( val ); + shape_padding.readIfUnset( val, source ); break; case SP_PROP_DOMINANT_BASELINE: - dominant_baseline.readIfUnset( val ); + dominant_baseline.readIfUnset( val, source ); break; case SP_PROP_BASELINE_SHIFT: - baseline_shift.readIfUnset( val ); + baseline_shift.readIfUnset( val, source ); break; case SP_PROP_TEXT_RENDERING: - text_rendering.readIfUnset( val ); + text_rendering.readIfUnset( val, source ); break; case SP_PROP_ALIGNMENT_BASELINE: g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val); @@ -818,25 +820,25 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { g_warning("Unimplemented style property SP_PROP_CLIP: value: %s", val); break; case SP_PROP_COLOR: - color.readIfUnset( val ); + color.readIfUnset( val, source ); break; case SP_PROP_CURSOR: g_warning("Unimplemented style property SP_PROP_CURSOR: value: %s", val); break; case SP_PROP_DISPLAY: - display.readIfUnset( val ); + display.readIfUnset( val, source ); break; case SP_PROP_OVERFLOW: - overflow.readIfUnset( val ); + overflow.readIfUnset( val, source ); break; case SP_PROP_VISIBILITY: - visibility.readIfUnset( val ); + visibility.readIfUnset( val, source ); break; case SP_PROP_ISOLATION: - isolation.readIfUnset( val ); + isolation.readIfUnset( val, source ); break; case SP_PROP_MIX_BLEND_MODE: - mix_blend_mode.readIfUnset( val ); + mix_blend_mode.readIfUnset( val, source ); break; /* SVG */ @@ -854,7 +856,7 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { this->object->getRepr()->setAttribute("clip-path", val); break; case SP_PROP_CLIP_RULE: - clip_rule.readIfUnset( val ); + clip_rule.readIfUnset( val, source ); break; case SP_PROP_MASK: /** \todo @@ -866,14 +868,14 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { this->object->getRepr()->setAttribute("mask", val); break; case SP_PROP_OPACITY: - opacity.readIfUnset( val ); + opacity.readIfUnset( val, source ); break; case SP_PROP_ENABLE_BACKGROUND: - enable_background.readIfUnset( val ); + enable_background.readIfUnset( val, source ); break; /* Filter */ case SP_PROP_FILTER: - if( !filter.inherit ) filter.readIfUnset( val ); + if( !filter.inherit ) filter.readIfUnset( val, source ); break; case SP_PROP_FLOOD_COLOR: g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val); @@ -898,86 +900,86 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { /* Paint */ case SP_PROP_COLOR_INTERPOLATION: // We read it but issue warning - color_interpolation.readIfUnset( val ); + color_interpolation.readIfUnset( val, source ); if( color_interpolation.value != SP_CSS_COLOR_INTERPOLATION_SRGB ) { g_warning("Inkscape currently only supports color-interpolation = sRGB"); } break; case SP_PROP_COLOR_INTERPOLATION_FILTERS: - color_interpolation_filters.readIfUnset( val ); + color_interpolation_filters.readIfUnset( val, source ); break; case SP_PROP_COLOR_PROFILE: g_warning("Unimplemented style property SP_PROP_COLOR_PROFILE: value: %s", val); break; case SP_PROP_COLOR_RENDERING: - color_rendering.readIfUnset( val ); + color_rendering.readIfUnset( val, source ); break; case SP_PROP_SOLID_COLOR: - solid_color.readIfUnset( val ); + solid_color.readIfUnset( val, source ); break; case SP_PROP_SOLID_OPACITY: - solid_opacity.readIfUnset( val ); + solid_opacity.readIfUnset( val, source ); break; case SP_PROP_VECTOR_EFFECT: vector_effect.readIfUnset( val ); break; case SP_PROP_FILL: - fill.readIfUnset( val ); + fill.readIfUnset( val, source ); break; case SP_PROP_FILL_OPACITY: - fill_opacity.readIfUnset( val ); + fill_opacity.readIfUnset( val, source ); break; case SP_PROP_FILL_RULE: - fill_rule.readIfUnset( val ); + fill_rule.readIfUnset( val, source ); break; case SP_PROP_IMAGE_RENDERING: - image_rendering.readIfUnset( val ); + image_rendering.readIfUnset( val, source ); break; case SP_PROP_MARKER: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker.readIfUnset( val ); + marker.readIfUnset( val, source ); break; case SP_PROP_MARKER_START: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker_start.readIfUnset( val ); + marker_start.readIfUnset( val, source ); break; case SP_PROP_MARKER_MID: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker_mid.readIfUnset( val ); + marker_mid.readIfUnset( val, source ); break; case SP_PROP_MARKER_END: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - marker_end.readIfUnset( val ); + marker_end.readIfUnset( val, source ); break; case SP_PROP_SHAPE_RENDERING: - shape_rendering.readIfUnset( val ); + shape_rendering.readIfUnset( val, source ); break; case SP_PROP_STROKE: - stroke.readIfUnset( val ); + stroke.readIfUnset( val, source ); break; case SP_PROP_STROKE_WIDTH: - stroke_width.readIfUnset( val ); + stroke_width.readIfUnset( val, source ); break; case SP_PROP_STROKE_DASHARRAY: - stroke_dasharray.readIfUnset( val ); + stroke_dasharray.readIfUnset( val, source ); break; case SP_PROP_STROKE_DASHOFFSET: - stroke_dashoffset.readIfUnset( val ); + stroke_dashoffset.readIfUnset( val, source ); break; case SP_PROP_STROKE_LINECAP: - stroke_linecap.readIfUnset( val ); + stroke_linecap.readIfUnset( val, source ); break; case SP_PROP_STROKE_LINEJOIN: - stroke_linejoin.readIfUnset( val ); + stroke_linejoin.readIfUnset( val, source ); break; case SP_PROP_STROKE_MITERLIMIT: - stroke_miterlimit.readIfUnset( val ); + stroke_miterlimit.readIfUnset( val, source ); break; case SP_PROP_STROKE_OPACITY: - stroke_opacity.readIfUnset( val ); + stroke_opacity.readIfUnset( val, source ); break; case SP_PROP_PAINT_ORDER: - paint_order.readIfUnset( val ); + paint_order.readIfUnset( val, source ); break; default: g_warning("SPIStyle::readIfUnset(): Invalid style property id: %d value: %s", id, val); @@ -999,16 +1001,16 @@ SPStyle::readIfUnset( gint id, gchar const *val ) { * \post ret != NULL. */ Glib::ustring -SPStyle::write( guint const flags, SPStyle const *const base ) const { +SPStyle::write( guint const flags, SPStyleSrc const &style_src_req, SPStyle const *const base ) const { - // std::cout << "SPStyle::write" << std::endl; + // std::cout << "SPStyle::write: flags: " << flags << std::endl; Glib::ustring style_string; for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { if( base != NULL ) { - style_string += _properties[i]->write( flags, base->_properties[i] ); + style_string += _properties[i]->write( flags, style_src_req, base->_properties[i] ); } else { - style_string += _properties[i]->write( flags, NULL ); + style_string += _properties[i]->write( flags, style_src_req, NULL ); } } // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { @@ -1112,13 +1114,13 @@ SPStyle::_mergeString( gchar const *const p ) { CRDeclaration *const decl_list = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8); if (decl_list) { - _mergeDeclList( decl_list ); + _mergeDeclList( decl_list, SP_STYLE_SRC_STYLE_PROP ); cr_declaration_destroy(decl_list); } } void -SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) { +SPStyle::_mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source ) { // std::cout << "SPStyle::_mergeDeclList" << std::endl; @@ -1126,13 +1128,13 @@ SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) { // (Properties are only set if not previously set. See: // Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.) if (decl_list->next) { - _mergeDeclList( decl_list->next ); + _mergeDeclList( decl_list->next, source ); } - _mergeDecl( decl_list ); + _mergeDecl( decl_list, source ); } void -SPStyle::_mergeDecl( CRDeclaration const *const decl ) { +SPStyle::_mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source ) { // std::cout << "SPStyle::_mergeDecl" << std::endl; @@ -1145,7 +1147,7 @@ SPStyle::_mergeDecl( CRDeclaration const *const decl ) { */ guchar *const str_value_unsigned = cr_term_to_string(decl->value); gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned); - readIfUnset( prop_idx, str_value ); + readIfUnset( prop_idx, str_value, source ); g_free(str_value); } } @@ -1160,7 +1162,7 @@ SPStyle::_mergeProps( CRPropList *const props ) { _mergeProps( cr_prop_list_get_next( props ) ); CRDeclaration *decl = NULL; cr_prop_list_get_decl(props, &decl); - _mergeDecl( decl ); + _mergeDecl( decl, SP_STYLE_SRC_STYLE_SHEET ); } } diff --git a/src/style.h b/src/style.h index c8192f782..00c8c032a 100644 --- a/src/style.h +++ b/src/style.h @@ -50,8 +50,9 @@ public: void read(SPObject *object, Inkscape::XML::Node *repr); void readFromObject(SPObject *object); void readFromPrefs(Glib::ustring const &path); - void readIfUnset( int id, char const *val ); + void readIfUnset( int id, char const *val, SPStyleSrc const &source = SP_STYLE_SRC_STYLE_PROP ); Glib::ustring write( unsigned int const flags = SP_STYLE_FLAG_IFSET, + SPStyleSrc const &style_src_req = SP_STYLE_SRC_STYLE_PROP, SPStyle const *const base = NULL ) const; void cascade( SPStyle const *const parent ); void merge( SPStyle const *const parent ); @@ -64,8 +65,8 @@ public: private: void _mergeString( char const *const p ); - void _mergeDeclList( CRDeclaration const *const decl_list ); - void _mergeDecl( CRDeclaration const *const decl ); + void _mergeDeclList( CRDeclaration const *const decl_list, SPStyleSrc const &source ); + void _mergeDecl( CRDeclaration const *const decl, SPStyleSrc const &source ); void _mergeProps( CRPropList *const props ); void _mergeObjectStylesheet( SPObject const *const object ); diff --git a/src/svg/svg-length.h b/src/svg/svg-length.h index bd3435ca6..4663aced4 100644 --- a/src/svg/svg-length.h +++ b/src/svg/svg-length.h @@ -12,6 +12,8 @@ #ifndef SEEN_SP_SVG_LENGTH_H #define SEEN_SP_SVG_LENGTH_H +#include <string> + /** * SVG length type */ diff --git a/src/trace/trace.h b/src/trace/trace.h index 662b2537e..f562e89aa 100644 --- a/src/trace/trace.h +++ b/src/trace/trace.h @@ -13,10 +13,6 @@ # include "config.h" #endif -#ifdef HAVE_STDLIB_H -# include <stdlib.h> -#endif - #ifdef HAVE_STRING_H # include <string.h> #endif diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index f2a256698..259a06013 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -57,6 +57,7 @@ set(ui_SRC dialog/calligraphic-profile-rename.cpp dialog/clonetiler.cpp dialog/color-item.cpp + dialog/cssdialog.cpp dialog/debug.cpp dialog/desktop-tracker.cpp dialog/dialog-manager.cpp @@ -98,7 +99,9 @@ set(ui_SRC dialog/polar-arrange-tab.cpp dialog/print-colors-preview-dialog.cpp dialog/print.cpp + dialog/prototype.cpp dialog/spellcheck.cpp + dialog/styledialog.cpp dialog/svg-fonts-dialog.cpp dialog/swatches.cpp dialog/symbols.cpp @@ -195,6 +198,7 @@ set(ui_SRC dialog/calligraphic-profile-rename.h dialog/clonetiler.h dialog/color-item.h + dialog/cssdialog.h dialog/debug.h dialog/desktop-tracker.h dialog/dialog-manager.h @@ -238,7 +242,9 @@ set(ui_SRC dialog/polar-arrange-tab.h dialog/print-colors-preview-dialog.h dialog/print.h + dialog/prototype.h dialog/spellcheck.h + dialog/styledialog.h dialog/svg-fonts-dialog.h dialog/swatches.h dialog/symbols.h diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index c1e824c1e..734640584 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -76,6 +76,7 @@ #include "sp-namedview.h" #include "persp3d.h" #include "object-set.h" +#include "extension/find_extension_by_mime.h" /// Made up mimetype to represent Gdk::Pixbuf clipboard contents. #define CLIPBOARD_GDK_PIXBUF_TARGET "image/x-gdk-pixbuf" @@ -143,6 +144,11 @@ private: Inkscape::XML::Node *_clipnode; ///< The node that holds extra information Inkscape::XML::Document *_doc; ///< Reference to the clipboard's Inkscape::XML::Document std::set<SPItem*> cloned_elements; + std::vector<SPCSSAttr*> te_selected_style; + std::vector<unsigned> te_selected_style_positions; + int nr_blocks = 0; + unsigned copied_style_length = 0; + // we need a way to copy plain text AND remember its style; // the standard _clipnode is only available in an SVG tree, hence this special storage @@ -241,6 +247,9 @@ void ClipboardManagerImpl::copy(ObjectSet *set) sp_repr_css_attr_unref(_text_style); _text_style = NULL; } + te_selected_style.clear(); + te_selected_style_positions.clear(); + te_selected_style = Inkscape::UI::Tools::sp_text_get_selected_style(desktop->event_context, &copied_style_length, &nr_blocks, &te_selected_style_positions); _text_style = Inkscape::UI::Tools::sp_text_get_style_at_cursor(desktop->event_context); return; } @@ -945,15 +954,7 @@ bool ClipboardManagerImpl::_pasteImage(SPDocument *doc) 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; + Inkscape::Extension::Extension *png = Inkscape::Extension::find_by_mime("image/png"); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::ustring attr_saved = prefs->getString("/dialogs/import/link"); bool ask_saved = prefs->getBool("/dialogs/import/ask"); @@ -983,7 +984,42 @@ bool ClipboardManagerImpl::_pasteText(SPDesktop *desktop) // if the text editing tool is active, paste the text into the active text object if (tools_isactive(desktop, TOOLS_TEXT)) { - return Inkscape::UI::Tools::sp_text_paste_inline(desktop->event_context); + if(Inkscape::UI::Tools::sp_text_paste_inline(desktop->event_context) == false) + return false; + //apply the saved style to pasted text + Glib::RefPtr<Gtk::Clipboard> refClipboard = Gtk::Clipboard::get(); + Glib::ustring const clip_text = refClipboard->wait_for_text(); + Glib::ustring text(clip_text); + if(text.length() == copied_style_length) + { + Inkscape::UI::Tools::TextTool *tc = SP_TEXT_CONTEXT(desktop->event_context); + Inkscape::Text::Layout const *layout = te_get_layout(tc->text); + Inkscape::Text::Layout::iterator it_next; + Inkscape::Text::Layout::iterator it = tc->text_sel_end; + + SPCSSAttr *css = take_style_from_item(tc->text); + for (int i = 0; i < nr_blocks; ++i) + { + gchar const *w = sp_repr_css_property(css, "font-size", "40px"); + if (w) + sp_repr_css_set_property(te_selected_style[i], "font-size", w); + } + + for (int i = 0; i < text.length(); ++i) + it.prevCharacter(); + it_next = layout->charIndexToIterator(layout->iteratorToCharIndex(it)); + + for (int i = 0; i < nr_blocks; ++i) + { + for (int j = te_selected_style_positions[i]; j < te_selected_style_positions[i+1]; ++j) + it_next.nextCharacter(); + sp_te_apply_style(tc->text, it, it_next, te_selected_style[i]); + te_update_layout_now_recursive(tc->text); + for (int j = te_selected_style_positions[i]; j < te_selected_style_positions[i+1]; ++j) + it.nextCharacter(); + } + } + return true; } // try to parse the text as a color and, if successful, apply it as the current style diff --git a/src/ui/dialog/aboutbox.cpp b/src/ui/dialog/aboutbox.cpp index 8f0545e96..805bfb562 100644 --- a/src/ui/dialog/aboutbox.cpp +++ b/src/ui/dialog/aboutbox.cpp @@ -115,7 +115,7 @@ AboutBox::AboutBox() : Gtk::Dialog(_("About Inkscape")) { Gtk::Label *link = new Gtk::Label(); const gchar *website_link = - "<a href=\"https://www.inkscape.org\"> https://www.inkscape.org</a>"; + "<a href=\"https://www.inkscape.org\">https://www.inkscape.org</a>"; link->set_markup(website_link); link->set_alignment(Gtk::ALIGN_END); @@ -152,16 +152,16 @@ Gtk::Widget *build_splash_widget() { the `screens' directory. Thus the translation of "about.svg" should be the filename of its translated version, e.g. about.zh.svg for Chinese. - N.B. about.svg changes once per release. (We should probably rename - the original to about-0.40.svg etc. as soon as we have a translation. - If we do so, then add an item to release-checklist saying that the - string here should be changed.) */ + Please don't translate the filename unless the translated picture exists. */ // FIXME? INKSCAPE_SCREENSDIR and "about.svg" are in UTF-8, not the // native filename encoding... and the filename passed to sp_document_new // should be in UTF-*8.. char *about=g_build_filename(INKSCAPE_SCREENSDIR, _("about.svg"), NULL); + if (!g_file_test (about, G_FILE_TEST_EXISTS)) { + about=g_build_filename(INKSCAPE_SCREENSDIR, "about.svg", NULL); + } SPDocument *doc=SPDocument::createNewDoc (about, TRUE); g_free(about); g_return_val_if_fail(doc != NULL, NULL); diff --git a/src/ui/dialog/cssdialog.cpp b/src/ui/dialog/cssdialog.cpp new file mode 100644 index 000000000..85c804b75 --- /dev/null +++ b/src/ui/dialog/cssdialog.cpp @@ -0,0 +1,146 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * Tavmjong Bah + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "cssdialog.h" +#include "ui/widget/addtoicon.h" +#include "widgets/icon.h" +#include "verbs.h" +#include "sp-object.h" +#include "selection.h" +#include "xml/attribute-record.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * Constructor + * A treeview whose each row corresponds to a CSS property of selector selected. + * New CSS property can be added by clicking '+' at bottom of the CSS pane. '-' + * in front of the CSS property row can be clicked to delete the CSS property. + * Besides clicking on an already selected property row makes the property editable + * and clicking 'Enter' updates the property with changes reflected in the + * drawing. + */ +CssDialog::CssDialog(): + UI::Widget::Panel("", "/dialogs/css", SP_VERB_DIALOG_CSS), + _desktop(0) +{ + set_size_request(20, 15); + _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET); + _treeView.set_headers_visible(true); + _scrolledWindow.add(_treeView); + _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + _store = Gtk::ListStore::create(_cssColumns); + _treeView.set_model(_store); + + Inkscape::UI::Widget::AddToIcon * addRenderer = manage(new Inkscape::UI::Widget::AddToIcon()); + addRenderer->property_active() = false; + + int addCol = _treeView.append_column("", *addRenderer) - 1; + Gtk::TreeViewColumn *col = _treeView.get_column(addCol); + if (col) { + col->add_attribute(addRenderer->property_active(), _cssColumns._colUnsetProp); + } + + _propRenderer = Gtk::manage(new Gtk::CellRendererText()); + _propRenderer->property_editable() = true; + int nameColNum = _treeView.append_column("CSS Property", *_propRenderer) - 1; + _propCol = _treeView.get_column(nameColNum); + if (_propCol) { + _propCol->add_attribute(_propRenderer->property_text(), _cssColumns._propertyLabel); + } + + _sheetRenderer = Gtk::manage(new Gtk::CellRendererText()); + _sheetRenderer->property_editable() = true; + int sheetColNum = _treeView.append_column("Style Sheet", *_sheetRenderer) - 1; + _sheetCol = _treeView.get_column(sheetColNum); + if (_sheetCol) { + _sheetCol->add_attribute(_sheetRenderer->property_text(), _cssColumns._styleSheetVal); + } + + _attrRenderer = Gtk::manage(new Gtk::CellRendererText()); + _attrRenderer->property_editable() = false; + int attrColNum = _treeView.append_column("Style Attribute", *_attrRenderer) - 1; + _attrCol = _treeView.get_column(attrColNum); + if (_attrCol) { + _attrCol->add_attribute(_attrRenderer->property_text(), _cssColumns._styleAttrVal); + } + + _styleButton(_buttonAddProperty, "list-add", "Add a new property"); + + _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK); + _buttonBox.pack_start(_buttonAddProperty, Gtk::PACK_SHRINK); + + _getContents()->pack_start(_mainBox, Gtk::PACK_EXPAND_WIDGET); + + setDesktop(getDesktop()); + + _buttonAddProperty.signal_clicked().connect(sigc::mem_fun(*this, &CssDialog::_addProperty)); +} + + +/** + * @brief CssDialog::~CssDialog + * Class destructor + */ +CssDialog::~CssDialog() +{ + setDesktop(NULL); +} + + +/** + * @brief CssDialog::setDesktop + * @param desktop + * This function sets the 'desktop' for the CSS pane. + */ +void CssDialog::setDesktop(SPDesktop* desktop) +{ + _desktop = desktop; +} + + +/** + * @brief CssDialog::_styleButton + * @param btn + * @param iconName + * @param tooltip + * This function sets the style of '+'button at the bottom of dialog. + */ +void CssDialog::_styleButton(Gtk::Button& btn, char const* iconName, + char const* tooltip) +{ + GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName); + gtk_widget_show(child); + btn.add(*manage(Glib::wrap(child))); + btn.set_relief(Gtk::RELIEF_NONE); + btn.set_tooltip_text(tooltip); +} + + +/** + * @brief CssDialog::_addProperty + * This function is a slot to signal_clicked for '+' button at the bottom of CSS + * panel. A new row is added, double clicking which text for new property can be + * added. + */ +void CssDialog::_addProperty() +{ + _propRow = *(_store->append()); +} + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape diff --git a/src/ui/dialog/cssdialog.h b/src/ui/dialog/cssdialog.h new file mode 100644 index 000000000..31eb67e3f --- /dev/null +++ b/src/ui/dialog/cssdialog.h @@ -0,0 +1,93 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * Tavmjong Bah + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef CSSDIALOG_H +#define CSSDIALOG_H + +#include <gtkmm/treeview.h> +#include <gtkmm/liststore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include <ui/widget/panel.h> + +#include "desktop.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief The CssDialog class + * This dialog allows to add, delete and modify CSS properties for selectors + * created in Style Dialog. Double clicking any selector in Style dialog, a list + * of CSS properties will show up in this dialog (if any exist), else new properties + * can be added and each new property forms a new row in this pane. + */ +class CssDialog : public UI::Widget::Panel +{ +public: + CssDialog(); + ~CssDialog(); + + static CssDialog &getInstance() { return *new CssDialog(); } + + // Data structure + class CssColumns : public Gtk::TreeModel::ColumnRecord { + public: + CssColumns() { + add(_colUnsetProp); + add(_propertyLabel); + add(_styleSheetVal); + add(_styleAttrVal); + } + Gtk::TreeModelColumn<bool> _colUnsetProp; + Gtk::TreeModelColumn<Glib::ustring> _propertyLabel; + Gtk::TreeModelColumn<Glib::ustring> _styleSheetVal; + Gtk::TreeModelColumn<Glib::ustring> _styleAttrVal; + }; + CssColumns _cssColumns; + + // TreeView + Gtk::TreeView _treeView; + Glib::RefPtr<Gtk::ListStore> _store; + Gtk::TreeModel::Row _propRow; + Gtk::CellRendererText *_propRenderer; + Gtk::CellRendererText *_sheetRenderer; + Gtk::CellRendererText *_attrRenderer; + Gtk::TreeViewColumn *_propCol; + Gtk::TreeViewColumn *_sheetCol; + Gtk::TreeViewColumn *_attrCol; + + // Widgets + Gtk::VBox _mainBox; + Gtk::ScrolledWindow _scrolledWindow; + Gtk::HBox _buttonBox; + Gtk::Button _buttonAddProperty; + + // Variables - Inkscape + SPDesktop* _desktop; + + // Helper functions + void setDesktop(SPDesktop* desktop); + void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip); + + // Signal handlers + void _addProperty(); +}; + + +} // namespace Dialog +} // namespace UI +} // namespace Inkscape + +#endif // CSSDIALOG_H diff --git a/src/ui/dialog/dialog-manager.cpp b/src/ui/dialog/dialog-manager.cpp index c53112656..01cd9dd0f 100644 --- a/src/ui/dialog/dialog-manager.cpp +++ b/src/ui/dialog/dialog-manager.cpp @@ -19,6 +19,7 @@ #include "ui/dialog/dialog-manager.h" +#include "ui/dialog/prototype.h" #include "ui/dialog/align-and-distribute.h" #include "ui/dialog/document-metadata.h" #include "ui/dialog/document-properties.h" @@ -57,6 +58,8 @@ #include "ui/dialog/svg-fonts-dialog.h" #include "ui/dialog/objects.h" #include "ui/dialog/tags.h" +#include "ui/dialog/styledialog.h" +#include "ui/dialog/cssdialog.h" namespace Inkscape { namespace UI { @@ -103,6 +106,7 @@ DialogManager::DialogManager() { registerFactory("InkscapePreferences", &create<InkscapePreferences, FloatingBehavior>); if (dialogs_type == FLOATING) { + registerFactory("Prototype", &create<Prototype, FloatingBehavior>); registerFactory("AlignAndDistribute", &create<AlignAndDistribute, FloatingBehavior>); registerFactory("DocumentMetadata", &create<DocumentMetadata, FloatingBehavior>); registerFactory("DocumentProperties", &create<DocumentProperties, FloatingBehavior>); @@ -125,6 +129,8 @@ DialogManager::DialogManager() { registerFactory("Swatches", &create<SwatchesPanel, FloatingBehavior>); registerFactory("TileDialog", &create<ArrangeDialog, FloatingBehavior>); registerFactory("Symbols", &create<SymbolsDialog, FloatingBehavior>); + registerFactory("StyleDialog", &create<StyleDialog, FloatingBehavior>); + registerFactory("CssDialog", &create<CssDialog, FloatingBehavior>); #if HAVE_POTRACE registerFactory("Trace", &create<TraceDialog, FloatingBehavior>); @@ -142,6 +148,7 @@ DialogManager::DialogManager() { } else { + registerFactory("Prototype", &create<Prototype, DockBehavior>); registerFactory("AlignAndDistribute", &create<AlignAndDistribute, DockBehavior>); registerFactory("DocumentMetadata", &create<DocumentMetadata, DockBehavior>); registerFactory("DocumentProperties", &create<DocumentProperties, DockBehavior>); @@ -164,6 +171,8 @@ DialogManager::DialogManager() { registerFactory("Swatches", &create<SwatchesPanel, DockBehavior>); registerFactory("TileDialog", &create<ArrangeDialog, DockBehavior>); registerFactory("Symbols", &create<SymbolsDialog, DockBehavior>); + registerFactory("StyleDialog", &create<StyleDialog, DockBehavior>); + registerFactory("CssDialog", &create<CssDialog, DockBehavior>); #if HAVE_POTRACE registerFactory("Trace", &create<TraceDialog, DockBehavior>); diff --git a/src/ui/dialog/dialog-manager.h b/src/ui/dialog/dialog-manager.h index 15573f760..36e9a12be 100644 --- a/src/ui/dialog/dialog-manager.h +++ b/src/ui/dialog/dialog-manager.h @@ -30,11 +30,11 @@ public: static DialogManager &getInstance(); - sigc::signal<void> show_dialogs; - sigc::signal<void> show_f12; - sigc::signal<void> hide_dialogs; - sigc::signal<void> hide_f12; - sigc::signal<void> transientize; + // sigc::signal<void> show_dialogs; + // sigc::signal<void> show_f12; + // sigc::signal<void> hide_dialogs; + // sigc::signal<void> hide_f12; + // sigc::signal<void> transientize; /* generic dialog management start */ typedef std::map<GQuark, DialogFactory> FactoryMap; diff --git a/src/ui/dialog/filedialogimpl-win32.cpp b/src/ui/dialog/filedialogimpl-win32.cpp index 02d77cba1..1efec7d52 100644 --- a/src/ui/dialog/filedialogimpl-win32.cpp +++ b/src/ui/dialog/filedialogimpl-win32.cpp @@ -18,38 +18,23 @@ #endif #include "filedialogimpl-win32.h" //General includes -#include <list> -#include <unistd.h> -#include <sys/stat.h> -#include <errno.h> -#include <set> +#include <cairomm/win32_surface.h> #include <gdk/gdkwin32.h> -#include <glib/gstdio.h> -#include <glibmm/i18n.h> +#include <gdkmm/general.h> #include <glibmm/fileutils.h> -#include <gtkmm/window.h> +#include <glibmm/i18n.h> //Inkscape includes -#include "inkscape.h" -#include "ui/dialog-events.h" +#include "display/cairo-utils.h" +#include "document.h" +#include "extension/db.h" #include "extension/input.h" #include "extension/output.h" -#include "extension/db.h" - -//#include "display/drawing-item.h" -//#include "display/drawing.h" -#include "sp-item.h" -#include "display/canvas-arena.h" - #include "filedialog.h" - -#include "sp-root.h" +#include "helper/pixbuf-ops.h" #include "preferences.h" +#include "util/units.h" -#include <zlib.h> -#include <cairomm/win32_surface.h> -#include <cairomm/context.h> -#include <gdkmm/general.h> using namespace std; using namespace Glib; @@ -807,7 +792,7 @@ LRESULT CALLBACK FileOpenDialogImplWin32::preview_wnd_proc(HWND hwnd, UINT uMsg, _wsplitpath(pImpl->_path_string, NULL, NULL, szFileName, NULL); const int iLength = snwprintf(szCaption, - sizeof(szCaption), L"%s\n%d kB", + sizeof(szCaption), L"%ls\n%d kB", szFileName, pImpl->_preview_file_size); DrawTextW(dc, szCaption, iLength, &rcCaptionRect, @@ -1020,15 +1005,11 @@ void FileOpenDialogImplWin32::free_preview() bool FileOpenDialogImplWin32::set_svg_preview() { - return false; - // NOTE: it's not worth the effort to fix this to use Cairo. - // Native file dialogs are unmaintainable and should be removed anyway. - #if 0 const int PreviewSize = 512; gchar *utf8string = g_utf16_to_utf8((const gunichar2*)_path_string, _MAX_PATH, NULL, NULL, NULL); - SPDocument *svgDoc = SPDocument::createNewDoc (utf8string, true); + SPDocument *svgDoc = SPDocument::createNewDoc (utf8string, 0); g_free(utf8string); // Check the document loaded properly @@ -1042,87 +1023,39 @@ bool FileOpenDialogImplWin32::set_svg_preview() } // Get the size of the document - const double svgWidth = svgDoc->getWidth(); - const double svgHeight = svgDoc->getHeight(); + Inkscape::Util::Quantity svgWidth = svgDoc->getWidth(); + Inkscape::Util::Quantity svgHeight = svgDoc->getHeight(); + const double svgWidth_px = svgWidth.value("px"); + const double svgHeight_px = svgHeight.value("px"); // Find the minimum scale to fit the image inside the preview area - const double scaleFactorX = PreviewSize / svgWidth; - const double scaleFactorY = PreviewSize / svgHeight; + const double scaleFactorX = PreviewSize / svgWidth_px; + const double scaleFactorY = PreviewSize / svgHeight_px; const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX; // Now get the resized values - const double scaledSvgWidth = scaleFactor * svgWidth; - const double scaledSvgHeight = scaleFactor * svgHeight; - - Geom::Rect area(Geom::Point(0, 0), Geom::Point(scaledSvgWidth, scaledSvgHeight)); - NRRectL areaL = {0, 0, scaledSvgWidth, scaledSvgHeight}; - NRRectL bbox = {0, 0, scaledSvgWidth, scaledSvgHeight}; - - // write object bbox to area - svgDoc->ensureUpToDate(); - Geom::OptRect maybeArea = area | svgDoc->getRoot()->desktopVisualBounds(); - - NRArena *const arena = NRArena::create(); - - unsigned const key = SPItem::display_key_new(1); - - NRArenaItem *root = svgDoc->getRoot()->invoke_show( - arena, key, SP_ITEM_SHOW_DISPLAY); - - NRGC gc(NULL); - gc.transform = Geom::Affine(Geom::Scale(scaleFactor, scaleFactor)); + const int scaledSvgWidth = round(scaleFactor * svgWidth_px); + const int scaledSvgHeight = round(scaleFactor * svgHeight_px); - nr_arena_item_invoke_update (root, NULL, &gc, - NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE); - - // Prepare a GDI compatible NRPixBlock - NRPixBlock pixBlock; - pixBlock.size = NR_PIXBLOCK_SIZE_BIG; - pixBlock.mode = NR_PIXBLOCK_MODE_R8G8B8; - pixBlock.empty = 1; - pixBlock.visible_area.x0 = pixBlock.area.x0 = 0; - pixBlock.visible_area.y0 = pixBlock.area.y0 = 0; - pixBlock.visible_area.x1 = pixBlock.area.x1 = scaledSvgWidth; - pixBlock.visible_area.y1 = pixBlock.area.y1 = scaledSvgHeight; - pixBlock.rs = 4 * ((3 * (int)scaledSvgWidth + 3) / 4); - pixBlock.data.px = g_try_new (unsigned char, pixBlock.rs * scaledSvgHeight); - - // Fail if the pixblock failed to allocate - if(pixBlock.data.px == NULL) - { - svgDoc->doUnref(); - return false; - } - - memset(pixBlock.data.px, 0xFF, pixBlock.rs * scaledSvgHeight); - - memcpy(&root->bbox, &areaL, sizeof(areaL)); - - // Render the image - nr_arena_item_invoke_render(NULL, root, &bbox, &pixBlock, /*0*/NR_ARENA_ITEM_RENDER_NO_CACHE); + const double dpi = 96*scaleFactor; + Inkscape::Pixbuf * pixbuf = sp_generate_internal_bitmap(svgDoc, NULL, 0, 0, svgWidth_px, svgHeight_px, scaledSvgWidth, scaledSvgHeight, dpi, dpi, (guint32) 0xffffff00, NULL); // Tidy up svgDoc->doUnref(); - svgDoc->getRoot()->invoke_hide(key); - nr_object_unref((NRObject *) arena); + if (pixbuf == NULL) { + return false; + } // Create the GDK pixbuf _mutex->lock(); - - _preview_bitmap_image = Gdk::Pixbuf::create_from_data( - pixBlock.data.px, Gdk::COLORSPACE_RGB, false, 8, - (int)scaledSvgWidth, (int)scaledSvgHeight, pixBlock.rs, - sigc::ptr_fun(destroy_svg_rendering)); - - _preview_document_width = scaledSvgWidth; - _preview_document_height = scaledSvgHeight; - _preview_image_width = svgWidth; - _preview_image_height = svgHeight; - + _preview_bitmap_image = Glib::wrap(pixbuf->getPixbufRaw()); + _preview_document_width = svgWidth_px; + _preview_document_height = svgHeight_px; + _preview_image_width = scaledSvgWidth; + _preview_image_height = scaledSvgHeight; _mutex->unlock(); return true; - #endif } void FileOpenDialogImplWin32::destroy_svg_rendering(const guint8 *buffer) @@ -1301,17 +1234,8 @@ bool FileOpenDialogImplWin32::set_emf_preview() const double emfWidth = w; const double emfHeight = h; - // Find the minimum scale to fit the image inside the preview area - const double scaleFactorX = PreviewSize / emfWidth; - const double scaleFactorY = PreviewSize / emfHeight; - const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX; - - // Now get the resized values - const double scaledEmfWidth = scaleFactor * emfWidth; - const double scaledEmfHeight = scaleFactor * emfHeight; - - _preview_document_width = scaledEmfWidth; - _preview_document_height = scaledEmfHeight; + _preview_document_width = emfWidth / 2540 * 96; // width is in units of 0.01 mm + _preview_document_height = emfHeight / 2540 * 96; // height is in units of 0.01 mm _preview_image_width = emfWidth; _preview_image_height = emfHeight; @@ -1370,17 +1294,13 @@ void FileOpenDialogImplWin32::render_preview() } // Find the minimum scale to fit the image inside the preview area - const double scaleFactorX = - ((double)_preview_width - pagePadding * 2 - blurRadius) / _preview_document_width; - const double scaleFactorY = - ((double)_preview_height - pagePadding * 2 - - shaddowOffsetY - halfBlurRadius - captionHeight) / _preview_document_height; - double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX; - scaleFactor = (scaleFactor > 1.0) ? 1.0 : scaleFactor; + const double scaleFactorX = ((double)_preview_width - pagePadding * 2 - blurRadius) / _preview_image_width; + const double scaleFactorY = ((double)_preview_height - pagePadding * 2 - shaddowOffsetY - halfBlurRadius - captionHeight) / _preview_image_height; + const double scaleFactor = (scaleFactorX > scaleFactorY) ? scaleFactorY : scaleFactorX; // Now get the resized values - const double scaledSvgWidth = scaleFactor * _preview_document_width; - const double scaledSvgHeight = scaleFactor * _preview_document_height; + const double scaledSvgWidth = scaleFactor * _preview_image_width; + const double scaledSvgHeight = scaleFactor * _preview_image_height; const int svgX = pagePadding + halfBlurRadius; const int svgY = pagePadding; @@ -1565,7 +1485,7 @@ int FileOpenDialogImplWin32::format_caption(wchar_t *caption, int caption_size) _wsplitpath(_path_string, NULL, NULL, szFileName, NULL); return snwprintf(caption, caption_size, - L"%s\n%d kB\n%d \xD7 %d", szFileName, _preview_file_size, + L"%ls\n%d\u2009kB\n%d\u2009px \xD7 %d\u2009px", szFileName, _preview_file_size, (int)_preview_document_width, (int)_preview_document_height); } @@ -1971,18 +1891,18 @@ UINT_PTR CALLBACK FileSaveDialogImplWin32::GetSaveFileName_hookproc( if(dlgFont) SendMessage(pImpl->_title_edit, WM_SETFONT, (WPARAM)dlgFont, MAKELPARAM(FALSE, 0)); SetWindowPos(pImpl->_title_edit, NULL, rCB1.left-rROOT.left, rCB1.top+ydelta-rROOT.top, rCB1.right-rCB1.left, rCB1.bottom-rCB1.top, SWP_SHOWWINDOW|SWP_NOZORDER); - // TODO: make sure this works for Unicode - SetWindowText(pImpl->_title_edit, pImpl->myDocTitle.c_str()); + SetWindowTextW(pImpl->_title_edit, + (const wchar_t*)g_utf8_to_utf16(pImpl->myDocTitle.c_str(), -1, NULL, NULL, NULL)); } } break; case WM_DESTROY: { if(pImpl->_title_edit) { - int length = GetWindowTextLength(pImpl->_title_edit)+1; - char* temp_title = new char[length]; - GetWindowText(pImpl->_title_edit, temp_title, length); - pImpl->myDocTitle = temp_title; + int length = GetWindowTextLengthW(pImpl->_title_edit)+1; + wchar_t* temp_title = new wchar_t[length]; + GetWindowTextW(pImpl->_title_edit, temp_title, length); + pImpl->myDocTitle = g_utf16_to_utf8((gunichar2*)temp_title, -1, NULL, NULL, NULL); delete[] temp_title; DestroyWindow(pImpl->_title_label); pImpl->_title_label = NULL; diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp index c349ffd78..82c6035d4 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.cpp +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.cpp @@ -34,32 +34,30 @@ FilletChamferPropertiesDialog::FilletChamferPropertiesDialog() { Gtk::Box *mainVBox = get_vbox(); mainVBox->set_homogeneous(false); - _layout_table.set_spacings(4); - _layout_table.resize(3, 3); + _layout_table.set_row_spacing(4); + _layout_table.set_column_spacing(4); // Layer name widgets _fillet_chamfer_position_numeric.set_digits(4); _fillet_chamfer_position_numeric.set_increments(1,1); //todo: get tha max aloable infinity freeze the widget _fillet_chamfer_position_numeric.set_range(0., SCALARPARAM_G_MAXDOUBLE); + _fillet_chamfer_position_numeric.set_hexpand(); _fillet_chamfer_position_label.set_label(_("Radius (pixels):")); _fillet_chamfer_position_label.set_alignment(1.0, 0.5); - _layout_table.attach(_fillet_chamfer_position_label, 0, 1, 0, 1, Gtk::FILL, - Gtk::FILL); - _layout_table.attach(_fillet_chamfer_position_numeric, 1, 2, 0, 1, - Gtk::FILL | Gtk::EXPAND, Gtk::FILL); + _layout_table.attach(_fillet_chamfer_position_label, 0, 0, 1, 1); + _layout_table.attach(_fillet_chamfer_position_numeric, 1, 0, 1, 1); _fillet_chamfer_chamfer_subdivisions.set_digits(0); _fillet_chamfer_chamfer_subdivisions.set_increments(1,1); //todo: get tha max aloable infinity freeze the widget _fillet_chamfer_chamfer_subdivisions.set_range(0, SCALARPARAM_G_MAXDOUBLE); + _fillet_chamfer_chamfer_subdivisions.set_hexpand(); _fillet_chamfer_chamfer_subdivisions_label.set_label(_("Chamfer subdivisions:")); _fillet_chamfer_chamfer_subdivisions_label.set_alignment(1.0, 0.5); - _layout_table.attach(_fillet_chamfer_chamfer_subdivisions_label, 0, 1, 1, 2, Gtk::FILL, - Gtk::FILL); - _layout_table.attach(_fillet_chamfer_chamfer_subdivisions, 1, 2, 1, 2, - Gtk::FILL | Gtk::EXPAND, Gtk::FILL); + _layout_table.attach(_fillet_chamfer_chamfer_subdivisions_label, 0, 1, 1, 1); + _layout_table.attach(_fillet_chamfer_chamfer_subdivisions, 1, 1, 1, 1); _fillet_chamfer_type_fillet.set_label(_("Fillet")); _fillet_chamfer_type_fillet.set_group(_fillet_chamfer_type_group); _fillet_chamfer_type_inverse_fillet.set_label(_("Inverse fillet")); diff --git a/src/ui/dialog/lpe-fillet-chamfer-properties.h b/src/ui/dialog/lpe-fillet-chamfer-properties.h index 105cafe68..4021d6152 100644 --- a/src/ui/dialog/lpe-fillet-chamfer-properties.h +++ b/src/ui/dialog/lpe-fillet-chamfer-properties.h @@ -51,7 +51,7 @@ protected: Gtk::Label _fillet_chamfer_chamfer_subdivisions_label; Gtk::SpinButton _fillet_chamfer_chamfer_subdivisions; - Gtk::Table _layout_table; + Gtk::Grid _layout_table; bool _position_visible; Gtk::Button _close_button; diff --git a/src/ui/dialog/lpe-powerstroke-properties.cpp b/src/ui/dialog/lpe-powerstroke-properties.cpp index ca10721db..0757aa1c3 100644 --- a/src/ui/dialog/lpe-powerstroke-properties.cpp +++ b/src/ui/dialog/lpe-powerstroke-properties.cpp @@ -40,14 +40,15 @@ PowerstrokePropertiesDialog::PowerstrokePropertiesDialog() { Gtk::Box *mainVBox = get_vbox(); - _layout_table.set_spacings(4); - _layout_table.resize (2, 2); + _layout_table.set_row_spacing(4); + _layout_table.set_column_spacing(4); // Layer name widgets _powerstroke_position_entry.set_activates_default(true); _powerstroke_position_entry.set_digits(4); _powerstroke_position_entry.set_increments(1,1); _powerstroke_position_entry.set_range(-SCALARPARAM_G_MAXDOUBLE, SCALARPARAM_G_MAXDOUBLE); + _powerstroke_position_entry.set_hexpand(); _powerstroke_position_label.set_label(_("Position:")); _powerstroke_position_label.set_alignment(1.0, 0.5); @@ -55,16 +56,14 @@ PowerstrokePropertiesDialog::PowerstrokePropertiesDialog() _powerstroke_width_entry.set_digits(4); _powerstroke_width_entry.set_increments(1,1); _powerstroke_width_entry.set_range(-SCALARPARAM_G_MAXDOUBLE, SCALARPARAM_G_MAXDOUBLE); + _powerstroke_width_entry.set_hexpand(); _powerstroke_width_label.set_label(_("Width:")); _powerstroke_width_label.set_alignment(1.0, 0.5); - _layout_table.attach(_powerstroke_position_label, - 0, 1, 0, 1, Gtk::FILL, Gtk::FILL); - _layout_table.attach(_powerstroke_position_entry, - 1, 2, 0, 1, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); - - _layout_table.attach(_powerstroke_width_label, 0, 1, 1, 2, Gtk::FILL, Gtk::FILL); - _layout_table.attach(_powerstroke_width_entry, 1, 2, 1, 2, Gtk::FILL | Gtk::EXPAND, Gtk::FILL); + _layout_table.attach(_powerstroke_position_label,0,0,1,1); + _layout_table.attach(_powerstroke_position_entry,1,0,1,1); + _layout_table.attach(_powerstroke_width_label, 0,1,1,1); + _layout_table.attach(_powerstroke_width_entry, 1,1,1,1); mainVBox->pack_start(_layout_table, true, true, 4); diff --git a/src/ui/dialog/lpe-powerstroke-properties.h b/src/ui/dialog/lpe-powerstroke-properties.h index 1e4c1df5b..56b6e499d 100644 --- a/src/ui/dialog/lpe-powerstroke-properties.h +++ b/src/ui/dialog/lpe-powerstroke-properties.h @@ -40,7 +40,7 @@ protected: Gtk::SpinButton _powerstroke_position_entry; Gtk::Label _powerstroke_width_label; Gtk::SpinButton _powerstroke_width_entry; - Gtk::Table _layout_table; + Gtk::Grid _layout_table; bool _position_visible; Gtk::Button _close_button; diff --git a/src/ui/dialog/prototype.cpp b/src/ui/dialog/prototype.cpp new file mode 100644 index 000000000..b3bf60aab --- /dev/null +++ b/src/ui/dialog/prototype.cpp @@ -0,0 +1,171 @@ +/* + * A bare minimum example of deriving from Inkscape::UI:Widget::Panel. + * + * Author: + * Tavmjong Bah + * + * Copyright (C) Tavmjong Bah <tavmjong@free.fr> + * + * Released under the GNU GPL, read the file 'COPYING' for more information. + */ + +#include "ui/dialog/prototype.h" +#include "verbs.h" +#include "desktop.h" +#include "document.h" +#include "selection.h" + +// Only for use in demonstration widget. +#include "sp-root.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +Prototype::Prototype() : + // UI::Widget::Panel("Prototype Label", "/dialogs/prototype", SP_VERB_DIALOG_PROTOTYPE, + // "Prototype Apply Label", true), + UI::Widget::Panel("Prototype Label", "/dialogs/prototype", SP_VERB_DIALOG_PROTOTYPE), + + desktopTracker() //, + // desktopChangedConnection() +{ + std::cout << "Prototype::Prototype()" << std::endl; + + // A widget for demonstration that displays the current SVG's id. + _getContents()->pack_start(label); // Panel::_getContents() + + // desktop is set by Panel constructor so this should never be NULL. + // Note, we need to use getDesktop() since _desktop is private in Panel.h. + // It should probably be protected instead... but need to verify in doesn't break anything. + if (getDesktop() == NULL) { + std::cerr << "Prototype::Prototype: desktop is NULL!" << std::endl; + } + + connectionDesktopChanged = desktopTracker.connectDesktopChanged( + sigc::mem_fun(*this, &Prototype::handleDesktopChanged) ); + desktopTracker.connect(GTK_WIDGET(gobj())); + + // This results in calling handleDocumentReplaced twice. Fix me! + connectionDocumentReplaced = getDesktop()->connectDocumentReplaced( + sigc::mem_fun(this, &Prototype::handleDocumentReplaced)); + + // Alternative mechanism but results in calling handleDocumentReplaced four times. + // signalDocumentReplaced().connect( + // sigc::mem_fun(this, &Prototype::handleDocumentReplaced)); + + connectionSelectionChanged = getDesktop()->getSelection()->connectChanged( + sigc::hide(sigc::mem_fun(this, &Prototype::handleSelectionChanged))); + + updateLabel(); +} + +Prototype::~Prototype() +{ + // Never actually called. + std::cout << "Prototype::~Prototype()" << std::endl; + connectionDesktopChanged.disconnect(); + connectionDocumentReplaced.disconnect(); + connectionSelectionChanged.disconnect(); +} + +/* + * Called when a dialog is displayed, including when a dialog is reopened. + * (When a dialog is closed, it is not destroyed so the contructor is not called. + * This function can handle any reinitialization needed.) + */ +void +Prototype::present() +{ + std::cout << "Prototype::present()" << std::endl; + UI::Widget::Panel::present(); +} + +/* + * When Inkscape is first opened, a default document is shown. If another document is immediately + * opened, it will replace the default document in the same desktop. This function handles the + * change. Bug: This is called twice for some reason. + */ +void +Prototype::handleDocumentReplaced(SPDesktop *desktop, SPDocument * /* document */) +{ + std::cout << "Prototype::handleDocumentReplaced()" << std::endl; + if (getDesktop() != desktop) { + std::cerr << "Prototype::handleDocumentReplaced(): Error: panel desktop not equal to existing desktop!" << std::endl; + } + + connectionSelectionChanged.disconnect(); + + connectionSelectionChanged = desktop->getSelection()->connectChanged( + sigc::hide(sigc::mem_fun(this, &Prototype::handleSelectionChanged))); + + // Update demonstration widget. + updateLabel(); +} + +/* + * When a dialog is floating, it is connected to the active desktop. + */ +void +Prototype::handleDesktopChanged(SPDesktop* desktop) { + std::cout << "Prototype::handleDesktopChanged(): " << desktop << std::endl; + + if (getDesktop() == desktop) { + // This will happen after construction of Prototype. We've already + // set up signals so just return. + std::cout << " getDesktop() == desktop" << std::endl; + return; + } + + // Connections are disconnect safe. + connectionSelectionChanged.disconnect(); + connectionDocumentReplaced.disconnect(); + + setDesktop( desktop ); + + connectionSelectionChanged = desktop->getSelection()->connectChanged( + sigc::hide(sigc::mem_fun(this, &Prototype::handleSelectionChanged))); + connectionDocumentReplaced = desktop->connectDocumentReplaced( + sigc::mem_fun(this, &Prototype::handleDocumentReplaced)); + + // Update demonstration widget. + updateLabel(); +} + +/* + * Handle a change in which objects are selected in a document. + */ +void +Prototype::handleSelectionChanged() { + std::cout << "Prototype::handleSelectionChanged()" << std::endl; + + // Update demonstration widget. + label.set_label("Selection Changed!"); +} + +/* + * Update label... just a utility function for this example. + */ +void +Prototype::updateLabel() { + + const gchar* root_id = getDesktop()->getDocument()->getRoot()->getId(); + Glib::ustring label_string("Document's SVG id: "); + label_string += (root_id?root_id:"null"); + label.set_label(label_string); +} + +} // 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/prototype.h b/src/ui/dialog/prototype.h new file mode 100644 index 000000000..95c3856f8 --- /dev/null +++ b/src/ui/dialog/prototype.h @@ -0,0 +1,80 @@ +/* + * A bare minimum example of deriving from Inkscape::UI:Widget::Panel. + * + * Author: + * Tavmjong Bah + * + * Copyright (C) Tavmjong Bah <tavmjong@free.fr> + * + * Released under the GNU GPL, read the file 'COPYING' for more information. + */ + +#ifndef SEEN_PROTOTYPE_PANEL_H +#define SEEN_PROTOTYPE_PANEL_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <iostream> +#include "ui/widget/panel.h" +#include "ui/dialog/desktop-tracker.h" + +// Only to display status. +#include <gtkmm/label.h> + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * A panel that does almost nothing! + */ +class Prototype : public UI::Widget::Panel +{ +public: + virtual ~Prototype(); + + static Prototype& getInstance() { return *new Prototype(); }; + + virtual void present(); + +private: + + // No default constructor, noncopyable, nonassignable + Prototype(); + Prototype(Prototype const &d); + Prototype operator=(Prototype const &d); + + // Signals and handlers + sigc::connection connectionDocumentReplaced; + sigc::connection connectionDesktopChanged; + sigc::connection connectionSelectionChanged; + + void handleDocumentReplaced(SPDesktop* desktop, SPDocument *document); + void handleDesktopChanged(SPDesktop* desktop); + void handleSelectionChanged(); + + DesktopTracker desktopTracker; + + // Just for example + Gtk::Label label; + void updateLabel(); +}; + +} //namespace Dialogs +} //namespace UI +} //namespace Inkscape + +#endif // SEEN_PROTOTYPE_PANEL_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/styledialog.cpp b/src/ui/dialog/styledialog.cpp new file mode 100644 index 000000000..4576671e7 --- /dev/null +++ b/src/ui/dialog/styledialog.cpp @@ -0,0 +1,1395 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * Tavmjong Bah + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "styledialog.h" +#include "ui/widget/addtoicon.h" +#include "widgets/icon.h" +#include "verbs.h" +#include "sp-object.h" +#include "selection.h" +#include "xml/attribute-record.h" +#include "xml/node-observer.h" +#include "attribute-rel-svg.h" +#include "inkscape.h" +#include "document-undo.h" + +#include <glibmm/i18n.h> +#include <glibmm/regex.h> + +#include <map> + +//#define DEBUG_STYLEDIALOG + +using Inkscape::DocumentUndo; +using Inkscape::Util::List; +using Inkscape::XML::AttributeRecord; + +/** + * This macro is used to remove spaces around selectors or any strings when + * parsing is done to update XML style element or row labels in this dialog. + */ +#define REMOVE_SPACES(x) x.erase(0, x.find_first_not_of(' ')); \ + x.erase(x.find_last_not_of(' ') + 1); + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class StyleDialog::NodeObserver : public Inkscape::XML::NodeObserver { +public: + NodeObserver(StyleDialog* styleDialog) : + _styleDialog(styleDialog) + { +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::NodeObserver: Constructor" << std::endl; +#endif + }; + + virtual void notifyContentChanged(Inkscape::XML::Node &node, + Inkscape::Util::ptr_shared<char> old_content, + Inkscape::Util::ptr_shared<char> new_content); + + StyleDialog * _styleDialog; +}; + + +void +StyleDialog::NodeObserver::notifyContentChanged( + Inkscape::XML::Node &/*node*/, + Inkscape::Util::ptr_shared<char> /*old_content*/, + Inkscape::Util::ptr_shared<char> /*new_content*/ ) { + +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::NodeObserver::notifyContentChanged" << std::endl; +#endif + + _styleDialog->_readStyleElement(); + _styleDialog->_selectRow(); +} + + +StyleDialog::TreeStore::TreeStore() +{ +} + + +/** + * Allow dragging only selectors. + */ +bool +StyleDialog::TreeStore::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::TreeStore::row_draggable_vfunc" << std::endl; +#endif + auto unconstThis = const_cast<StyleDialog::TreeStore*>(this); + const_iterator iter = unconstThis->get_iter(path); + if (iter) { + Gtk::TreeModel::Row row = *iter; + bool is_draggable = row[_styledialog->_mColumns._colIsSelector]; + return is_draggable; + } + return Gtk::TreeStore::row_draggable_vfunc(path); +} + + +/** + * Allow dropping only inbetween other selectors. + */ +bool +StyleDialog::TreeStore::row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, + const Gtk::SelectionData& selection_data) const +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::TreeStore::row_drop_possible_vfunc" << std::endl; +#endif + + Gtk::TreeModel::Path dest_parent = dest; + dest_parent.up(); + return dest_parent.empty(); +} + + +// This is only here to handle updating style element after a drag and drop. +void +StyleDialog::TreeStore::on_row_deleted(const TreeModel::Path& path) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "on_row_deleted" << std::endl; +#endif + + if (_styledialog->_updating) return; // Don't write if we deleted row (other than from DND) + + _styledialog->_writeStyleElement(); +} + + +Glib::RefPtr<StyleDialog::TreeStore> StyleDialog::TreeStore::create(StyleDialog *styledialog) +{ + StyleDialog::TreeStore * store = new StyleDialog::TreeStore(); + store->_styledialog = styledialog; + store->set_column_types( store->_styledialog->_mColumns ); + return Glib::RefPtr<StyleDialog::TreeStore>( store ); +} + +/** + * Constructor + * A treeview and a set of two buttons are added to the dialog. _addSelector + * adds selectors to treeview. _delSelector deletes the selector from the dialog. + * Any addition/deletion of the selectors updates XML style element accordingly. + */ +StyleDialog::StyleDialog() : + UI::Widget::Panel("", "/dialogs/style", SP_VERB_DIALOG_STYLE), + _updating(false), + _textNode(NULL), + _desktopTracker() +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::StyleDialog" << std::endl; +#endif + + // Tree + Inkscape::UI::Widget::AddToIcon * addRenderer = manage( + new Inkscape::UI::Widget::AddToIcon() ); + + _store = TreeStore::create(this); + _treeView.set_model(_store); + _treeView.set_headers_visible(true); + _treeView.enable_model_drag_source(); + _treeView.enable_model_drag_dest( Gdk::ACTION_MOVE ); + + int addCol = _treeView.append_column("", *addRenderer) - 1; + Gtk::TreeViewColumn *col = _treeView.get_column(addCol); + if ( col ) { + col->add_attribute( addRenderer->property_active(), _mColumns._colIsSelector ); + } + _treeView.append_column("CSS Selector", _mColumns._colSelector); + _treeView.set_expander_column(*(_treeView.get_column(1))); + + // Pack widgets + _paned.pack1(_mainBox, Gtk::SHRINK); + _mainBox.pack_start(_scrolledWindow, Gtk::PACK_EXPAND_WIDGET); + _scrolledWindow.add(_treeView); + _scrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + + create = manage( new Gtk::Button() ); + _styleButton(*create, "list-add", "Add a new CSS Selector"); + create->signal_clicked().connect(sigc::mem_fun(*this, &StyleDialog::_addSelector)); + + del = manage( new Gtk::Button() ); + _styleButton(*del, "list-remove", "Remove a CSS Selector"); + del->signal_clicked().connect(sigc::mem_fun(*this, &StyleDialog::_delSelector)); + del->set_sensitive(false); + + _mainBox.pack_end(_buttonBox, Gtk::PACK_SHRINK); + + _buttonBox.pack_start(*create, Gtk::PACK_SHRINK); + _buttonBox.pack_start(*del, Gtk::PACK_SHRINK); + + _getContents()->pack_start(_paned, Gtk::PACK_EXPAND_WIDGET); + + // Dialog size request + Gtk::Requisition sreq1, sreq2; + get_preferred_size(sreq1, sreq2); + int minWidth = 300; + int minHeight = 300; + minWidth = (sreq2.width > minWidth ? sreq2.width : minWidth ); + minHeight = (sreq2.height > minHeight ? sreq2.height : minHeight); + set_size_request(minWidth, minHeight); + + // Signal handlers + _treeView.signal_button_release_event().connect( // Needs to be release, not press. + sigc::mem_fun(*this, &StyleDialog::_handleButtonEvent), + false); + + _treeView.signal_button_release_event().connect_notify( + sigc::mem_fun(*this, &StyleDialog::_buttonEventsSelectObjs), + false); + + //_treeView.get_selection()->signal_changed().connect( + // sigc::mem_fun(*this, &StyleDialog::_selChanged)); + + _objObserver.signal_changed().connect(sigc::mem_fun(*this, &StyleDialog::_objChanged)); + + + // Add CSS dialog + _cssPane = new CssDialog; + _paned.pack2(*_cssPane, Gtk::SHRINK); + _cssPane->show_all(); + + _cssPane->_propRenderer->signal_edited().connect( + sigc::mem_fun(*this, &StyleDialog::_handleProp)); + _cssPane->_sheetRenderer->signal_edited().connect( + sigc::mem_fun(*this, &StyleDialog::_handleSheet)); + _cssPane->_attrRenderer->signal_edited().connect( + sigc::mem_fun(*this, &StyleDialog::_handleAttr)); + _cssPane->_treeView.signal_button_release_event().connect( + sigc::mem_fun(*this, &StyleDialog::_delProperty), + false); + + // Document & Desktop + _desktop_changed_connection = _desktopTracker.connectDesktopChanged( + sigc::mem_fun(*this, &StyleDialog::_handleDesktopChanged) ); + _desktopTracker.connect(GTK_WIDGET(gobj())); + + _document_replaced_connection = getDesktop()->connectDocumentReplaced( + sigc::mem_fun(this, &StyleDialog::_handleDocumentReplaced)); + + _selection_changed_connection = getDesktop()->getSelection()->connectChanged( + sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged))); + + // Load tree + _readStyleElement(); + _selectRow(); + + if (!_store->children().empty()) { + del->set_sensitive(true); + } + +} + + +/** + * @brief StyleDialog::~StyleDialog + * Class destructor + */ +StyleDialog::~StyleDialog() +{ +#ifdef DEBUG_STYLEDIALOOG + std::cout << "StyleDialog::~StyleDialog" << std::endl; +#endif + _desktop_changed_connection.disconnect(); + _document_replaced_connection.disconnect(); + _selection_changed_connection.disconnect(); +} + + +/** + * @brief StyleDialog::_styleTextNode + * @return Inkscape::XML::Node* pointing to a style element's text node. + * Returns the style element's text node. If there is no style element, one is created. + * Ditto for text node. + */ +Inkscape::XML::Node* StyleDialog::_getStyleTextNode() +{ + + Inkscape::XML::Node *styleNode = NULL; + Inkscape::XML::Node *textNode = NULL; + + Inkscape::XML::Node *root = SP_ACTIVE_DOCUMENT->getReprRoot(); + for (unsigned i = 0; i < root->childCount(); ++i) { + if (Glib::ustring(root->nthChild(i)->name()) == "svg:style") { + + styleNode = root->nthChild(i); + + for (unsigned j = 0; j < styleNode->childCount(); ++j) { + if (styleNode->nthChild(j)->type() == Inkscape::XML::TEXT_NODE) { + textNode = styleNode->nthChild(j); + } + } + + if (textNode == NULL) { + // Style element found but does not contain text node! + std::cerr << "StyleDialog::_getStyleTextNode(): No text node!" << std::endl; + textNode = SP_ACTIVE_DOCUMENT->getReprDoc()->createTextNode(""); + styleNode->appendChild(textNode); + Inkscape::GC::release(textNode); + } + } + } + + if (styleNode == NULL) { + // Style element not found, create one + styleNode = SP_ACTIVE_DOCUMENT->getReprDoc()->createElement("svg:style"); + textNode = SP_ACTIVE_DOCUMENT->getReprDoc()->createTextNode(""); + + styleNode->appendChild(textNode); + Inkscape::GC::release(textNode); + + root->addChild(styleNode, NULL); + Inkscape::GC::release(styleNode); + } + + if (_textNode != textNode) { + _textNode = textNode; + NodeObserver *no = new NodeObserver(this); + textNode->addObserver(*no); + } + + return textNode; +} + + +/** + * @brief StyleDialog::_readStyleElement + * Fill the Gtk::TreeStore from the svg:style element. + */ +void StyleDialog::_readStyleElement() +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_readStyleElement: updating " << (_updating?"true":"false")<< std::endl; +#endif + + if (_updating) return; // Don't read if we wrote style element. + _updating = true; + _store->clear(); + + Inkscape::XML::Node * textNode = _getStyleTextNode(); + if (textNode == NULL) { + std::cerr << "StyleDialog::_readStyleElement: No text node!" << std::endl; + } + + // Get content from style text node. + std::string content = (textNode->content() ? textNode->content() : ""); + + // Remove end-of-lines (check it works on Windoze). + content.erase(std::remove(content.begin(), content.end(), '\n'), content.end()); + + // First split into selector/value chunks. + // An attempt to use Glib::Regex failed. A C++11 version worked but + // reportedly has problems on Windows. Using split_simple() is simpler + // and probably faster. + // + // Glib::RefPtr<Glib::Regex> regex1 = + // Glib::Regex::create("([^\\{]+)\\{([^\\{]+)\\}"); + // + // Glib::MatchInfo minfo; + // regex1->match(content, minfo); + + // Split on curly brackets. Even tokens are selectors, odd are values. + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[}{]", content); + + // If text node is empty, return (avoids problem with negative below). + if (tokens.size() == 0) { + _updating = false; + return; + } + + for (unsigned i = 0; i < tokens.size()-1; i += 2) { + + Glib::ustring selector = tokens[i]; + REMOVE_SPACES(selector); // Remove leading/trailing spaces + + // Get list of objects selector matches + std::vector<SPObject *> objVec = _getObjVec( selector ); + + Glib::ustring properties; + // Check to make sure we do have a value to match selector. + if ((i+1) < tokens.size()) { + properties = tokens[i+1]; + } else { + std::cerr << "StyleDialog::_readStyleElement: Missing values " + "for last selector!" << std::endl; + } + REMOVE_SPACES(properties); + + Gtk::TreeModel::Row row = *(_store->append()); + row[_mColumns._colSelector] = selector; + row[_mColumns._colIsSelector] = true; + row[_mColumns._colObj] = objVec; + row[_mColumns._colProperties] = properties; + + // Add as children, objects that match selector. + for (auto& obj: objVec) { + Gtk::TreeModel::Row childrow = *(_store->append(row->children())); + childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId()); + childrow[_mColumns._colIsSelector] = false; + childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj); + childrow[_mColumns._colProperties] = ""; // Unused + } + } + _updating = false; +} + + +/** + * @brief StyleDialog::_writeStyleElement + * Update the content of the style element as selectors (or objects) are added/removed. + */ +void StyleDialog::_writeStyleElement() +{ + _updating = true; + + Glib::ustring styleContent; + for (auto& row: _store->children()) { + styleContent = styleContent + row[_mColumns._colSelector] + + " { " + row[_mColumns._colProperties] + " }\n"; + } + // We could test if styleContent is empty and then delete the style node here but there is no + // harm in keeping it around ... + + Inkscape::XML::Node *textNode = _getStyleTextNode(); + textNode->setContent(styleContent.c_str()); + + DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_STYLE, _("Edited style element.")); + + _updating = false; +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_writeStyleElement(): |" << styleContent << "|" << std::endl; +#endif +} + + +/** + * @brief StyleDialog::_addToSelector + * @param row + * Add selected objects on the desktop to the selector corresponding to 'row'. + */ +void StyleDialog::_addToSelector(Gtk::TreeModel::Row row) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_addToSelector: Entrance" << std::endl; +#endif + if (*row) { + + Glib::ustring selector = row[_mColumns._colSelector]; + + if (selector[0] == '#') { + // 'id' selector... add selected object's id's to list. + Inkscape::Selection* selection = getDesktop()->getSelection(); + for (auto& obj: selection->objects()) { + + Glib::ustring id = (obj->getId()?obj->getId():""); + + std::vector<SPObject *> objVec = row[_mColumns._colObj]; + bool found = false; + for (auto& obj: objVec) { + if (id == obj->getId()) { + found = true; + break; + } + } + + if (!found) { + // Update row + objVec.push_back(obj); // Adding to copy so need to update tree + row[_mColumns._colObj] = objVec; + row[_mColumns._colSelector] = _getIdList( objVec ); + + // Add child row + Gtk::TreeModel::Row childrow = *(_store->append(row->children())); + childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId()); + childrow[_mColumns._colIsSelector] = false; + childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj); + childrow[_mColumns._colProperties] = ""; // Unused + } + } + } + + else if (selector[0] == '.') { + // 'class' selector... add value to class attribute of selected objects. + + // Get first class (split on white space or comma) + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[,\\s]+", selector); + Glib::ustring className = tokens[0]; + className.erase(0,1); + + // Get list of objects to modify + Inkscape::Selection* selection = getDesktop()->getSelection(); + std::vector<SPObject *> objVec( selection->objects().begin(), + selection->objects().end() ); + + _insertClass( objVec, className ); + + row[_mColumns._colObj] = _getObjVec( selector ); + + for (auto& obj: objVec) { + // Add child row + Gtk::TreeModel::Row childrow = *(_store->append(row->children())); + childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId()); + childrow[_mColumns._colIsSelector] = false; + childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj); + childrow[_mColumns._colProperties] = ""; // Unused + } + } + + else { + // Do nothing for element selectors. + // std::cout << " Element selector... doing nothing!" << std::endl; + } + } + + _writeStyleElement(); +} + + +/** + * @brief StyleDialog::_removeFromSelector + * @param row + * Remove the object corresponding to 'row' from the parent selector. + */ +void StyleDialog::_removeFromSelector(Gtk::TreeModel::Row row) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_removeFromSelector: Entrance" << std::endl; +#endif + if (*row) { + + Glib::ustring objectLabel = row[_mColumns._colSelector]; + Gtk::TreeModel::iterator iter = row->parent(); + if (iter) { + Gtk::TreeModel::Row parent = *iter; + Glib::ustring selector = parent[_mColumns._colSelector]; + + if (selector[0] == '#') { + // 'id' selector... remove selected object's id's to list. + + // Erase from selector label. + auto i = selector.find(objectLabel); + if (i != Glib::ustring::npos) { + selector.erase(i, objectLabel.length()); + } + // Erase any comma/space + if (i != Glib::ustring::npos && selector[i] == ',') { + selector.erase(i, 1); + } + if (i != Glib::ustring::npos && selector[i] == ' ') { + selector.erase(i, 1); + } + + // Update store + if (selector.empty()) { + _store->erase(parent); + } else { + // Save new selector and update object vector. + parent[_mColumns._colSelector] = selector; + parent[_mColumns._colObj] = _getObjVec( selector ); + _store->erase(row); + } + } + + else if (selector[0] == '.') { + // 'class' selector... remove value to class attribute of selected objects. + + std::vector<SPObject *> objVec = row[_mColumns._colObj]; // Just one + + // Get first class (split on white space or comma) + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("[,\\s]+", selector); + Glib::ustring className = tokens[0]; + className.erase(0,1); // Erase '.' + + // Erase class name from 'class' attribute. + Glib::ustring classAttr = objVec[0]->getRepr()->attribute("class"); + auto i = classAttr.find( className ); + if (i != Glib::ustring::npos) { + classAttr.erase(i, className.length()); + } + if (i != Glib::ustring::npos && classAttr[i] == ' ') { + classAttr.erase(i, 1); + } + objVec[0]->getRepr()->setAttribute("class", classAttr); + + parent[_mColumns._colObj] = _getObjVec( selector ); + _store->erase(row); + } + + else { + // Do nothing for element selectors. + // std::cout << " Element selector... doing nothing!" << std::endl; + } + } + } + + _writeStyleElement(); + +} + + +/** + * @brief StyleDialog::_getIdList + * @param sel + * @return This function returns a comma seperated list of ids for objects in input vector. + * It is used in creating an 'id' selector. It relies on objects having 'id's. + */ +Glib::ustring StyleDialog::_getIdList(std::vector<SPObject*> sel) +{ + Glib::ustring str; + for (auto& obj: sel) { + str += "#" + Glib::ustring(obj->getId()) + ", "; + } + if (!str.empty()) { + str.erase(str.size()-1); // Remove space at end. c++11 has pop_back() but not ustring. + str.erase(str.size()-1); // Remove comma at end. + } + return str; +} + +/** + * @brief StyleDialog::_getObjVec + * @param selector: a valid CSS selector string. + * @return objVec: a vector of pointers to SPObject's the selector matches. + * Return a vector of all objects that selector matches. + */ +std::vector<SPObject *> StyleDialog::_getObjVec(Glib::ustring selector) { + + std::vector<SPObject *> objVec = SP_ACTIVE_DOCUMENT->getObjectsBySelector( selector ); + +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_getObjVec: |" << selector << "|" << std::endl; + for (auto& obj: objVec) { + std::cout << " " << (obj->getId()?obj->getId():"null") << std::endl; + } +#endif + + return objVec; +} + + +/** + * @brief StyleDialog::_insertClass + * @param objs: list of objects to insert class + * @param class: class to insert + * Insert a class name into objects' 'class' attribute. + */ +void StyleDialog::_insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className) { + + for (auto& obj: objVec) { + + if (!obj->getRepr()->attribute("class")) { + // 'class' attribute does not exist, create it. + obj->getRepr()->setAttribute("class", className); + } else { + // 'class' attribute exists, append. + Glib::ustring classAttr = obj->getRepr()->attribute("class"); + + // Split on white space. + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s+", classAttr); + bool add = true; + for (auto& token: tokens) { + if (token == className) { + add = false; // Might be useful to still add... + break; + } + } + if (add) { + obj->getRepr()->setAttribute("class", classAttr + " " + className ); + } + } + } + } + + +/** + * @brief StyleDialog::_selectObjects + * @param eventX + * @param eventY + * This function selects objects in the drawing corresponding to the selector + * selected in the treeview. + */ +void StyleDialog::_selectObjects(int eventX, int eventY) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_selectObjects: " << eventX << ", " << eventY << std::endl; +#endif + + getDesktop()->selection->clear(); + Gtk::TreeViewColumn *col = _treeView.get_column(1); + Gtk::TreeModel::Path path; + int x2 = 0; + int y2 = 0; + // To do: We should be able to do this via passing in row. + if (_treeView.get_path_at_pos(eventX, eventY, path, col, x2, y2)) { + if (col == _treeView.get_column(1)) { + Gtk::TreeModel::iterator iter = _store->get_iter(path); + if (iter) { + Gtk::TreeModel::Row row = *iter; + Gtk::TreeModel::Children children = row.children(); + std::vector<SPObject *> objVec = row[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { + SPObject *obj = objVec[i]; + getDesktop()->selection->add(obj); + } + } + } + } +} + + +/** + * @brief StyleDialog::_addSelector + * + * This function opens a dialog to add a selector. The dialog is prefilled + * with an 'id' selector containing a list of the id's of selected objects + * or with a 'class' selector if no objects are selected. + */ +void StyleDialog::_addSelector() +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_addSelector: Entrance" << std::endl; +#endif + + // Store list of selected elements on desktop (not to be confused with selector). + Inkscape::Selection* selection = getDesktop()->getSelection(); + std::vector<SPObject *> objVec( selection->objects().begin(), + selection->objects().end() ); + + // ==== Create popup dialog ==== + Gtk::Dialog *textDialogPtr = new Gtk::Dialog(); + textDialogPtr->add_button(_("Cancel"), Gtk::RESPONSE_CANCEL); + textDialogPtr->add_button(_("Add"), Gtk::RESPONSE_OK); + + Gtk::Entry *textEditPtr = manage ( new Gtk::Entry() ); + textDialogPtr->get_vbox()->pack_start(*textEditPtr, Gtk::PACK_SHRINK); + + Gtk::Label *textLabelPtr = manage ( new Gtk::Label( + _("Invalid entry: Not an id (#), class (.), or element CSS selector.") + ) ); + textDialogPtr->get_vbox()->pack_start(*textLabelPtr, Gtk::PACK_SHRINK); + + /** + * By default, the entrybox contains 'Class1' as text. However, if object(s) + * is(are) selected and user clicks '+' at the bottom of dialog, the + * entrybox will have the id(s) of the selected objects as text. + */ + if (getDesktop()->getSelection()->isEmpty()) { + textEditPtr->set_text(".Class1"); + } else { + textEditPtr->set_text(_getIdList(objVec)); + } + + Gtk::Requisition sreq1, sreq2; + textDialogPtr->get_preferred_size(sreq1, sreq2); + int minWidth = 200; + int minHeight = 100; + minWidth = (sreq2.width > minWidth ? sreq2.width : minWidth ); + minHeight = (sreq2.height > minHeight ? sreq2.height : minHeight); + textDialogPtr->set_size_request(minWidth, minHeight); + textEditPtr->show(); + textLabelPtr->hide(); + textDialogPtr->show(); + + + // ==== Get response ==== + int result = -1; + bool invalid = true; + Glib::ustring selectorValue; + + while (invalid) { + result = textDialogPtr->run(); + if (result != Gtk::RESPONSE_OK) { // Cancel, close dialog, etc. + textDialogPtr->hide(); + return; + } + /** + * @brief selectorName + * This string stores selector name. The text from entrybox is saved as name + * for selector. If the entrybox is empty, the text (thus selectorName) is + * set to ".Class1" + */ + selectorValue = textEditPtr->get_text(); + Glib::ustring firstWord = selectorValue.substr(0, selectorValue.find(" ")); + + del->set_sensitive(true); + + if (selectorValue[0] == '.' || + selectorValue[0] == '#' || + selectorValue[0] == '*' || + SPAttributeRelSVG::isSVGElement( firstWord ) ) { + invalid = false; + } else { + textLabelPtr->show(); + } + } + delete textDialogPtr; + + // ==== Handle response ==== + + // If class selector, add selector name to class attribute for each object + if (selectorValue[0] == '.') { + + Glib::ustring className = selectorValue; + className.erase(0,1); + _insertClass(objVec, className); + } + + // Generate a new object vector (we could have an element selector, + // the user could have edited the id selector list, etc.). + objVec = _getObjVec( selectorValue ); + + // Add entry to GUI tree + Gtk::TreeModel::Row row = *(_store->append()); + row[_mColumns._colSelector] = selectorValue; + row[_mColumns._colIsSelector] = true; + row[_mColumns._colObj] = objVec; + + // Add as children objects that match selector. + for (auto& obj: objVec) { + Gtk::TreeModel::Row childrow = *(_store->append(row->children())); + childrow[_mColumns._colSelector] = "#" + Glib::ustring(obj->getId()); + childrow[_mColumns._colIsSelector] = false; + childrow[_mColumns._colObj] = std::vector<SPObject *>(1, obj); + } + + // Add entry to style element + _writeStyleElement(); +} + +/** + * @brief StyleDialog::_delSelector + * This function deletes selector when '-' at the bottom is clicked. + * Note: If deleting a class selector, class attributes are NOT changed. + */ +void StyleDialog::_delSelector() +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_delSelector" << std::endl; +#endif + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + _updating = true; + _store->erase(iter); + _updating = false; + _writeStyleElement(); + } +} + +/** + * @brief StyleDialog::_handleButtonEvent + * @param event + * @return + * Handles the event when '+' button in front of a selector name is clicked or when a '-' button in + * front of a child object is clicked. In the first case, the selected objects on the desktop (if + * any) are added as children of the selector in the treeview. In the latter case, the object + * corresponding to the row is removed from the selector. + */ +bool StyleDialog::_handleButtonEvent(GdkEventButton *event) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_handleButtonEvent: Entrance" << std::endl; +#endif + if (event->type == GDK_BUTTON_RELEASE && event->button == 1) { + Gtk::TreeViewColumn *col = 0; + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + if (_treeView.get_path_at_pos(x, y, path, col, x2, y2)) { + if (col == _treeView.get_column(0)) { + Gtk::TreeModel::iterator iter = _store->get_iter(path); + Gtk::TreeModel::Row row = *iter; + + // Add or remove objects from a + if (!row.parent()) { + // Add selected objects to selector. + _addToSelector(row); + } else { + // Remove object from selector + _removeFromSelector(row); + } + } + } + } + return false; +} + +// ------------------------------------------------------------------- + +class PropertyData +{ +public: + PropertyData() {}; + PropertyData(Glib::ustring name) : _name(name) {}; + + void _setSheetValue(Glib::ustring value) { _sheetValue = value; }; + void _setAttrValue(Glib::ustring value) { _attrValue = value; }; + Glib::ustring _getName() { return _name; }; + Glib::ustring _getSheetValue() { return _sheetValue; }; + Glib::ustring _getAttrValue() { return _attrValue; }; + +private: + Glib::ustring _name; + Glib::ustring _sheetValue; + Glib::ustring _attrValue; +}; + +// ------------------------------------------------------------------- + + +/** + * @brief StyleDialog::_updateCSSPanel + * Updates CSS panel according to row in Style panel. + */ +void StyleDialog::_updateCSSPanel() +{ + // This should probably be in a member function of CSSDialog. +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_updateCSSPanel" << std::endl; +#endif + _updating = true; + + _cssPane->_store->clear(); + + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + Glib::ustring properties; + Glib::ustring sheet; + Glib::ustring attr; + if (row[_mColumns._colIsSelector]) { + _cssPane->_propRenderer->property_editable() = true; + _cssPane->_sheetRenderer->property_editable() = true; + _cssPane->_sheetRenderer->property_foreground_rgba() = Gdk::RGBA("black"); + _cssPane->_attrRenderer->property_editable() = false; + _cssPane->_buttonAddProperty.set_sensitive(true); + + properties = row[_mColumns._colProperties]; + sheet = row[_mColumns._colProperties]; + _objObserver.set( NULL ); + } else { + _cssPane->_propRenderer->property_editable() = false; + _cssPane->_sheetRenderer->property_editable() = false; + _cssPane->_sheetRenderer->property_foreground_rgba() = Gdk::RGBA("gray"); + _cssPane->_attrRenderer->property_editable() = false; // false for now... + _cssPane->_buttonAddProperty.set_sensitive(false); + + std::vector<SPObject *> objects = row[_mColumns._colObj]; + Gtk::TreeModel::iterator piter = row.parent(); + if (piter) { + Gtk::TreeModel::Row prow = *piter; + sheet = prow[_mColumns._colProperties]; + } + _objObserver.set( objects[0] ); + if (objects[0] && objects[0]->getAttribute("style") != NULL) { + properties = objects[0]->getAttribute("style"); + attr = objects[0]->getAttribute("style"); + } + } + REMOVE_SPACES(properties); // Remove leading/trailing spaces + + std::map<Glib::ustring, PropertyData> propMap; + + std::vector<Glib::ustring> sheetList = Glib::Regex::split_simple("\\s*;\\s*", sheet); + for (auto& token: sheetList) { + + if (token.empty()) break; + + std::vector<Glib::ustring> pair = + Glib::Regex::split_simple("\\s*:\\s*", token); + if( pair.size() > 1) { + PropertyData temp( pair[0] ); + temp._setSheetValue( pair[1] ); + propMap[pair[0]] = temp; + } + } + + std::vector<Glib::ustring> attrList = Glib::Regex::split_simple("\\s*;\\s*", attr); + for (auto& token: attrList) { + + if (token.empty()) break; + + std::vector<Glib::ustring> pair = + Glib::Regex::split_simple("\\s*:\\s*", token); + + if( pair.size() > 1) { + auto it = propMap.find(pair[0]); + if (it != propMap.end()) { + (*it).second._setAttrValue( pair[1] ); + } else { + PropertyData temp(pair[0]); + temp._setAttrValue( pair[1] ); + propMap[pair[0]] = temp; + } + } + } + + for (auto it : propMap) { + // std::cout << " " << it.first + // << " " << it.second._getName() + // << " " << it.second._getSheetValue() + // << " " << it.second._getAttrValue() + // << std::endl; + _cssPane->_propRow = *(_cssPane->_store->append()); + _cssPane->_propRow[_cssPane->_cssColumns._colUnsetProp] = false; + _cssPane->_propRow[_cssPane->_cssColumns._propertyLabel] = it.second._getName(); + _cssPane->_propRow[_cssPane->_cssColumns._styleSheetVal] = it.second._getSheetValue(); + _cssPane->_propRow[_cssPane->_cssColumns._styleAttrVal ] = it.second._getAttrValue(); + } + } + + _updating = false; +} + + +/** + * Handle document replaced. (Happens when a default document is immediately replaced by another + * document in a new window.) + */ +void +StyleDialog::_handleDocumentReplaced(SPDesktop *desktop, SPDocument * /* document */) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::handleDocumentReplaced()" << std::endl; +#endif + + _selection_changed_connection.disconnect(); + + _selection_changed_connection = desktop->getSelection()->connectChanged( + sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged))); + + _readStyleElement(); + _selectRow(); +} + + +/* + * When a dialog is floating, it is connected to the active desktop. + */ +void +StyleDialog::_handleDesktopChanged(SPDesktop* desktop) { +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::handleDesktopReplaced()" << std::endl; +#endif + + if (getDesktop() == desktop) { + // This will happen after construction of dialog. We've already + // set up signals so just return. + return; + } + + _selection_changed_connection.disconnect(); + _document_replaced_connection.disconnect(); + + setDesktop( desktop ); + + _selection_changed_connection = desktop->getSelection()->connectChanged( + sigc::hide(sigc::mem_fun(this, &StyleDialog::_handleSelectionChanged))); + _document_replaced_connection = desktop->connectDocumentReplaced( + sigc::mem_fun(this, &StyleDialog::_handleDocumentReplaced)); + + _readStyleElement(); + _selectRow(); +} + + +/* + * Handle a change in which objects are selected in a document. + */ +void +StyleDialog::_handleSelectionChanged() { +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_handleSelectionChanged()" << std::endl; +#endif + + _selectRow(); +} + + +/** + * @brief StyleDialog::_buttonEventsSelectObjs + * @param event + * This function detects single or double click on a selector in any row. Clicking + * on a selector selects the matching objects on the desktop. A double click will + * in addition open the CSS dialog. + */ +void StyleDialog::_buttonEventsSelectObjs(GdkEventButton* event ) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_buttonEventsSelectObjs" << std::endl; +#endif + + _updating = true; + + if (event->type == GDK_BUTTON_RELEASE && event->button == 1) { + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + _selectObjects(x, y); + //} + //else if (event->type == GDK_2BUTTON_PRESS && event->button == 1) { + //int x = static_cast<int>(event->x); + //int y = static_cast<int>(event->y); + //_selectObjects(x, y); + + _updateCSSPanel(); + } + _updating = false; +} + + +/** + * @brief StyleDialog::_selectRow + * This function selects the row in treeview corresponding to an object selected + * in the drawing. If more than one row matches, the first is chosen. + */ +void StyleDialog::_selectRow() +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_selectRow: updating: " << (_updating?"true":"false") << std::endl; +#endif + if (_updating || !getDesktop()) return; // Avoid updating if we have set row via dialog. + + if (SP_ACTIVE_DESKTOP != getDesktop()) { + std::cerr << "StyleDialog::_selectRow: SP_ACTIVE_DESKTOP != getDesktop()" << std::endl; + return; + } + + Inkscape::Selection* selection = getDesktop()->getSelection(); + if (!selection->isEmpty()) { + SPObject *obj = selection->objects().back(); + + Gtk::TreeModel::Children children = _store->children(); + for(Gtk::TreeModel::Children::iterator iter = children.begin(); + iter != children.end(); ++iter) { + + Gtk::TreeModel::Row row = *iter; + std::vector<SPObject *> objVec = row[_mColumns._colObj]; + for (unsigned i = 0; i < objVec.size(); ++i) { + if (obj->getId() == objVec[i]->getId()) { + _treeView.get_selection()->select(row); + _updateCSSPanel(); + return; + } + } + } + } + + // Selection empty or no row matches. + _treeView.get_selection()->unselect_all(); + _updateCSSPanel(); +} + + +void StyleDialog::_objChanged() { +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_objChanged" << std::endl; +#endif + if (_updating) return; + _updateCSSPanel(); +} + + +/** + * @brief StyleDialog::_handleProp + * @param path + * @param new_text + * Called when new text is entered into a "prop" cell.. + */ +void StyleDialog::_handleProp(const Glib::ustring& path, const Glib::ustring& new_text) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_handleProp: path: " << path + << " new_text: " << new_text << std::endl; +#endif + + Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path); + if (iterCss) { + Gtk::TreeModel::Row row = *iterCss; + row[_cssPane->_cssColumns._propertyLabel] = new_text; + } + + // To do: validate. +} + +/** + * @brief StyleDialog::_handleSheet + * @param path + * @param new_text + * Called when new text is entered into a "sheet" cell.. + */ +void StyleDialog::_handleSheet(const Glib::ustring& path, const Glib::ustring& new_text) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_handleSheet: path: " << path + << " new_text: " << new_text << std::endl; +#endif + + Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path); + if (iterCss) { + Gtk::TreeModel::Row row = *iterCss; + row[_cssPane->_cssColumns._styleSheetVal] = new_text; + } + + // To do: validate (run through style.read()/style.write()?). + + Glib::ustring properties; + for (auto& crow: _cssPane->_store->children()) { + properties = properties + + crow[_cssPane->_cssColumns._propertyLabel] + ": " + + crow[_cssPane->_cssColumns._styleSheetVal] + "; "; + } + + // Update selector data. + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + row[_mColumns._colProperties] = properties; + _writeStyleElement(); + } +} + +/** + * @brief StyleDialog::_handleAttr + * @param path + * @param new_text + * Called when new text is entered into an "attr" cell.. + */ +void StyleDialog::_handleAttr(const Glib::ustring& path, const Glib::ustring& new_text) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_handleAttr: path: " << path + << " new_text: " << new_text << std::endl; +#endif + + Gtk::TreeModel::iterator iterCss = _cssPane->_treeView.get_model()->get_iter(path); + if (iterCss) { + Gtk::TreeModel::Row row = *iterCss; + row[_cssPane->_cssColumns._styleAttrVal] = new_text; + } + + // To do: validate (run through style.read()/style.write()?). + + Glib::ustring properties; + for (auto& crow: _cssPane->_store->children()) { + properties = properties + + crow[_cssPane->_cssColumns._propertyLabel] + ": "; + crow[_cssPane->_cssColumns._styleAttrVal] + "; "; + } + + std::cout << "StyleDialog::_handlerAttr(): Unimplemented write." << std::endl; +} + +/** + * @brief StyleDialog::_delProperty + * @param event + * @return + * Delete a property from the CSS dialog and then update trees. + */ +bool StyleDialog::_delProperty(GdkEventButton *event) +{ +#ifdef DEBUG_STYLEDIALOG + std::cout << "StyleDialog::_delProperty" << std::endl; +#endif + + if (event->type == GDK_BUTTON_RELEASE && event->button == 1) { + Gtk::TreeViewColumn *col = 0; + Gtk::TreeModel::Path path; + int x = static_cast<int>(event->x); + int y = static_cast<int>(event->y); + int x2 = 0; + int y2 = 0; + Gtk::TreeModel::Row cssRow; + Glib::ustring toDelProperty; + if (_cssPane->_treeView.get_path_at_pos(x, y, path, col, x2, y2)) { + if (col == _cssPane->_treeView.get_column(0)) { + Gtk::TreeModel::iterator cssIter = + _cssPane->_treeView.get_selection()->get_selected(); + if (cssIter) { + + Gtk::TreeModel::Row cssRow = *cssIter; + + // Update selector data. + Glib::RefPtr<Gtk::TreeSelection> refTreeSelection = _treeView.get_selection(); + Gtk::TreeModel::iterator iter = refTreeSelection->get_selected(); + if (iter) { + + Gtk::TreeModel::Row row = *iter; + + if ( row[_mColumns._colIsSelector]) { + + // We only care about style sheet for selectors so erase row in CSS pane. + _cssPane->_store->erase(cssIter); + + // Update style sheet + Glib::ustring properties; + for (auto& crow: _cssPane->_store->children()) { + Glib::ustring sheetVal = crow[_cssPane->_cssColumns._styleSheetVal]; + if (!sheetVal.empty()) { + properties = properties + + crow[_cssPane->_cssColumns._propertyLabel] + ": " + + crow[_cssPane->_cssColumns._styleSheetVal] + "; "; + } + } + + row[_mColumns._colProperties] = properties; + _writeStyleElement(); + + } else { + + // We only erase row if style sheet does not contain deleted property. + // Otherwise we set style attr value to empty string. + Gtk::TreeModel::Row cssRow = *cssIter; + Glib::ustring val = cssRow[_cssPane->_cssColumns._styleSheetVal]; + if (val.empty()) { + _cssPane->_store->erase(cssIter); + } else { + cssRow[_cssPane->_cssColumns._styleAttrVal] = Glib::ustring(); + } + + // Update style attribute + std::vector<SPObject *> objects = row[_mColumns._colObj]; + Glib::ustring properties; + for (auto& crow: _cssPane->_store->children()) { + Glib::ustring attrVal = crow[_cssPane->_cssColumns._styleAttrVal]; + if (!attrVal.empty()) { + properties = properties + + crow[_cssPane->_cssColumns._propertyLabel] + ": " + + crow[_cssPane->_cssColumns._styleAttrVal] + "; "; + } + } + + if (objects[0]) { + if (properties.empty()) { + objects[0]->setAttribute("style", NULL); + } else { + objects[0]->setAttribute("style", properties); + } + DocumentUndo::done(SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_STYLE, + _("Deleted property from style attribute.")); + + } + } + } + } + } + } + } + return false; +} + + +/** + * @brief StyleDialog::_styleButton + * @param btn + * @param iconName + * @param tooltip + * Set the style of '+' and '-' buttons at the bottom of dialog. + */ +void StyleDialog::_styleButton(Gtk::Button& btn, char const* iconName, + char const* tooltip) +{ + GtkWidget *child = sp_icon_new(Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName); + gtk_widget_show(child); + btn.add(*manage(Glib::wrap(child))); + btn.set_relief(Gtk::RELIEF_NONE); + btn.set_tooltip_text (tooltip); +} + + +} // 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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/styledialog.h b/src/ui/dialog/styledialog.h new file mode 100644 index 000000000..e84489e66 --- /dev/null +++ b/src/ui/dialog/styledialog.h @@ -0,0 +1,175 @@ +/** @file + * @brief A dialog for CSS selectors + */ +/* Authors: + * Kamalpreet Kaur Grewal + * Tavmjong Bah + * + * Copyright (C) Kamalpreet Kaur Grewal 2016 <grewalkamal005@gmail.com> + * Copyright (C) Tavmjong Bah 2017 <tavmjong@free.fr> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef STYLEDIALOG_H +#define STYLEDIALOG_H + +#include <ui/widget/panel.h> +#include <gtkmm/treeview.h> +#include <gtkmm/treestore.h> +#include <gtkmm/scrolledwindow.h> +#include <gtkmm/dialog.h> +#include <gtkmm/treeselection.h> +#include <gtkmm/paned.h> + +#include "ui/dialog/desktop-tracker.h" +#include "ui/dialog/cssdialog.h" + +#include "xml/helper-observer.h" + +namespace Inkscape { +namespace UI { +namespace Dialog { + +/** + * @brief The StyleDialog class + * A list of CSS selectors will show up in this dialog. This dialog allows one to + * add and delete selectors. Elements can be added to and removed from the selectors + * in the dialog. Selection of any selector row selects the matching objects in + * the drawing and vice-versa. (Only simple selectors supported for now.) + * + * This class must keep two things in sync: + * 1. The text node of the style element. + * 2. The Gtk::TreeModel. + */ +class StyleDialog : public Widget::Panel { + +public: + ~StyleDialog(); + + static StyleDialog &getInstance() { return *new StyleDialog(); } + +private: + // No default constructor, noncopyable, nonassignable + StyleDialog(); + StyleDialog(StyleDialog const &d); + StyleDialog operator=(StyleDialog const &d); + + // Monitor <style> element for changes. + class NodeObserver; + + // Data structure + class ModelColumns : public Gtk::TreeModel::ColumnRecord { + public: + ModelColumns() { + add(_colSelector); + add(_colIsSelector); + add(_colObj); + add(_colProperties); + } + Gtk::TreeModelColumn<Glib::ustring> _colSelector; // Selector or matching object id. + Gtk::TreeModelColumn<bool> _colIsSelector; // Selector row or child object row. + Gtk::TreeModelColumn<std::vector<SPObject *> > _colObj; // List of matching objects. + Gtk::TreeModelColumn<Glib::ustring> _colProperties; // List of properties. + }; + ModelColumns _mColumns; + + // Override Gtk::TreeStore to control drag-n-drop (only allow dragging and dropping of selectors). + // See: https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en + // + // TreeStore implements simple drag and drop (DND) but there appears no way to know when a DND + // has been completed (other than doing the whole DND ourselves). As a hack, we use + // on_row_deleted to trigger write of style element. + class TreeStore : public Gtk::TreeStore { + protected: + TreeStore(); + bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const override; + bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& path, + const Gtk::SelectionData& selection_data) const override; + void on_row_deleted(const TreeModel::Path& path) override; + + public: + static Glib::RefPtr<StyleDialog::TreeStore> create(StyleDialog *styledialog); + + private: + StyleDialog *_styledialog; + }; + + // TreeView + Gtk::TreeView _treeView; + Glib::RefPtr<TreeStore> _store; + + // Widgets + Gtk::VPaned _paned; + Gtk::VBox _mainBox; + Gtk::HBox _buttonBox; + Gtk::ScrolledWindow _scrolledWindow; + Gtk::Button* del; + Gtk::Button* create; + CssDialog *_cssPane; + + // Reading and writing the style element. + Inkscape::XML::Node *_getStyleTextNode(); + void _readStyleElement(); + void _writeStyleElement(); + + // Manipulate Tree + void _addToSelector(Gtk::TreeModel::Row row); + void _removeFromSelector(Gtk::TreeModel::Row row); + Glib::ustring _getIdList(std::vector<SPObject *>); + std::vector<SPObject *> _getObjVec(Glib::ustring selector); + void _insertClass(const std::vector<SPObject *>& objVec, const Glib::ustring& className); + void _selectObjects(int, int); + void _updateCSSPanel(); + + // Variables + bool _updating; // Prevent cyclic actions: read <-> write, select via dialog <-> via desktop + Inkscape::XML::Node *_textNode; // Track so we know when to add a NodeObserver. + + // Signals and handlers - External + sigc::connection _document_replaced_connection; + sigc::connection _desktop_changed_connection; + sigc::connection _selection_changed_connection; + + void _handleDocumentReplaced(SPDesktop* desktop, SPDocument *document); + void _handleDesktopChanged(SPDesktop* desktop); + void _handleSelectionChanged(); + + DesktopTracker _desktopTracker; + + Inkscape::XML::SignalObserver _objObserver; // Track object in selected row (for style change). + + // Signal and handlers - Internal + void _addSelector(); + void _delSelector(); + bool _handleButtonEvent(GdkEventButton *event); + void _buttonEventsSelectObjs(GdkEventButton *event); + void _selectRow(); // Select row in tree when selection changed. + void _objChanged(); + + // Signal handlers for CssDialog + void _handleProp( const Glib::ustring& path, const Glib::ustring& new_text); + void _handleSheet(const Glib::ustring& path, const Glib::ustring& new_text); + void _handleAttr( const Glib::ustring& path, const Glib::ustring& new_text); + bool _delProperty(GdkEventButton *event); + + // GUI + void _styleButton(Gtk::Button& btn, char const* iconName, char const* tooltip); +}; + +} // namespace Dialogc +} // namespace UI +} // namespace Inkscape + +#endif // STYLEDIALOG_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:fileencoding=utf-8:textwidth=99 : diff --git a/src/ui/dialog/symbols.cpp b/src/ui/dialog/symbols.cpp index 4840b897b..740ff4f39 100644 --- a/src/ui/dialog/symbols.cpp +++ b/src/ui/dialog/symbols.cpp @@ -25,6 +25,7 @@ #include <gtkmm/comboboxtext.h> #include <gtkmm/iconview.h> #include <gtkmm/liststore.h> +#include <glibmm/regex.h> #include <glibmm/stringutils.h> #include <glibmm/markup.h> #include <glibmm/i18n.h> @@ -127,7 +128,7 @@ SymbolsDialog::SymbolsDialog( gchar const* prefsPath ) : sigc::connection connSet = symbolSet->signal_changed().connect( sigc::mem_fun(*this, &SymbolsDialog::rebuild)); instanceConns.push_back(connSet); - + ++row; /********************* Icon View **************************/ @@ -342,7 +343,7 @@ void SymbolsDialog::rebuild() { addSymbol->set_sensitive( true ); removeSymbol->set_sensitive( true ); } else { - addSymbol->set_sensitive( false ); + addSymbol->set_sensitive( false ); removeSymbol->set_sensitive( false ); } add_symbols( symbolDocument ); @@ -451,10 +452,21 @@ void SymbolsDialog::iconChanged() { #ifdef WITH_LIBVISIO // Read Visio stencil files -SPDocument* read_vss( gchar* fullname, gchar* filename ) { +SPDocument* read_vss( gchar* fullname, Glib::ustring name ) { + + #ifdef WIN32 + // RVNGFileStream uses fopen() internally which unfortunately only uses ANSI encoding on Windows + // therefore attempt to convert uri to the system codepage + // even if this is not possible the alternate short (8.3) file name will be used if available + fullname = g_win32_locale_filename_from_utf8(fullname); + #endif RVNGFileStream input(fullname); + #ifdef WIN32 + g_free(fullname); + #endif + if (!libvisio::VisioDocument::isSupported(&input)) { return NULL; } @@ -474,6 +486,12 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) { return NULL; } + // prepare a valid title for the symbol file + Glib::ustring title = Glib::Markup::escape_text(name); + // prepare a valid id prefix for the symbols (unfortunately libvisio doesn't give us a name) + Glib::RefPtr<Glib::Regex> regex1 = Glib::Regex::create("[^a-zA-Z0-9_-]"); + Glib::ustring id = regex1->replace(name, 0, "_", Glib::REGEX_MATCH_PARTIAL); + Glib::ustring tmpSVGOutput; tmpSVGOutput += "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"; tmpSVGOutput += "<svg\n"; @@ -483,17 +501,10 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) { tmpSVGOutput += " version=\"1.1\"\n"; tmpSVGOutput += " style=\"fill:none;stroke:#000000;stroke-width:2\">\n"; tmpSVGOutput += " <title>"; - tmpSVGOutput += filename; + tmpSVGOutput += title; tmpSVGOutput += "</title>\n"; tmpSVGOutput += " <defs>\n"; - // Create a string we can use for the symbol id (libvisio doesn't give us a name) - std::string sanitized( filename ); - sanitized.erase( sanitized.find_last_of(".vss")-3 ); - sanitized.erase( std::remove_if( sanitized.begin(), sanitized.end(), ispunct ), sanitized.end() ); - std::replace( sanitized.begin(), sanitized.end(), ' ', '_' ); - // std::cout << filename << " |" << sanitized << "|" << std::endl; - // Each "symbol" is in it's own SVG file, we wrap with <symbol> and merge into one file. for (unsigned i=0; i<output.size(); ++i) { @@ -501,7 +512,7 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) { ss << i; tmpSVGOutput += " <symbol id=\""; - tmpSVGOutput += sanitized; + tmpSVGOutput += id; tmpSVGOutput += "_"; tmpSVGOutput += ss.str(); tmpSVGOutput += "\">\n"; @@ -521,12 +532,12 @@ SPDocument* read_vss( gchar* fullname, gchar* filename ) { tmpSVGOutput += " </defs>\n"; tmpSVGOutput += "</svg>\n"; - + return SPDocument::createNewDocFromMem( tmpSVGOutput.c_str(), strlen( tmpSVGOutput.c_str()), 0 ); } #endif - + /* Hunts preference directories for symbol files */ void SymbolsDialog::get_symbols() { @@ -565,11 +576,14 @@ void SymbolsDialog::get_symbols() { #ifdef WITH_LIBVISIO if( tag.compare( "vss" ) == 0 ) { + // strip extension from filename and use it as name for the symbol set + Glib::ustring name = Glib::ustring(filename); + name = name.erase(name.rfind('.')); - symbol_doc = read_vss( fullname, filename ); + symbol_doc = read_vss( fullname, name ); if( symbol_doc ) { - symbolSets[Glib::ustring(filename)]= symbol_doc; - symbolSet->append(filename); + symbolSets[name]= symbol_doc; + symbolSet->append(name); } } #endif @@ -630,7 +644,7 @@ GSList* SymbolsDialog::symbols_in_doc( SPDocument* symbolDocument ) { } GSList* SymbolsDialog::use_in_doc_recursive (SPObject *r, GSList *l) -{ +{ if ( dynamic_cast<SPUse *>(r) ) { l = g_slist_prepend (l, r); @@ -816,7 +830,7 @@ SPDocument* SymbolsDialog::symbols_preview_doc() " xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"" " xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"" " xmlns:xlink=\"http://www.w3.org/1999/xlink\">" -" <defs id=\"defs\">" +" <defs id=\"defs\">" " <symbol id=\"the_symbol\"/>" " </defs>" " <use id=\"the_use\" xlink:href=\"#the_symbol\"/>" diff --git a/src/ui/interface.cpp b/src/ui/interface.cpp index d50c56b76..5e85065d1 100644 --- a/src/ui/interface.cpp +++ b/src/ui/interface.cpp @@ -72,6 +72,7 @@ #include "sp-mask.h" #include "message-stack.h" #include "ui/dialog/layer-properties.h" +#include "extension/find_extension_by_mime.h" using Inkscape::DocumentUndo; @@ -1234,15 +1235,7 @@ sp_ui_drag_data_received(GtkWidget *widget, case PNG_DATA: case JPEG_DATA: case IMAGE_DATA: { - const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png"); - - 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; + Inkscape::Extension::Extension *ext = Inkscape::Extension::find_by_mime((info == JPEG_DATA ? "image/jpeg" : "image/png")); bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0); ext->set_param_optiongroup("link", "embed"); ext->set_gui(false); diff --git a/src/ui/object-edit.cpp b/src/ui/object-edit.cpp index cf2c03396..b11a61710 100644 --- a/src/ui/object-edit.cpp +++ b/src/ui/object-edit.cpp @@ -812,7 +812,9 @@ ArcKnotHolderEntityStart::knot_set(Geom::Point const &p, Geom::Point const &/*or g_assert(arc != NULL); gint side = sp_genericellipse_side(arc, p); - if(side != 0) { arc->setClosed(side == -1); } + if(side != 0) { arc->setArcType( (side == -1) ? + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); Geom::Scale sc(arc->rx.computed, arc->ry.computed); @@ -861,7 +863,9 @@ ArcKnotHolderEntityEnd::knot_set(Geom::Point const &p, Geom::Point const &/*orig g_assert(arc != NULL); gint side = sp_genericellipse_side(arc, p); - if(side != 0) { arc->setClosed(side == -1); } + if(side != 0) { arc->setArcType( (side == -1) ? + SP_GENERIC_ELLIPSE_ARC_TYPE_SLICE : + SP_GENERIC_ELLIPSE_ARC_TYPE_ARC); } Geom::Point delta = p - Geom::Point(arc->cx.computed, arc->cy.computed); Geom::Scale sc(arc->rx.computed, arc->ry.computed); diff --git a/src/ui/tool/multi-path-manipulator.h b/src/ui/tool/multi-path-manipulator.h index c908cede2..4f152e0a2 100644 --- a/src/ui/tool/multi-path-manipulator.h +++ b/src/ui/tool/multi-path-manipulator.h @@ -82,8 +82,19 @@ private: template <typename R> void invokeForAll(R (PathManipulator::*method)()) { - for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ++i) { - ((i->second.get())->*method)(); + for (MapType::iterator i = _mmap.begin(); i != _mmap.end(); ) { + // Sometimes the PathManipulator got freed at loop end, thus + // invalidating the iterator so make sure that next_i will + // be a valid iterator and then assign i to it. + MapType::iterator next_i = i; + ++next_i; + // i->second is a boost::shared_ptr so try to hold on to it so + // it won't get freed prematurely by the WriteXML() method or + // whatever. See https://bugs.launchpad.net/inkscape/+bug/1617615 + // Applicable to empty paths. + boost::shared_ptr<PathManipulator> hold(i->second); + ((hold.get())->*method)(); + i = next_i; } } template <typename R, typename A> diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h index 025c460e2..a05f0e3b9 100644 --- a/src/ui/tool/node.h +++ b/src/ui/tool/node.h @@ -20,12 +20,7 @@ #include <iosfwd> #include <stdexcept> #include <cstddef> - -#if __cplusplus >= 201103L #include <functional> -#else -#include <tr1/functional> -#endif #include <boost/enable_shared_from_this.hpp> #include <boost/shared_ptr.hpp> diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp index 1801a0ea0..6e1d085aa 100644 --- a/src/ui/tools/flood-tool.cpp +++ b/src/ui/tools/flood-tool.cpp @@ -21,6 +21,7 @@ #include <config.h> #endif +#include <cmath> #include "trace/potrace/inkscape-potrace.h" #include <2geom/pathvector.h> #include <gdk/gdkkeysyms.h> @@ -186,6 +187,21 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width } /** + * \brief Check whether two unsigned integers are close to each other + * + * \param[in] a The 1st unsigned int + * \param[in] b The 2nd unsigned int + * \param[in] d The threshold for comparison + * + * \return true if |a-b| <= d; false otherwise + */ +static bool compare_guint32(guint32 const a, guint32 const b, guint32 const d) +{ + const int difference = std::abs(static_cast<int>(a) - static_cast<int>(b)); + return difference <= d; +} + +/** * Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation. * @param check The pixel in the pixel buffer to check. * @param orig The original selected pixel to use as the fill target color. @@ -196,7 +212,6 @@ inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width */ static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixel, guint32 dtc, int threshold, PaintBucketChannels method) { - int diff = 0; float hsl_check[3] = {0,0,0}, hsl_orig[3] = {0,0,0}; guint32 ac = 0, rc = 0, gc = 0, bc = 0; @@ -222,27 +237,35 @@ static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixe switch (method) { case FLOOD_CHANNELS_ALPHA: - return abs(static_cast<int>(ac) - ao) <= threshold; + return compare_guint32(ac, ao, threshold); case FLOOD_CHANNELS_R: - return abs(static_cast<int>(ac ? unpremul_alpha(rc, ac) : 0) - (ao ? unpremul_alpha(ro, ao) : 0)) <= threshold; + return compare_guint32(ac ? unpremul_alpha(rc, ac) : 0, + ao ? unpremul_alpha(ro, ao) : 0, + threshold); case FLOOD_CHANNELS_G: - return abs(static_cast<int>(ac ? unpremul_alpha(gc, ac) : 0) - (ao ? unpremul_alpha(go, ao) : 0)) <= threshold; + return compare_guint32(ac ? unpremul_alpha(gc, ac) : 0, + ao ? unpremul_alpha(go, ao) : 0, + threshold); case FLOOD_CHANNELS_B: - return abs(static_cast<int>(ac ? unpremul_alpha(bc, ac) : 0) - (ao ? unpremul_alpha(bo, ao) : 0)) <= threshold; + return compare_guint32(ac ? unpremul_alpha(bc, ac) : 0, + ao ? unpremul_alpha(bo, ao) : 0, + threshold); case FLOOD_CHANNELS_RGB: - guint32 amc, rmc, bmc, gmc; - //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255; - //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255; - amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha - rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255; - gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255; - bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255; - - diff += abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - (amop ? unpremul_alpha(rmop, amop) : 0)); - diff += abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - (amop ? unpremul_alpha(gmop, amop) : 0)); - diff += abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - (amop ? unpremul_alpha(bmop, amop) : 0)); - return ((diff / 3) <= ((threshold * 3) / 4)); - + { + guint32 amc, rmc, bmc, gmc; + //amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255; + //amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255; + amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha + rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255; + gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255; + bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255; + + int diff = 0; // The total difference between each of the 3 color components + diff += std::abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(rmop, amop) : 0)); + diff += std::abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(gmop, amop) : 0)); + diff += std::abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - static_cast<int>(amop ? unpremul_alpha(bmop, amop) : 0)); + return ((diff / 3) <= ((threshold * 3) / 4)); + } case FLOOD_CHANNELS_H: return ((int)(fabs(hsl_check[0] - hsl_orig[0]) * 100.0) <= threshold); case FLOOD_CHANNELS_S: diff --git a/src/ui/tools/freehand-base.h b/src/ui/tools/freehand-base.h index a3e7b42f9..3ee4cd7d0 100644 --- a/src/ui/tools/freehand-base.h +++ b/src/ui/tools/freehand-base.h @@ -76,8 +76,10 @@ public: GSList *white_curves; GSList *white_anchors; - //ALternative curve to use on continuing exisiting curve in case of bspline or spirolive - //because usigh anchor curves give memory and random bugs, - and obscure code- in some plataform reported by su_v in mac + // Alternative curve to use on continuing the exisiting curve in case of + // bspline or spirolive, because using anchor curves gives random memory + // bugs as reported by su_v when running this code on macOS (as well as + // making the code hard to understand). SPCurve *overwrite_curve; // Start anchor diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp index e628094d9..ac43b6c9d 100644 --- a/src/ui/tools/mesh-tool.cpp +++ b/src/ui/tools/mesh-tool.cpp @@ -1137,7 +1137,7 @@ static void sp_mesh_new_default(MeshTool &rc) { Inkscape::FOR_FILL : Inkscape::FOR_STROKE; // Ensure mesh is immediately editable. - // Editting both fill and stroke at same time doesn't work well so avoid. + // Editing both fill and stroke at same time doesn't work well so avoid. if (fill_or_stroke == Inkscape::FOR_FILL) { prefs->setBool("/tools/mesh/edit_fill", true ); prefs->setBool("/tools/mesh/edit_stroke", false); diff --git a/src/ui/tools/select-tool.cpp b/src/ui/tools/select-tool.cpp index 6c450b3dc..bae1793ed 100644 --- a/src/ui/tools/select-tool.cpp +++ b/src/ui/tools/select-tool.cpp @@ -142,7 +142,7 @@ void SelectTool::setup() { this->_describer = new Inkscape::SelectionDescriber( desktop->selection, desktop->messageStack(), - _("Click selection to toggle scale/rotation handles"), + _("Click selection to toggle scale/rotation handles (or Shift+s)"), _("No objects selected. Click, Shift+click, Alt+scroll mouse on top of objects, or drag around objects to select.") ); diff --git a/src/ui/tools/text-tool.cpp b/src/ui/tools/text-tool.cpp index 559187764..649bbb045 100644 --- a/src/ui/tools/text-tool.cpp +++ b/src/ui/tools/text-tool.cpp @@ -48,6 +48,8 @@ #include "ui/control-manager.h" #include "verbs.h" #include "xml/node-event-vector.h" +#include "xml/attribute-record.h" +#include "xml/sp-css-attr.h" using Inkscape::ControlManager; using Inkscape::DocumentUndo; @@ -1357,6 +1359,59 @@ SPCSSAttr *sp_text_get_style_at_cursor(ToolBase const *ec) return NULL; } +static bool css_attrs_are_equal(SPCSSAttr const *first, SPCSSAttr const *second) +{ + Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attrs = first->attributeList(); + for ( ; attrs ; attrs++) { + gchar const *other_attr = second->attribute(g_quark_to_string(attrs->key)); + if (other_attr == NULL || strcmp(attrs->value, other_attr)) + return false; + } + attrs = second->attributeList(); + for ( ; attrs ; attrs++) { + gchar const *other_attr = first->attribute(g_quark_to_string(attrs->key)); + if (other_attr == NULL || strcmp(attrs->value, other_attr)) + return false; + } + return true; +} + +std::vector<SPCSSAttr*> sp_text_get_selected_style(ToolBase const *ec, unsigned *k, int *b, std::vector<unsigned> *positions) +{ + std::vector<SPCSSAttr*> vec; + SPCSSAttr *css, *css_new; + TextTool *tc = SP_TEXT_CONTEXT(ec); + Inkscape::Text::Layout::iterator i = std::min(tc->text_sel_start, tc->text_sel_end); + SPObject const *obj = sp_te_object_at_position(tc->text, i); + if (obj) { + css = take_style_from_item(const_cast<SPObject*>(obj)); + } + vec.push_back(css); + positions->push_back(0); + i.nextCharacter(); + *k = 1; + *b = 1; + while (i != std::max(tc->text_sel_start, tc->text_sel_end)) + { + obj = sp_te_object_at_position(tc->text, i); + if (obj) { + css_new = take_style_from_item(const_cast<SPObject*>(obj)); + } + if(!css_attrs_are_equal(css, css_new)) + { + vec.push_back(css_new); + css = sp_repr_css_attr_new(); + sp_repr_css_merge(css, css_new); + positions->push_back(*k); + (*b)++; + } + i.nextCharacter(); + (*k)++; + } + positions->push_back(*k); + return vec; +} + /** Deletes the currently selected characters. Returns false if there is no text selection currently. @@ -1442,7 +1497,6 @@ bool TextTool::_styleSet(SPCSSAttr const *css) _("Set text style")); sp_text_context_update_cursor(this); sp_text_context_update_text_selection(this); - return true; } diff --git a/src/ui/tools/text-tool.h b/src/ui/tools/text-tool.h index 289ee180d..f2e6fea1a 100644 --- a/src/ui/tools/text-tool.h +++ b/src/ui/tools/text-tool.h @@ -94,6 +94,7 @@ private: bool sp_text_paste_inline(ToolBase *ec); Glib::ustring sp_text_get_selected_text(ToolBase const *ec); SPCSSAttr *sp_text_get_style_at_cursor(ToolBase const *ec); +std::vector<SPCSSAttr*> sp_text_get_selected_style(ToolBase const *ec, unsigned *k, int *b, std::vector<unsigned> *positions); bool sp_text_delete_selection(ToolBase *ec); void sp_text_context_place_cursor (TextTool *tc, SPObject *text, Inkscape::Text::Layout::iterator where); void sp_text_context_place_cursor_at (TextTool *tc, SPObject *text, Geom::Point const p); diff --git a/src/ui/tools/tool-base.cpp b/src/ui/tools/tool-base.cpp index aab256712..8c7f54d14 100644 --- a/src/ui/tools/tool-base.cpp +++ b/src/ui/tools/tool-base.cpp @@ -497,7 +497,7 @@ bool ToolBase::root_handler(GdkEvent* event) { Geom::Point const motion_w(event->motion.x, event->motion.y); Geom::Point const moved_w(motion_w - button_w); - this->desktop->scroll_world(moved_w, true); // we're still scrolling, do not redraw + this->desktop->scroll_relative(moved_w, true); // we're still scrolling, do not redraw ret = TRUE; } } else if (zoom_rb) { @@ -575,7 +575,7 @@ bool ToolBase::root_handler(GdkEvent* event) { Geom::Point const motion_w(event->button.x, event->button.y); Geom::Point const moved_w(motion_w - button_w); - this->desktop->scroll_world(moved_w); + this->desktop->scroll_relative(moved_w); desktop->updateNow(); ret = TRUE; } else if (zoom_rb == event->button.button) { @@ -675,7 +675,7 @@ bool ToolBase::root_handler(GdkEvent* event) { acceleration, desktop->getCanvas())); gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(i, 0); + this->desktop->scroll_relative(Geom::Point(i, 0)); ret = TRUE; } break; @@ -688,7 +688,7 @@ bool ToolBase::root_handler(GdkEvent* event) { acceleration, desktop->getCanvas())); gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(0, i); + this->desktop->scroll_relative(Geom::Point(0, i)); ret = TRUE; } break; @@ -701,7 +701,7 @@ bool ToolBase::root_handler(GdkEvent* event) { acceleration, desktop->getCanvas())); gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(-i, 0); + this->desktop->scroll_relative(Geom::Point(-i, 0)); ret = TRUE; } break; @@ -714,7 +714,7 @@ bool ToolBase::root_handler(GdkEvent* event) { acceleration, desktop->getCanvas())); gobble_key_events(get_group0_keyval(&event->key), GDK_CONTROL_MASK); - this->desktop->scroll_world(0, -i); + this->desktop->scroll_relative(Geom::Point(0, -i)); ret = TRUE; } break; @@ -856,11 +856,11 @@ bool ToolBase::root_handler(GdkEvent* event) { if (event->scroll.state & GDK_SHIFT_MASK) { switch (event->scroll.direction) { case GDK_SCROLL_UP: - desktop->scroll_world(wheel_scroll, 0); + desktop->scroll_relative(Geom::Point(wheel_scroll, 0)); break; case GDK_SCROLL_DOWN: - desktop->scroll_world(-wheel_scroll, 0); + desktop->scroll_relative(Geom::Point(-wheel_scroll, 0)); break; default: @@ -896,24 +896,24 @@ bool ToolBase::root_handler(GdkEvent* event) { } else { switch (event->scroll.direction) { case GDK_SCROLL_UP: - desktop->scroll_world(0, wheel_scroll); + desktop->scroll_relative(Geom::Point(0, wheel_scroll)); break; case GDK_SCROLL_DOWN: - desktop->scroll_world(0, -wheel_scroll); + desktop->scroll_relative(Geom::Point(0, -wheel_scroll)); break; case GDK_SCROLL_LEFT: - desktop->scroll_world(wheel_scroll, 0); + desktop->scroll_relative(Geom::Point(wheel_scroll, 0)); break; case GDK_SCROLL_RIGHT: - desktop->scroll_world(-wheel_scroll, 0); + desktop->scroll_relative(Geom::Point(-wheel_scroll, 0)); break; case GDK_SCROLL_SMOOTH: gdk_event_get_scroll_deltas(event, &delta_x, &delta_y); - desktop->scroll_world(delta_x, delta_y); + desktop->scroll_relative(Geom::Point(delta_x, delta_y)); break; } } diff --git a/src/ui/widget/panel.h b/src/ui/widget/panel.h index 9cbf39de9..b5498498d 100644 --- a/src/ui/widget/panel.h +++ b/src/ui/widget/panel.h @@ -104,6 +104,7 @@ public: void setDefaultResponse(int response_id); void setResponseSensitive(int response_id, bool setting); + /* Return signals. Signals emited by PanelDialog. */ virtual sigc::signal<void, SPDesktop *, SPDocument *> &signalDocumentReplaced(); virtual sigc::signal<void, SPDesktop *> &signalActivateDesktop(); virtual sigc::signal<void, SPDesktop *> &signalDeactiveDesktop(); diff --git a/src/uri-references.cpp b/src/uri-references.cpp index 170c98beb..7d6abd10a 100644 --- a/src/uri-references.cpp +++ b/src/uri-references.cpp @@ -11,16 +11,19 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "uri-references.h" + #include <iostream> #include <cstring> +#include <glibmm/miscutils.h> + +#include "bad-uri-exception.h" #include "document.h" #include "sp-object.h" #include "uri.h" -#include "uri-references.h" #include "extract-uri.h" #include "sp-tag-use.h" -#include <glibmm/miscutils.h> namespace Inkscape { @@ -103,7 +106,7 @@ bool URIReference::_acceptObject(SPObject *obj) const -void URIReference::attach(const URI &uri) throw(BadURIException) +void URIReference::attach(const URI &uri) { SPDocument *document = NULL; diff --git a/src/uri-references.h b/src/uri-references.h index e56ea0612..ce0f5f2ae 100644 --- a/src/uri-references.h +++ b/src/uri-references.h @@ -20,7 +20,6 @@ #include <sigc++/connection.h> #include <sigc++/trackable.h> -#include "bad-uri-exception.h" #include "sp-object.h" #include "sp-item.h" #include "sp-use.h" @@ -66,7 +65,7 @@ public: * @param rel_document document for relative URIs * @param uri the URI to watch */ - void attach(URI const& uri) throw(BadURIException); + void attach(URI const& uri); /** * Detaches from the currently attached URI target, if any; diff --git a/src/uri.cpp b/src/uri.cpp index f2578b989..9178c8ae8 100644 --- a/src/uri.cpp +++ b/src/uri.cpp @@ -8,11 +8,13 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -#include <glib.h> #include "uri.h" + #include <glibmm/ustring.h> #include <glibmm/miscutils.h> +#include "bad-uri-exception.h" + namespace Inkscape { URI::URI() { @@ -25,7 +27,7 @@ URI::URI(const URI &uri) { _impl = uri._impl; } -URI::URI(gchar const *preformed) throw(BadURIException) { +URI::URI(gchar const *preformed) { xmlURIPtr uri; if (!preformed) { throw MalformedURIException(); @@ -132,7 +134,7 @@ const gchar *URI::Impl::getOpaque() const { return (gchar *)_uri->opaque; } -gchar *URI::to_native_filename(gchar const* uri) throw(BadURIException) +gchar *URI::to_native_filename(gchar const* uri) { gchar *filename = NULL; URI tmp(uri); @@ -166,7 +168,7 @@ const std::string URI::getFullPath(std::string const &base) const { /* TODO !!! proper error handling */ -gchar *URI::toNativeFilename() const throw(BadURIException) { +gchar *URI::toNativeFilename() const { gchar *uriString = toString(); if (isRelativePath()) { return uriString; @@ -181,7 +183,7 @@ gchar *URI::toNativeFilename() const throw(BadURIException) { } } -URI URI::fromUtf8( gchar const* path ) throw (BadURIException) { +URI URI::fromUtf8( gchar const* path ) { if ( !path ) { throw MalformedURIException(); } @@ -215,7 +217,7 @@ URI URI::fromUtf8( gchar const* path ) throw (BadURIException) { } /* TODO !!! proper error handling */ -URI URI::from_native_filename(gchar const *path) throw(BadURIException) { +URI URI::from_native_filename(gchar const *path) { gchar *uri = g_filename_to_uri(path, NULL, NULL); URI result(uri); g_free( uri ); @@ -13,7 +13,6 @@ #include <exception> #include <libxml/uri.h> -#include "bad-uri-exception.h" #include <string> namespace Inkscape { @@ -37,7 +36,7 @@ public: * * @param preformed Properly quoted C-style string to be represented. */ - explicit URI(char const *preformed) throw(BadURIException); + explicit URI(char const *preformed); /** * Destructor. @@ -98,15 +97,15 @@ public: const char *getOpaque() const { return _impl->getOpaque(); } - static URI fromUtf8( char const* path ) throw (BadURIException); + static URI fromUtf8( char const* path ); - static URI from_native_filename(char const *path) throw(BadURIException); + static URI from_native_filename(char const *path); - static char *to_native_filename(char const* uri) throw(BadURIException); + static char *to_native_filename(char const* uri); const std::string getFullPath(std::string const &base) const; - char *toNativeFilename() const throw(BadURIException); + char *toNativeFilename() const; /** * Returns a glib string version of this URI. diff --git a/src/vanishing-point.cpp b/src/vanishing-point.cpp index d849b35d9..ed4a781cf 100644 --- a/src/vanishing-point.cpp +++ b/src/vanishing-point.cpp @@ -20,12 +20,12 @@ #include "desktop.h" #include "display/sp-canvas-item.h" #include "display/sp-ctrlline.h" -#include "ui/tools/tool-base.h" +#include "document-undo.h" #include "perspective-line.h" -#include "ui/shape-editor.h" #include "snap.h" #include "sp-namedview.h" -#include "document-undo.h" +#include "ui/shape-editor.h" +#include "ui/tools/tool-base.h" #include "verbs.h" using Inkscape::CTLINE_PRIMARY; @@ -48,45 +48,41 @@ namespace Box3D { #define MERGE_DIST 0.1 // knot shapes corresponding to GrPointType enum -SPKnotShapeType vp_knot_shapes [] = { - SP_KNOT_SHAPE_SQUARE, // VP_FINITE - SP_KNOT_SHAPE_CIRCLE //VP_INFINITE +SPKnotShapeType vp_knot_shapes[] = { + SP_KNOT_SHAPE_SQUARE, // VP_FINITE + SP_KNOT_SHAPE_CIRCLE // VP_INFINITE }; -static void -vp_drag_sel_changed(Inkscape::Selection */*selection*/, gpointer data) +static void vp_drag_sel_changed(Inkscape::Selection * /*selection*/, gpointer data) { - VPDrag *drag = (VPDrag *) data; + VPDrag *drag = (VPDrag *)data; drag->updateDraggers(); drag->updateLines(); drag->updateBoxReprs(); } -static void -vp_drag_sel_modified (Inkscape::Selection */*selection*/, guint /*flags*/, gpointer data) +static void vp_drag_sel_modified(Inkscape::Selection * /*selection*/, guint /*flags*/, gpointer data) { - VPDrag *drag = (VPDrag *) data; - drag->updateLines (); - //drag->updateBoxReprs(); - drag->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all) - drag->updateDraggers (); + VPDrag *drag = (VPDrag *)data; + drag->updateLines(); + // drag->updateBoxReprs(); + drag->updateBoxHandles(); // FIXME: Only update the handles of boxes on this dragger (not on all) + drag->updateDraggers(); } -static bool -have_VPs_of_same_perspective (VPDragger *dr1, VPDragger *dr2) +static bool have_VPs_of_same_perspective(VPDragger *dr1, VPDragger *dr2) { for (std::list<VanishingPoint>::iterator i = dr1->vps.begin(); i != dr1->vps.end(); ++i) { - if (dr2->hasPerspective ((*i).get_perspective())) { + if (dr2->hasPerspective((*i).get_perspective())) { return true; } } return false; } -static void -vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data) +static void vp_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data) { - VPDragger *dragger = (VPDragger *) data; + VPDragger *dragger = (VPDragger *)data; VPDrag *drag = dragger->parent; Geom::Point p = ppointer; @@ -102,16 +98,16 @@ vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, g /* with Shift; if there is more than one box linked to this VP we need to split it and create a new perspective */ if (dragger->numberOfBoxes() > 1) { // FIXME: Don't do anything if *all* boxes of a VP are selected - std::set<VanishingPoint*, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes(); + std::set<VanishingPoint *, less_ptr> sel_vps = dragger->VPsOfSelectedBoxes(); std::list<SPBox3D *> sel_boxes; - for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) { + for (std::set<VanishingPoint *, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) { // for each VP that has selected boxes: Persp3D *old_persp = (*vp)->get_perspective(); sel_boxes = (*vp)->selectedBoxes(SP_ACTIVE_DESKTOP->getSelection()); // we create a new perspective ... - Persp3D *new_persp = persp3d_create_xml_element (dragger->parent->document, old_persp->perspective_impl); + Persp3D *new_persp = persp3d_create_xml_element(dragger->parent->document, old_persp->perspective_impl); /* ... unlink the boxes from the old one and FIXME: We need to unlink the _un_selected boxes of each VP so that @@ -126,19 +122,19 @@ vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, g } } // FIXME: Do we need to create a new dragger as well? - dragger->updateZOrders (); - DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_3DBOX, - _("Split vanishing points")); + dragger->updateZOrders(); + DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_3DBOX, _("Split vanishing points")); return; } } if (!(state & GDK_SHIFT_MASK)) { // without Shift; see if we need to snap to another dragger - for (std::vector<VPDragger *>::const_iterator di = dragger->parent->draggers.begin(); di != dragger->parent->draggers.end(); ++di) { - VPDragger *d_new = *di; - if ((d_new != dragger) && (Geom::L2 (d_new->point - p) < snap_dist)) { - if (have_VPs_of_same_perspective (dragger, d_new)) { + for (std::vector<VPDragger *>::const_iterator di = dragger->parent->draggers.begin(); + di != dragger->parent->draggers.end(); ++di) { + VPDragger *d_new = *di; + if ((d_new != dragger) && (Geom::L2(d_new->point - p) < snap_dist)) { + if (have_VPs_of_same_perspective(dragger, d_new)) { // this would result in degenerate boxes, which we disallow for the time being continue; } @@ -153,7 +149,8 @@ vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, g d_new->vps.merge(dragger->vps); // ... delete old dragger ... - drag->draggers.erase(std::remove(drag->draggers.begin(), drag->draggers.end(), dragger),drag->draggers.end()); + drag->draggers.erase(std::remove(drag->draggers.begin(), drag->draggers.end(), dragger), + drag->draggers.end()); delete dragger; dragger = NULL; @@ -163,25 +160,27 @@ vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, g // TODO: Update the new merged dragger d_new->updateTip(); - d_new->parent->updateBoxDisplays (); // FIXME: Only update boxes in current dragger! - d_new->updateZOrders (); + d_new->parent->updateBoxDisplays(); // FIXME: Only update boxes in current dragger! + d_new->updateZOrders(); - drag->updateLines (); + drag->updateLines(); // TODO: Undo machinery; this doesn't work yet because perspectives must be created and // deleted according to changes in the svg representation, not based on any user input // as is currently the case. DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_3DBOX, - _("Merge vanishing points")); + _("Merge vanishing points")); return; } } } - // We didn't hit the return statement above, so we didn't snap to another dragger. Therefore we'll now try a regular snap - // Regardless of the status of the SHIFT key, we will try to snap; Here SHIFT does not disable snapping, as the shift key + // We didn't hit the return statement above, so we didn't snap to another dragger. Therefore we'll now try a regular + // snap + // Regardless of the status of the SHIFT key, we will try to snap; Here SHIFT does not disable snapping, as the + // shift key // has a different purpose in this context (see above) SPDesktop *desktop = SP_ACTIVE_DESKTOP; SnapManager &m = desktop->namedview->snap_manager; @@ -197,7 +196,7 @@ vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, g dragger->updateVPs(p); dragger->updateBoxDisplays(); - dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all) + dragger->parent->updateBoxHandles(); // FIXME: Only update the handles of boxes on this dragger (not on all) dragger->updateZOrders(); drag->updateLines(); @@ -205,59 +204,56 @@ vp_knot_moved_handler (SPKnot *knot, Geom::Point const &ppointer, guint state, g dragger->dragging_started = true; } -static void -vp_knot_grabbed_handler (SPKnot */*knot*/, unsigned int /*state*/, gpointer data) +static void vp_knot_grabbed_handler(SPKnot * /*knot*/, unsigned int /*state*/, gpointer data) { - VPDragger *dragger = (VPDragger *) data; + VPDragger *dragger = (VPDragger *)data; VPDrag *drag = dragger->parent; drag->dragging = true; } -static void -vp_knot_ungrabbed_handler (SPKnot *knot, guint /*state*/, gpointer data) +static void vp_knot_ungrabbed_handler(SPKnot *knot, guint /*state*/, gpointer data) { - VPDragger *dragger = (VPDragger *) data; + VPDragger *dragger = (VPDragger *)data; dragger->point_original = dragger->point = knot->pos; dragger->dragging_started = false; for (std::list<VanishingPoint>::iterator i = dragger->vps.begin(); i != dragger->vps.end(); ++i) { - (*i).set_pos (knot->pos); + (*i).set_pos(knot->pos); (*i).updateBoxReprs(); (*i).updatePerspRepr(); } - dragger->parent->updateDraggers (); - dragger->parent->updateLines (); - dragger->parent->updateBoxHandles (); + dragger->parent->updateDraggers(); + dragger->parent->updateLines(); + dragger->parent->updateBoxHandles(); // TODO: Update box's paths and svg representation dragger->parent->dragging = false; // TODO: Undo machinery!! - g_return_if_fail (dragger->parent); - g_return_if_fail (dragger->parent->document); - DocumentUndo::done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX, - _("3D box: Move vanishing point")); + g_return_if_fail(dragger->parent); + g_return_if_fail(dragger->parent->document); + DocumentUndo::done(dragger->parent->document, SP_VERB_CONTEXT_3DBOX, _("3D box: Move vanishing point")); } unsigned int VanishingPoint::global_counter = 0; // FIXME: Rename to something more meaningful! -void -VanishingPoint::set_pos(Proj::Pt2 const &pt) { - g_return_if_fail (_persp); - _persp->perspective_impl->tmat.set_image_pt (_axis, pt); +void VanishingPoint::set_pos(Proj::Pt2 const &pt) +{ + g_return_if_fail(_persp); + _persp->perspective_impl->tmat.set_image_pt(_axis, pt); } -std::list<SPBox3D *> -VanishingPoint::selectedBoxes(Inkscape::Selection *sel) { +std::list<SPBox3D *> VanishingPoint::selectedBoxes(Inkscape::Selection *sel) +{ std::list<SPBox3D *> sel_boxes; - auto itemlist= sel->items(); - for (auto i=itemlist.begin();i!=itemlist.end();++i) { + auto itemlist = sel->items(); + for (auto i = itemlist.begin(); i != itemlist.end(); ++i) { SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box && this->hasBox(box)) { @@ -267,13 +263,13 @@ VanishingPoint::selectedBoxes(Inkscape::Selection *sel) { return sel_boxes; } -VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp) : - parent(parent), - knot(NULL), - point(p), - point_original(p), - dragging_started(false), - vps() +VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp) + : parent(parent) + , knot(NULL) + , point(p) + , point_original(p) + , dragging_started(false) + , vps() { if (vp.is_finite()) { // create the knot @@ -290,12 +286,15 @@ VPDragger::VPDragger(VPDrag *parent, Geom::Point p, VanishingPoint &vp) : this->knot->show(); // connect knot's signals - this->_moved_connection = this->knot->moved_signal.connect(sigc::bind(sigc::ptr_fun(vp_knot_moved_handler), this)); - this->_grabbed_connection = this->knot->grabbed_signal.connect(sigc::bind(sigc::ptr_fun(vp_knot_grabbed_handler), this)); - this->_ungrabbed_connection = this->knot->ungrabbed_signal.connect(sigc::bind(sigc::ptr_fun(vp_knot_ungrabbed_handler), this)); + this->_moved_connection = + this->knot->moved_signal.connect(sigc::bind(sigc::ptr_fun(vp_knot_moved_handler), this)); + this->_grabbed_connection = + this->knot->grabbed_signal.connect(sigc::bind(sigc::ptr_fun(vp_knot_grabbed_handler), this)); + this->_ungrabbed_connection = + this->knot->ungrabbed_signal.connect(sigc::bind(sigc::ptr_fun(vp_knot_ungrabbed_handler), this)); // add the initial VP (which may be NULL!) - this->addVP (vp); + this->addVP(vp); } } @@ -313,39 +312,42 @@ VPDragger::~VPDragger() /** Updates the statusbar tip of the dragger knot, based on its draggables */ -void -VPDragger::updateTip () +void VPDragger::updateTip() { if (this->knot && this->knot->tip) { - g_free (this->knot->tip); + g_free(this->knot->tip); this->knot->tip = NULL; } guint num = this->numberOfBoxes(); if (this->vps.size() == 1) { if (this->vps.front().is_finite()) { - this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box", - "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)", - num), - num); - } else { + this->knot->tip = g_strdup_printf(ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box", + "<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag " + "with <b>Shift</b> to separate selected box(es)", + num), + num); + } + else { // This won't make sense any more when infinite VPs are not shown on the canvas, // but currently we update the status message anyway - this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box", - "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)", - num), - num); + this->knot->tip = g_strdup_printf(ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box", + "<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; " + "drag with <b>Shift</b> to separate selected box(es)", + num), + num); } - } else { + } + else { int length = this->vps.size(); - char *desc1 = g_strdup_printf ("Collection of <b>%d</b> vanishing points ", length); - char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)", - "shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)", - num), - num); + char *desc1 = g_strdup_printf("Collection of <b>%d</b> vanishing points ", length); + char *desc2 = g_strdup_printf( + ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)", + "shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)", num), + num); this->knot->tip = g_strconcat(desc1, desc2, NULL); - g_free (desc1); - g_free (desc2); + g_free(desc1); + g_free(desc2); } } @@ -353,34 +355,32 @@ VPDragger::updateTip () * Adds a vanishing point to the dragger (also updates the position if necessary); * the perspective is stored separately, too, for efficiency in updating boxes. */ -void -VPDragger::addVP (VanishingPoint &vp, bool update_pos) +void VPDragger::addVP(VanishingPoint &vp, bool update_pos) { - if (!vp.is_finite() || std::find (vps.begin(), vps.end(), vp) != vps.end()) { + if (!vp.is_finite() || std::find(vps.begin(), vps.end(), vp) != vps.end()) { // don't add infinite VPs; don't add the same VP twice return; } if (update_pos) { - vp.set_pos (this->point); + vp.set_pos(this->point); } - this->vps.push_front (vp); + this->vps.push_front(vp); this->updateTip(); } -void -VPDragger::removeVP (VanishingPoint const &vp) +void VPDragger::removeVP(VanishingPoint const &vp) { - std::list<VanishingPoint>::iterator i = std::find (this->vps.begin(), this->vps.end(), vp); + std::list<VanishingPoint>::iterator i = std::find(this->vps.begin(), this->vps.end(), vp); if (i != this->vps.end()) { - this->vps.erase (i); + this->vps.erase(i); } this->updateTip(); } -VanishingPoint * -VPDragger::findVPWithBox (SPBox3D *box) { +VanishingPoint *VPDragger::findVPWithBox(SPBox3D *box) +{ for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) { if ((*vp).hasBox(box)) { return &(*vp); @@ -389,28 +389,27 @@ VPDragger::findVPWithBox (SPBox3D *box) { return NULL; } -std::set<VanishingPoint*, less_ptr> -VPDragger::VPsOfSelectedBoxes() { - std::set<VanishingPoint*, less_ptr> sel_vps; +std::set<VanishingPoint *, less_ptr> VPDragger::VPsOfSelectedBoxes() +{ + std::set<VanishingPoint *, less_ptr> sel_vps; VanishingPoint *vp; // FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference. Inkscape::Selection *sel = SP_ACTIVE_DESKTOP->getSelection(); - auto itemlist= sel->items(); - for (auto i=itemlist.begin();i!=itemlist.end();++i) { + auto itemlist = sel->items(); + for (auto i = itemlist.begin(); i != itemlist.end(); ++i) { SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box) { vp = this->findVPWithBox(box); if (vp) { - sel_vps.insert (vp); + sel_vps.insert(vp); } } } return sel_vps; } -guint -VPDragger::numberOfBoxes () +guint VPDragger::numberOfBoxes() { guint num = 0; for (std::list<VanishingPoint>::iterator vp = vps.begin(); vp != vps.end(); ++vp) { @@ -419,8 +418,7 @@ VPDragger::numberOfBoxes () return num; } -bool -VPDragger::hasPerspective (const Persp3D *persp) +bool VPDragger::hasPerspective(const Persp3D *persp) { for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) { if (persp3d_perspectives_coincide(persp, (*i).get_perspective())) { @@ -430,8 +428,7 @@ VPDragger::hasPerspective (const Persp3D *persp) return false; } -void -VPDragger::mergePerspectives () +void VPDragger::mergePerspectives() { Persp3D *persp1, *persp2; for (std::list<VanishingPoint>::iterator i = vps.begin(); i != vps.end(); ++i) { @@ -442,7 +439,7 @@ VPDragger::mergePerspectives () /* don't merge a perspective with itself */ continue; } - if (persp3d_perspectives_coincide(persp1,persp2)) { + if (persp3d_perspectives_coincide(persp1, persp2)) { /* if perspectives coincide but are not the same, merge them */ persp3d_absorb(persp1, persp2); @@ -454,39 +451,36 @@ VPDragger::mergePerspectives () } } -void -VPDragger::updateBoxDisplays () +void VPDragger::updateBoxDisplays() { for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) { (*i).updateBoxDisplays(); } } -void -VPDragger::updateVPs (Geom::Point const &pt) +void VPDragger::updateVPs(Geom::Point const &pt) { for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) { - (*i).set_pos (pt); + (*i).set_pos(pt); } } -void -VPDragger::updateZOrders () +void VPDragger::updateZOrders() { for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) { persp3d_update_z_orders((*i).get_perspective()); } } -void -VPDragger::printVPs() { - g_print ("VPDragger at position (%f, %f):\n", point[Geom::X], point[Geom::Y]); +void VPDragger::printVPs() +{ + g_print("VPDragger at position (%f, %f):\n", point[Geom::X], point[Geom::Y]); for (std::list<VanishingPoint>::iterator i = this->vps.begin(); i != this->vps.end(); ++i) { - g_print (" VP %s\n", (*i).axisString()); + g_print(" VP %s\n", (*i).axisString()); } } -VPDrag::VPDrag (SPDocument *document) +VPDrag::VPDrag(SPDocument *document) { this->document = document; this->selection = SP_ACTIVE_DESKTOP->getSelection(); @@ -496,20 +490,15 @@ VPDrag::VPDrag (SPDocument *document) this->dragging = false; - this->sel_changed_connection = this->selection->connectChanged( - sigc::bind ( - sigc::ptr_fun(&vp_drag_sel_changed), - (gpointer)this ) + this->sel_changed_connection = + this->selection->connectChanged(sigc::bind(sigc::ptr_fun(&vp_drag_sel_changed), (gpointer) this) - ); - this->sel_modified_connection = this->selection->connectModified( - sigc::bind( - sigc::ptr_fun(&vp_drag_sel_modified), - (gpointer)this ) - ); + ); + this->sel_modified_connection = + this->selection->connectModified(sigc::bind(sigc::ptr_fun(&vp_drag_sel_modified), (gpointer) this)); - this->updateDraggers (); - this->updateLines (); + this->updateDraggers(); + this->updateLines(); } VPDrag::~VPDrag() @@ -525,14 +514,13 @@ VPDrag::~VPDrag() for (std::vector<SPCtrlLine *>::const_iterator i = this->lines.begin(); i != this->lines.end(); ++i) { sp_canvas_item_destroy(SP_CANVAS_ITEM(*i)); } - this->lines.clear(); + this->lines.clear(); } /** * Select the dragger that has the given VP. */ -VPDragger * -VPDrag::getDraggerFor (VanishingPoint const &vp) +VPDragger *VPDrag::getDraggerFor(VanishingPoint const &vp) { for (std::vector<VPDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i) { VPDragger *dragger = *i; @@ -546,22 +534,20 @@ VPDrag::getDraggerFor (VanishingPoint const &vp) return NULL; } -void -VPDrag::printDraggers () +void VPDrag::printDraggers() { - g_print ("=== VPDrag info: =================================\n"); + g_print("=== VPDrag info: =================================\n"); for (std::vector<VPDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i) { (*i)->printVPs(); - g_print ("========\n"); + g_print("========\n"); } - g_print ("=================================================\n"); + g_print("=================================================\n"); } /** * Regenerates the draggers list from the current selection; is called when selection is changed or modified */ -void -VPDrag::updateDraggers () +void VPDrag::updateDraggers() { if (this->dragging) return; @@ -571,17 +557,17 @@ VPDrag::updateDraggers () } this->draggers.clear(); - g_return_if_fail (this->selection != NULL); + g_return_if_fail(this->selection != NULL); - auto itemlist= this->selection->items(); - for (auto i=itemlist.begin();i!=itemlist.end();++i) { + auto itemlist = this->selection->items(); + for (auto i = itemlist.begin(); i != itemlist.end(); ++i) { SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box) { VanishingPoint vp; for (int i = 0; i < 3; ++i) { vp.set(box3d_get_perspective(box), Proj::axes[i]); - addDragger (vp); + addDragger(vp); } } } @@ -591,8 +577,7 @@ VPDrag::updateDraggers () 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 perspective */ -void -VPDrag::updateLines () +void VPDrag::updateLines() { // delete old lines for (std::vector<SPCtrlLine *>::const_iterator i = this->lines.begin(); i != this->lines.end(); ++i) { @@ -601,24 +586,24 @@ VPDrag::updateLines () this->lines.clear(); // do nothing if perspective lines are currently disabled - if (this->show_lines == 0) return; + if (this->show_lines == 0) + return; - g_return_if_fail (this->selection != NULL); + g_return_if_fail(this->selection != NULL); - auto itemlist= this->selection->items(); - for (auto i=itemlist.begin();i!=itemlist.end();++i) { + auto itemlist = this->selection->items(); + for (auto i = itemlist.begin(); i != itemlist.end(); ++i) { SPItem *item = *i; SPBox3D *box = dynamic_cast<SPBox3D *>(item); if (box) { - this->drawLinesForFace (box, Proj::X); - this->drawLinesForFace (box, Proj::Y); - this->drawLinesForFace (box, Proj::Z); + this->drawLinesForFace(box, Proj::X); + this->drawLinesForFace(box, Proj::Y); + this->drawLinesForFace(box, Proj::Z); } } } -void -VPDrag::updateBoxHandles () +void VPDrag::updateBoxHandles() { // FIXME: Is there a way to update the knots without accessing the // (previously) statically linked function KnotHolder::update_knots? @@ -633,14 +618,13 @@ VPDrag::updateBoxHandles () } Inkscape::UI::Tools::ToolBase *ec = INKSCAPE.active_event_context(); - g_assert (ec != NULL); + g_assert(ec != NULL); if (ec->shape_editor != NULL) { ec->shape_editor->update_knotholder(); } } -void -VPDrag::updateBoxReprs () +void VPDrag::updateBoxReprs() { for (std::vector<VPDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i) { VPDragger *dragger = *i; @@ -650,8 +634,7 @@ VPDrag::updateBoxReprs () } } -void -VPDrag::updateBoxDisplays () +void VPDrag::updateBoxDisplays() { for (std::vector<VPDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i) { VPDragger *dragger = *i; @@ -665,7 +648,8 @@ VPDrag::updateBoxDisplays () /** * Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners. */ -void VPDrag::drawLinesForFace(const SPBox3D *box, Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4) +void VPDrag::drawLinesForFace(const SPBox3D *box, + Proj::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4) { CtrlLineType type = CTLINE_PRIMARY; switch (axis) { @@ -683,54 +667,48 @@ void VPDrag::drawLinesForFace(const SPBox3D *box, Proj::Axis axis) //, guint cor g_assert_not_reached(); } - Geom::Point corner1, corner2, corner3, corner4; - box3d_corners_for_PLs (box, axis, corner1, corner2, corner3, corner4); + const size_t NUM_CORNERS = 4; + Geom::Point corners[NUM_CORNERS]; + box3d_corners_for_PLs(box, axis, corners[0], corners[1], corners[2], corners[3]); - g_return_if_fail (box3d_get_perspective(box)); - Proj::Pt2 vp = persp3d_get_VP (box3d_get_perspective(box), axis); + g_return_if_fail(box3d_get_perspective(box)); + Proj::Pt2 vp = persp3d_get_VP(box3d_get_perspective(box), axis); if (vp.is_finite()) { // draw perspective lines for finite VPs Geom::Point pt = vp.affine(); if (this->front_or_rear_lines & 0x1) { // draw 'front' perspective lines - this->addLine(corner1, pt, type); - this->addLine(corner2, pt, type); + this->addLine(corners[0], pt, type); + this->addLine(corners[1], pt, type); } if (this->front_or_rear_lines & 0x2) { // draw 'rear' perspective lines - this->addLine(corner3, pt, type); - this->addLine(corner4, pt, type); + this->addLine(corners[2], pt, type); + this->addLine(corners[3], pt, type); } - } else { + } + else { // draw perspective lines for infinite VPs - boost::optional<Geom::Point> pt1, pt2, pt3, pt4; + boost::optional<Geom::Point> pts[NUM_CORNERS]; Persp3D *persp = box3d_get_perspective(box); SPDesktop *desktop = SP_ACTIVE_DESKTOP; // FIXME: Store the desktop in VPDrag - Box3D::PerspectiveLine pl (corner1, axis, persp); - pt1 = pl.intersection_with_viewbox(desktop); - pl = Box3D::PerspectiveLine (corner2, axis, persp); - pt2 = pl.intersection_with_viewbox(desktop); - - pl = Box3D::PerspectiveLine (corner3, axis, persp); - pt3 = pl.intersection_with_viewbox(desktop); - - pl = Box3D::PerspectiveLine (corner4, axis, persp); - pt4 = pl.intersection_with_viewbox(desktop); - - if (!pt1 || !pt2 || !pt3 || !pt4) { - // some perspective lines s are outside the canvas; currently we don't draw any of them - return; + for (size_t i = 0; i < NUM_CORNERS; i++) { + Box3D::PerspectiveLine pl(corners[i], axis, persp); + if (!(pts[i] = pl.intersection_with_viewbox(desktop))) { + // some perspective lines are outside the canvas; currently we don't draw any of them + return; + } } if (this->front_or_rear_lines & 0x1) { // draw 'front' perspective lines - this->addLine(corner1, *pt1, type); - this->addLine(corner2, *pt2, type); + this->addLine(corners[0], *pts[0], type); + this->addLine(corners[1], *pts[1], type); } if (this->front_or_rear_lines & 0x2) { // draw 'rear' perspective lines - this->addLine(corner3, *pt3, type); - this->addLine(corner4, *pt4, type); + this->addLine(corners[2], *pts[2], type); + this->addLine(corners[3], *pts[3], type); } } } @@ -740,8 +718,7 @@ void VPDrag::drawLinesForFace(const SPBox3D *box, Proj::Axis axis) //, guint cor * otherwise create new dragger and add it to draggers list * We also store the corresponding perspective in case it is not already present. */ -void -VPDrag::addDragger (VanishingPoint &vp) +void VPDrag::addDragger(VanishingPoint &vp) { if (!vp.is_finite()) { // don't create draggers for infinite vanishing points @@ -751,9 +728,9 @@ VPDrag::addDragger (VanishingPoint &vp) for (std::vector<VPDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i) { VPDragger *dragger = *i; - if (Geom::L2 (dragger->point - p) < MERGE_DIST) { + if (Geom::L2(dragger->point - p) < MERGE_DIST) { // distance is small, merge this draggable into dragger, no need to create new dragger - dragger->addVP (vp); + dragger->addVP(vp); return; } } @@ -763,13 +740,11 @@ VPDrag::addDragger (VanishingPoint &vp) this->draggers.push_back(new_dragger); } -void -VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1) +void VPDrag::swap_perspectives_of_VPs(Persp3D *persp2, Persp3D *persp1) { // iterate over all VP in all draggers and replace persp2 with persp1 for (std::vector<VPDragger *>::const_iterator i = this->draggers.begin(); i != this->draggers.end(); ++i) { - for (std::list<VanishingPoint>::iterator j = (*i)->vps.begin(); - j != (*i)->vps.end(); ++j) { + for (std::list<VanishingPoint>::iterator j = (*i)->vps.begin(); j != (*i)->vps.end(); ++j) { if ((*j).get_perspective() == persp2) { (*j).set_perspective(persp1); } diff --git a/src/verbs.cpp b/src/verbs.cpp index aeb742105..8b3d7a5a2 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -1890,7 +1890,10 @@ void ZoomVerb::perform(SPAction *action, void *data) zcorr = prefs->getDouble("/options/zoomcorrection/value", 1.0); Geom::Rect const d = dt->get_display_area(); - + + Geom::Rect const d_canvas = dt->getCanvas()->getViewbox(); // Not SVG 'viewBox' + Geom::Point midpoint = dt->w2d(d_canvas.midpoint()); // Midpoint of drawing on canvas. + switch (reinterpret_cast<std::size_t>(data)) { case SP_VERB_ZOOM_IN: { @@ -1907,7 +1910,7 @@ void ZoomVerb::perform(SPAction *action, void *data) } } - dt->zoom_relative( d.midpoint()[Geom::X], d.midpoint()[Geom::Y], mul*zoom_inc); + dt->zoom_relative_center_point( midpoint, mul*zoom_inc); break; } case SP_VERB_ZOOM_OUT: @@ -1924,17 +1927,17 @@ void ZoomVerb::perform(SPAction *action, void *data) } } - dt->zoom_relative( d.midpoint()[Geom::X], d.midpoint()[Geom::Y], 1 / (mul*zoom_inc) ); + dt->zoom_relative_center_point( midpoint, 1 / (mul*zoom_inc) ); break; } case SP_VERB_ZOOM_1_1: - dt->zoom_absolute( d.midpoint()[Geom::X], d.midpoint()[Geom::Y], 1.0 * zcorr ); + dt->zoom_absolute_center_point( midpoint, 1.0 * zcorr ); break; case SP_VERB_ZOOM_1_2: - dt->zoom_absolute( d.midpoint()[Geom::X], d.midpoint()[Geom::Y], 0.5 * zcorr ); + dt->zoom_absolute_center_point( midpoint, 0.5 * zcorr ); break; case SP_VERB_ZOOM_2_1: - dt->zoom_absolute( d.midpoint()[Geom::X], d.midpoint()[Geom::Y], 2.0 * zcorr ); + dt->zoom_absolute_center_point( midpoint, 2.0 * zcorr ); break; case SP_VERB_ZOOM_PAGE: dt->zoom_page(); @@ -1949,10 +1952,10 @@ void ZoomVerb::perform(SPAction *action, void *data) dt->zoom_selection(); break; case SP_VERB_ZOOM_NEXT: - dt->next_zoom(); + dt->next_transform(); break; case SP_VERB_ZOOM_PREV: - dt->prev_zoom(); + dt->prev_transform(); break; case SP_VERB_TOGGLE_RULERS: dt->toggleRulers(); @@ -2058,6 +2061,9 @@ void DialogVerb::perform(SPAction *action, void *data) g_assert(dt->_dlg_mgr != NULL); switch (reinterpret_cast<std::size_t>(data)) { + case SP_VERB_DIALOG_PROTOTYPE: + dt->_dlg_mgr->showDialog("Prototype"); + break; case SP_VERB_DIALOG_DISPLAY: //sp_display_dialog(); dt->_dlg_mgr->showDialog("InkscapePreferences"); @@ -2157,6 +2163,12 @@ void DialogVerb::perform(SPAction *action, void *data) case SP_VERB_DIALOG_PRINT_COLORS_PREVIEW: dt->_dlg_mgr->showDialog("PrintColorsPreviewDialog"); break; + case SP_VERB_DIALOG_STYLE: + dt->_dlg_mgr->showDialog("StyleDialog"); + break; + case SP_VERB_DIALOG_CSS: + dt->_dlg_mgr->showDialog("CssDialog"); + break; default: break; @@ -2886,6 +2898,7 @@ Verb *Verb::_base_verbs[] = { // Zoom/View new ZoomVerb(SP_VERB_ZOOM_IN, "ZoomIn", N_("Zoom In"), N_("Zoom in"), INKSCAPE_ICON("zoom-in")), new ZoomVerb(SP_VERB_ZOOM_OUT, "ZoomOut", N_("Zoom Out"), N_("Zoom out"), INKSCAPE_ICON("zoom-out")), + // WHY ARE THE FOLLOWING ZoomVerbs??? new ZoomVerb(SP_VERB_TOGGLE_RULERS, "ToggleRulers", N_("_Rulers"), N_("Show or hide the canvas rulers"), NULL), new ZoomVerb(SP_VERB_TOGGLE_SCROLLBARS, "ToggleScrollbars", N_("Scroll_bars"), N_("Show or hide the canvas scrollbars"), NULL), new ZoomVerb(SP_VERB_TOGGLE_GRID, "ToggleGrid", N_("Page _Grid"), N_("Show or hide the page grid"), INKSCAPE_ICON("show-grid")), @@ -2950,6 +2963,8 @@ Verb *Verb::_base_verbs[] = { N_("Zoom to fit selection in window"), INKSCAPE_ICON("zoom-fit-selection")), // Dialogs + new DialogVerb(SP_VERB_DIALOG_PROTOTYPE, "DialogPrototype", N_("Prototype..."), + N_("Prototype Dialog"), INKSCAPE_ICON("gtk-preferences")), new DialogVerb(SP_VERB_DIALOG_DISPLAY, "DialogPreferences", N_("P_references..."), N_("Edit global Inkscape preferences"), INKSCAPE_ICON("gtk-preferences")), new DialogVerb(SP_VERB_DIALOG_NAMEDVIEW, "DialogDocumentProperties", N_("_Document Properties..."), @@ -3005,6 +3020,10 @@ Verb *Verb::_base_verbs[] = { N_("View Objects"), INKSCAPE_ICON("dialog-layers")), new DialogVerb(SP_VERB_DIALOG_TAGS, "DialogTags", N_("Selection se_ts..."), N_("View Tags"), INKSCAPE_ICON("edit-select-all-layers")), + new DialogVerb(SP_VERB_DIALOG_STYLE, "DialogStyle", N_("Style Dialog..."), + N_("View Style Dialog"), NULL), + new DialogVerb(SP_VERB_DIALOG_CSS, "DialogCss", N_("Css Dialog..."), + N_("View Css Dialog"), NULL), new DialogVerb(SP_VERB_DIALOG_LIVE_PATH_EFFECT, "DialogLivePathEffect", N_("Path E_ffects ..."), N_("Manage, edit, and apply path effects"), INKSCAPE_ICON("dialog-path-effects")), new DialogVerb(SP_VERB_DIALOG_FILTER_EFFECTS, "DialogFilterEffects", N_("Filter _Editor..."), diff --git a/src/verbs.h b/src/verbs.h index 76a7d19a6..b6b7b54dc 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -291,6 +291,7 @@ enum { SP_VERB_ZOOM_DRAWING, SP_VERB_ZOOM_SELECTION, /* Dialogs */ + SP_VERB_DIALOG_PROTOTYPE, SP_VERB_DIALOG_DISPLAY, SP_VERB_DIALOG_NAMEDVIEW, SP_VERB_DIALOG_METADATA, @@ -317,6 +318,8 @@ enum { SP_VERB_DIALOG_LAYERS, SP_VERB_DIALOG_OBJECTS, SP_VERB_DIALOG_TAGS, + SP_VERB_DIALOG_STYLE, + SP_VERB_DIALOG_CSS, SP_VERB_DIALOG_LIVE_PATH_EFFECT, SP_VERB_DIALOG_FILTER_EFFECTS, SP_VERB_DIALOG_SVG_FONTS, diff --git a/src/viewbox.cpp b/src/viewbox.cpp index ebfc04d8a..1fa33117a 100644 --- a/src/viewbox.cpp +++ b/src/viewbox.cpp @@ -268,7 +268,7 @@ void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) { center_rotation *= Geom::Rotate(Geom::rad_from_deg(this->angle - this->previous_angle)); center_rotation *= Geom::Translate(page_center * vbt); view_center = desktop->dt2doc(view_center * center_rotation); - desktop->zoom_relative(view_center[Geom::X], view_center[Geom::Y], 1.0); + desktop->zoom_relative_center_point(view_center, 1.0); this->rotated = false; } } diff --git a/src/widgets/arc-toolbar.cpp b/src/widgets/arc-toolbar.cpp index 9b408a7b2..56eeb8922 100644 --- a/src/widgets/arc-toolbar.cpp +++ b/src/widgets/arc-toolbar.cpp @@ -148,7 +148,7 @@ static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl ) SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data( tbl, "desktop" )); if (DocumentUndo::getUndoSensitive(desktop->getDocument())) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0); + prefs->setInt("/tools/shapes/arc/arc_type", ege_select_one_action_get_active(act)); } // quit if run by the attr_changed listener @@ -159,35 +159,43 @@ static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl ) // in turn, prevent listener from responding g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); - bool modmade = false; - if ( ege_select_one_action_get_active(act) != 0 ) { - auto itemlist= desktop->getSelection()->items(); - for(auto i=itemlist.begin();i!=itemlist.end();++i){ - SPItem *item = *i; - if (SP_IS_GENERICELLIPSE(item)) { - Inkscape::XML::Node *repr = item->getRepr(); - repr->setAttribute("sodipodi:open", "true"); - item->updateRepr(); - modmade = true; - } - } - } else { - auto itemlist= desktop->getSelection()->items(); - for(auto i=itemlist.begin();i!=itemlist.end();++i){ - SPItem *item = *i; - if (SP_IS_GENERICELLIPSE(item)) { - Inkscape::XML::Node *repr = item->getRepr(); - repr->setAttribute("sodipodi:open", NULL); - item->updateRepr(); - modmade = true; - } + int mode = ege_select_one_action_get_active(act); + Glib::ustring arc_type = "slice"; + bool open = false; + switch (mode) { + case 0: + arc_type = "slice"; + open = false; + break; + case 1: + arc_type = "arc"; + open = true; + break; + case 2: + arc_type = "chord"; + open = true; // For backward compat, not truly open but chord most like arc. + break; + default: + std::cerr << "sp_arctb_open_state_changed: bad arc type: " << mode << std::endl; + } + + bool modmade = false; + auto itemlist= desktop->getSelection()->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + SPItem *item = *i; + if (SP_IS_GENERICELLIPSE(item)) { + Inkscape::XML::Node *repr = item->getRepr(); + repr->setAttribute("sodipodi:open", (open?"true":NULL) ); + repr->setAttribute("sodipodi:arc-type", arc_type.c_str()); + item->updateRepr(); + modmade = true; } } if (modmade) { DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_ARC, - _("Arc: Change open/closed")); + _("Arc: Changed arc type")); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -234,14 +242,22 @@ static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const * / sp_arctb_sensitivize( tbl, gtk_adjustment_get_value(adj1), gtk_adjustment_get_value(adj2) ); - char const *openstr = NULL; - openstr = repr->attribute("sodipodi:open"); + char const *arctypestr = NULL; + arctypestr = repr->attribute("sodipodi:arc-type"); + if (!arctypestr) { // For old files. + char const *openstr = NULL; + openstr = repr->attribute("sodipodi:open"); + arctypestr = (openstr ? "arc" : "slice"); + } + EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) ); - if (openstr) { + if (!strcmp(arctypestr,"slice")) { + ege_select_one_action_set_active( ocb, 0 ); + } else if (!strcmp(arctypestr,"arc")) { ege_select_one_action_set_active( ocb, 1 ); } else { - ege_select_one_action_set_active( ocb, 0 ); + ege_select_one_action_set_active( ocb, 2 ); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -338,25 +354,32 @@ void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObjec gtk_action_group_add_action( mainActions, GTK_ACTION(eact) ); } - /* Segments / Pie checkbox */ + /* Arc: Slice, Arc, Chord */ { GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING ); GtkTreeIter iter; gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, - 0, _("Closed arc"), - 1, _("Switch to segment (closed shape with two radii)"), + 0, _("Slice"), + 1, _("Switch to slice (closed shape with two radii)"), 2, INKSCAPE_ICON("draw-ellipse-segment"), -1 ); gtk_list_store_append( model, &iter ); gtk_list_store_set( model, &iter, - 0, _("Open Arc"), + 0, _("Arc (Open)"), 1, _("Switch to arc (unclosed shape)"), 2, INKSCAPE_ICON("draw-ellipse-arc"), -1 ); + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("Chord"), + 1, _("Switch to chord (closed shape)"), + 2, INKSCAPE_ICON("draw-ellipse-chord"), + -1 ); + EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) ); gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); g_object_set_data( holder, "open_action", act ); @@ -368,8 +391,8 @@ void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObjec ege_select_one_action_set_icon_size( act, secondarySize ); ege_select_one_action_set_tooltip_column( act, 1 ); - bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false); - ege_select_one_action_set_active( act, isClosed ? 0 : 1 ); + int arc_type = prefs->getInt("/tools/shapes/arc/arc_type", 0); + ege_select_one_action_set_active( act, arc_type ); g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder ); } diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index f0d0541c2..122b19e22 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -66,12 +66,6 @@ #include <gtkmm/cssprovider.h> #include <gtkmm/paned.h> #include <gtkmm/messagedialog.h> -#include <iomanip> - -#if defined (SOLARIS) && (SOLARIS == 8) -#include "round.h" -using Inkscape::round; -#endif using Inkscape::UI::Widget::UnitTracker; using Inkscape::UI::UXManager; @@ -79,14 +73,6 @@ using Inkscape::UI::ToolboxFactory; using ege::AppearTimeTracker; using Inkscape::Util::unit_table; -enum { - ACTIVATE, - DEACTIVATE, - MODIFIED, - EVENT_CONTEXT_CHANGED, - LAST_SIGNAL -}; - //--------------------------------------------------------------------- /* SPDesktopWidget */ @@ -606,7 +592,7 @@ void SPDesktopWidget::init( SPDesktopWidget *dtw ) dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "value_changed", G_CALLBACK (sp_dtw_zoom_value_changed), dtw); dtw->zoom_update = g_signal_connect (G_OBJECT (dtw->zoom_status), "populate_popup", G_CALLBACK (sp_dtw_zoom_populate_popup), dtw); auto css_provider_spinbutton = Gtk::CssProvider::create(); - css_provider_spinbutton->load_from_data("* { padding-left: 2; padding-right: 2; padding-top: 0; padding-bottom: 0;}"); + css_provider_spinbutton->load_from_data("* { padding-left: 2px; padding-right: 2px; padding-top: 0px; padding-bottom: 0px;}"); auto zoomstat = Glib::wrap(dtw->zoom_status); zoomstat->set_name("ZoomStatus"); auto context_zoom = zoomstat->get_style_context(); @@ -881,7 +867,7 @@ sp_desktop_widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation) double newshortside = MIN(newarea.width(), newarea.height()); zoom *= newshortside / oldshortside; } - dtw->desktop->zoom_absolute(area.midpoint()[Geom::X], area.midpoint()[Geom::Y], zoom); + dtw->desktop->zoom_absolute_center_point (area.midpoint(), zoom); // TODO - Should call show_dialogs() from sp_namedview_window_from_document only. // But delaying the call to here solves dock sizing issues on OS X, (see #171579) @@ -911,7 +897,7 @@ sp_desktop_widget_realize (GtkWidget *widget) if (d.width() < 1.0 || d.height() < 1.0) return; - dtw->desktop->set_display_area (d.left(), d.top(), d.right(), d.bottom(), 10); + dtw->desktop->set_display_area (d, 10); dtw->updateNamedview(); } @@ -1679,7 +1665,7 @@ SPDesktopWidget* SPDesktopWidget::createInstance(SPNamedView *namedview) gtk_widget_set_name(dtw->menubar, "MenuBar"); gtk_widget_show_all (dtw->menubar); SPNamedView *nv = dtw->desktop->namedview; - gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (dtw->vbox), dtw->menubar, FALSE, FALSE, 0); dtw->layoutWidgets(); gtk_spin_button_set_value(GTK_SPIN_BUTTON (dtw->rotation_status), namedview->document_rotation); sp_namedview_set_document_rotation(namedview); @@ -1749,9 +1735,16 @@ void SPDesktopWidget::namedviewModified(SPObject *obj, guint flags) if (GTK_IS_CONTAINER(i->data)) { GList *grch = gtk_container_get_children (GTK_CONTAINER(i->data)); for (GList *j = grch; j != NULL; j = j->next) { + if (!GTK_IS_WIDGET(j->data)) // wasn't a widget continue; + // Don't apply to text toolbar. We want to be able to + // use different units for text. (Bug 1562217) + const gchar* name = gtk_widget_get_name( (GTK_WIDGET(j->data)) ); + if (strcmp( name, "TextToolbar") == 0) + continue; + gpointer t = sp_search_by_data_recursive(GTK_WIDGET(j->data), (gpointer) "tracker"); if (t == NULL) // didn't find any tracker data continue; @@ -1794,15 +1787,9 @@ sp_desktop_widget_adjustment_value_changed (GtkAdjustment */*adj*/, SPDesktopWid dtw->update = 1; - dtw->canvas->scrollTo(gtk_adjustment_get_value(dtw->hadj), - gtk_adjustment_get_value(dtw->vadj), FALSE); - sp_desktop_widget_update_rulers (dtw); - - /* update perspective lines if we are in the 3D box tool (so that infinite ones are shown correctly) */ - //sp_box3d_context_update_lines(dtw->desktop->event_context); - if (SP_IS_BOX3D_CONTEXT(dtw->desktop->event_context)) { - SP_BOX3D_CONTEXT(dtw->desktop->event_context)->_vpdrag->updateLines(); - } + // Do not call canvas->scrollTo directly... messes up 'offset'. + dtw->desktop->scroll_absolute( Geom::Point(gtk_adjustment_get_value(dtw->hadj), + gtk_adjustment_get_value(dtw->vadj)), false); dtw->update = 0; } @@ -1839,16 +1826,21 @@ sp_dtw_zoom_display_to_value (gdouble value) static gint sp_dtw_zoom_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/) { - gdouble new_scrolled = gtk_spin_button_get_value (spin); - const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin)); - gdouble new_typed = atof (b); + gchar *b = g_strdup (gtk_entry_get_text (GTK_ENTRY (spin))); - if (sp_dtw_zoom_value_to_display (new_scrolled) == new_typed) { // the new value is set by scrolling - *new_val = new_scrolled; - } else { // the new value is typed in - *new_val = sp_dtw_zoom_display_to_value (new_typed); + gchar *comma = g_strstr_len (b, -1, ","); + if (comma) { + *comma = '.'; } + char *oldlocale = g_strdup (setlocale(LC_NUMERIC, NULL)); + setlocale (LC_NUMERIC, "C"); + gdouble new_typed = atof (b); + setlocale (LC_NUMERIC, oldlocale); + g_free (oldlocale); + g_free (b); + + *new_val = sp_dtw_zoom_display_to_value (new_typed); return TRUE; } @@ -1869,16 +1861,21 @@ sp_dtw_zoom_output (GtkSpinButton *spin, gpointer /*data*/) static gint sp_dtw_rotation_input (GtkSpinButton *spin, gdouble *new_val, gpointer /*data*/) { - gdouble new_scrolled = gtk_spin_button_get_value (spin); - const gchar *b = gtk_entry_get_text (GTK_ENTRY (spin)); - gdouble new_typed = atof (b); + gchar *b = g_strdup (gtk_entry_get_text (GTK_ENTRY (spin))); - if (new_scrolled == new_typed) { // the new value is set by scrolling - *new_val = new_scrolled; - } else { // the new value is typed in - *new_val = new_typed; + gchar *comma = g_strstr_len (b, -1, ","); + if (comma) { + *comma = '.'; } + char *oldlocale = g_strdup (setlocale(LC_NUMERIC, NULL)); + setlocale (LC_NUMERIC, "C"); + gdouble new_value = atof (b); + setlocale (LC_NUMERIC, oldlocale); + g_free (oldlocale); + g_free (b); + + *new_val = new_value; return TRUE; } @@ -1887,10 +1884,9 @@ sp_dtw_rotation_output (GtkSpinButton *spin, gpointer /*data*/) { gchar b[64]; double val = gtk_spin_button_get_value (spin); - std::ostringstream s; - s.imbue(std::locale(""));; - s << std::fixed << std::setprecision(2) << val << "º"; - gtk_entry_set_text (GTK_ENTRY (spin), s.str().c_str()); + g_snprintf (b, 64, "%7.2f°", val); + + gtk_entry_set_text (GTK_ENTRY (spin), b); return TRUE; } @@ -1902,9 +1898,11 @@ sp_dtw_zoom_value_changed (GtkSpinButton *spin, gpointer data) SPDesktopWidget *dtw = SP_DESKTOP_WIDGET (data); SPDesktop *desktop = dtw->desktop; - Geom::Rect const d = desktop->get_display_area(); + // Zoom around center of window + Geom::Rect const d_canvas = desktop->getCanvas()->getViewbox(); + Geom::Point midpoint = desktop->w2d(d_canvas.midpoint()); g_signal_handler_block (spin, dtw->zoom_update); - desktop->zoom_absolute (d.midpoint()[Geom::X], d.midpoint()[Geom::Y], zoom_factor); + desktop->zoom_absolute_center_point (midpoint, zoom_factor); g_signal_handler_unblock (spin, dtw->zoom_update); spinbutton_defocus (GTK_WIDGET(spin)); @@ -2045,44 +2043,44 @@ sp_dtw_rotation_populate_popup (GtkEntry */*entry*/, GtkMenu *menu, gpointer dat } g_list_free (children); - item = gtk_menu_item_new_with_label ("-180º"); + item = gtk_menu_item_new_with_label ("-180°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_180), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("-135º"); + item = gtk_menu_item_new_with_label ("-135°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_135), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("-90º"); + item = gtk_menu_item_new_with_label ("-90°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_90), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("-45º"); + item = gtk_menu_item_new_with_label ("-45°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_minus_45), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("0º"); + item = gtk_menu_item_new_with_label ("0°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_0), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("45º"); + item = gtk_menu_item_new_with_label ("45°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_45), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("90º"); + item = gtk_menu_item_new_with_label ("90°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_90), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - item = gtk_menu_item_new_with_label ("135º"); + item = gtk_menu_item_new_with_label ("135°"); g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (sp_dtw_rotate_135), dtw); gtk_widget_show (item); gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); @@ -2140,7 +2138,7 @@ static void sp_dtw_zoom_menu_handler (SPDesktop *dt, gdouble factor) { Geom::Rect const d = dt->get_display_area(); - dt->zoom_absolute(d.midpoint()[Geom::X], d.midpoint()[Geom::Y], factor); + dt->zoom_absolute_center_point (d.midpoint(), factor); } diff --git a/src/widgets/gimp/ruler.cpp b/src/widgets/gimp/ruler.cpp index 2a71b5c08..6a1f7f903 100644 --- a/src/widgets/gimp/ruler.cpp +++ b/src/widgets/gimp/ruler.cpp @@ -34,11 +34,10 @@ #include <cstdio> #include "ruler.h" -#include "round.h" #include <glibmm/i18n.h> #include "util/units.h" -#define ROUND(x) ((int) ((x) + 0.5)) +#define ROUND(x) ((int) round(x)) #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB @@ -1217,7 +1216,7 @@ sp_ruler_draw_ticks (SPRuler *ruler) // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h) // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-)) - pos = gint(Inkscape::round((cur - lower) * increment + 1e-12)); + pos = gint(round((cur - lower) * increment + 1e-12)); if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) { diff --git a/src/widgets/mesh-toolbar.cpp b/src/widgets/mesh-toolbar.cpp index 7a37376db..0ca1cc027 100644 --- a/src/widgets/mesh-toolbar.cpp +++ b/src/widgets/mesh-toolbar.cpp @@ -20,7 +20,6 @@ #include <gtkmm.h> -// REVIEW THESE AT END OF REWRITE #include "ui/widget/color-preview.h" #include "toolbox.h" #include "mesh-toolbar.h" diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index ba79517d2..efa5527e4 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -205,6 +205,7 @@ static void sp_text_fontsize_value_changed( Ink_ComboBoxEntry_Action *act, GObje if (endptr == text) { // Conversion failed, non-numeric input. g_warning( "Conversion of size text to double failed, input: %s\n", text ); g_free( text ); + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); return; } g_free( text ); @@ -301,10 +302,23 @@ static void sp_text_fontstyle_value_changed( Ink_ComboBoxEntry_Action *act, GObj SPDesktop *desktop = SP_ACTIVE_DESKTOP; sp_desktop_set_style (desktop, css, true, true); + + + // If no selected objects, set default. + SPStyle query(SP_ACTIVE_DOCUMENT); + int result_style = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTSTYLE); + if (result_style == QUERY_STYLE_NOTHING) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); + } else { + // Save for undo + DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Change font style")); + } + sp_repr_css_attr_unref (css); - DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_TEXT, - _("Text: Change font style")); } g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); @@ -588,17 +602,14 @@ static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl ) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - // Only save if not relative unit - if ( !is_relative(unit) ) { - // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit so - // we can save it (allows us to adjust line height value when unit changes). - SPILength temp_length; - Inkscape::CSSOStringStream temp_stream; - temp_stream << 1 << unit->abbr; - temp_length.read(temp_stream.str().c_str()); - prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); - g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); - } + // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit so + // we can save it (allows us to adjust line height value when unit changes). + SPILength temp_length; + Inkscape::CSSOStringStream temp_stream; + temp_stream << 1 << unit->abbr; + temp_length.read(temp_stream.str().c_str()); + prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); + g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); // Set css line height. SPCSSAttr *css = sp_repr_css_attr_new (); @@ -701,16 +712,13 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl ) g_return_if_fail(unit != NULL); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - // Only save if not relative unit - if ( !is_relative(unit) ) { - // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit. - SPILength temp_length; - Inkscape::CSSOStringStream temp_stream; - temp_stream << 1 << unit->abbr; - temp_length.read(temp_stream.str().c_str()); - prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); - g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); - } + // This nonsense is to get SP_CSS_UNIT_xx value corresponding to unit. + SPILength temp_length; + Inkscape::CSSOStringStream temp_stream; + temp_stream << 1 << unit->abbr; + temp_length.read(temp_stream.str().c_str()); + prefs->setInt("/tools/text/lineheight/display_unit", temp_length.unit); + g_object_set_data( tbl, "lineheight_unit", GINT_TO_POINTER(temp_length.unit)); // Read current line height value EgeAdjustmentAction *line_height_act = @@ -723,7 +731,10 @@ static void sp_text_lineheight_unit_changed( gpointer /* */, GObject *tbl ) auto itemlist = selection->items(); // Convert between units - if ((unit->abbr == "" || unit->abbr == "em") && old_unit == SP_CSS_UNIT_EX) { + if ((unit->abbr == "" || unit->abbr == "em") && + (old_unit == SP_CSS_UNIT_NONE || old_unit == SP_CSS_UNIT_EM)) { + // Do nothing + } else if ((unit->abbr == "" || unit->abbr == "em") && old_unit == SP_CSS_UNIT_EX) { line_height *= 0.5; } else if ((unit->abbr) == "ex" && (old_unit == SP_CSS_UNIT_EM || old_unit == SP_CSS_UNIT_NONE) ) { line_height *= 2.0; @@ -1130,6 +1141,54 @@ static void sp_text_orientation_changed( EgeSelectOneAction *act, GObject *tbl ) g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); } +static void sp_text_direction_changed( EgeSelectOneAction *act, GObject *tbl ) +{ + // quit if run by the _changed callbacks + if (g_object_get_data(G_OBJECT(tbl), "freeze")) { + return; + } + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) ); + + int mode = ege_select_one_action_get_active( act ); + + SPCSSAttr *css = sp_repr_css_attr_new (); + switch (mode) + { + case 0: + { + sp_repr_css_set_property (css, "direction", "ltr"); + break; + } + + case 1: + { + sp_repr_css_set_property (css, "direction", "rtl"); + break; + } + } + + SPStyle query(SP_ACTIVE_DOCUMENT); + int result_numbers = + sp_desktop_query_style (SP_ACTIVE_DESKTOP, &query, QUERY_STYLE_PROPERTY_FONTNUMBERS); + + // If querying returned nothing, update default style. + if (result_numbers == QUERY_STYLE_NOTHING) + { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->mergeStyle("/tools/text/style", css); + } + + sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true); + if(result_numbers != QUERY_STYLE_NOTHING) + { + DocumentUndo::done(SP_ACTIVE_DESKTOP->getDocument(), SP_VERB_CONTEXT_TEXT, + _("Text: Change direction")); + } + sp_repr_css_attr_unref (css); + + g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) ); +} + /* * Set the default list of font sizes, scaled to the users preferred unit */ @@ -1170,8 +1229,7 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl; std::cout << "sp_text_toolbox_selection_changed: start " << count << std::endl; - SPDesktop *desktop = SP_ACTIVE_DESKTOP; - Inkscape::Selection *selection = desktop->getSelection(); + Inkscape::Selection *selection = (SP_ACTIVE_DESKTOP)->getSelection(); auto itemlist0= selection->items(); for(auto i=itemlist0.begin();i!=itemlist0.end(); ++i) { const gchar* id = (*i)->getId(); @@ -1494,6 +1552,15 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ ege_select_one_action_update_sensitive( textOrientationAction ); + // Direction + int activeButton4 = 0; + if (query.direction.computed == SP_CSS_DIRECTION_LTR ) activeButton4 = 0; + if (query.direction.computed == SP_CSS_DIRECTION_RTL ) activeButton4 = 1; + + EgeSelectOneAction* textDirectionAction = + EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextDirectionAction" ) ); + ege_select_one_action_set_active( textDirectionAction, activeButton4 ); + } #ifdef DEBUG_TEXT @@ -1926,6 +1993,52 @@ void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObje g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_changed), holder ); } + + // Text direction (predominant direction of horizontal text). + { + GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN ); + + GtkTreeIter iter; + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("LTR"), + 1, _("Left to right text"), + 2, INKSCAPE_ICON("format-text-direction-horizontal"), + 3, true, + -1 ); + + gtk_list_store_append( model, &iter ); + gtk_list_store_set( model, &iter, + 0, _("RTL"), + 1, _("Right to left text"), + 2, INKSCAPE_ICON("format-text-direction-r2l"), + 3, true, + -1 ); + + EgeSelectOneAction* act = ege_select_one_action_new( "TextDirectionAction", // Name + _("Text direction"), // Label + _("Text direction for normally horizontal text."), // Tooltip + NULL, // Icon name + GTK_TREE_MODEL(model) ); // Model + + g_object_set( act, "short_label", "NotUsed", NULL ); + gtk_action_group_add_action( mainActions, GTK_ACTION(act) ); + g_object_set_data( holder, "TextDirectionAction", act ); + + ege_select_one_action_set_appearance( act, "full" ); + ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE ); + g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL ); + ege_select_one_action_set_icon_column( act, 2 ); + ege_select_one_action_set_icon_size( act, secondarySize ); + ege_select_one_action_set_tooltip_column( act, 1 ); + ege_select_one_action_set_sensitive_column( act, 3 ); + + gint mode = prefs->getInt("/tools/text/text_direction", 0); + ege_select_one_action_set_active( act, mode ); + g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_direction_changed), holder ); + } + /* Line height unit tracker */ UnitTracker* tracker = new UnitTracker(Inkscape::Util::UNIT_TYPE_LINEAR); tracker->prependUnit(unit_table.getUnit("")); // No unit diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 126eac9cd..016eafdaa 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -204,6 +204,7 @@ static struct { SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")}, { "/tools/lpetool", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar", SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")}, + // If you change TextToolbar here, change it also in desktop-widget.cpp { "/tools/text", "text_toolbox", 0, sp_text_toolbox_prep, "TextToolbar", SP_VERB_INVALID, 0, 0}, { "/tools/dropper", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar", @@ -529,6 +530,8 @@ static gchar const * ui_descr = " <toolitem action='TextWritingModeAction' />" " <separator />" " <toolitem action='TextOrientationAction' />" + " <separator />" + " <toolitem action='TextDirectionAction' />" " </toolbar>" " <toolbar name='LPEToolToolbar'>" @@ -1428,7 +1431,7 @@ void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop) auto kludge = dataHolders[aux_toolboxes[i].type_name]; auto holder = gtk_grid_new(); - gtk_widget_set_name( holder, "ToolbarHolder" ); + gtk_widget_set_name( holder, aux_toolboxes[i].ui_name ); gtk_grid_attach( GTK_GRID(holder), kludge, 2, 0, 1, 1); gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name ); GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp ); diff --git a/src/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp index 7e3d4fa7e..a34df09a5 100644 --- a/src/xml/rebase-hrefs.cpp +++ b/src/xml/rebase-hrefs.cpp @@ -276,7 +276,7 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b #ifdef WIN32 /* Native Windows path separators are replaced with / so that the href * also works on Gnu/Linux and OSX */ - std::replace(href.begin(), href.end(), '\\', '/'); + std::replace(new_href.begin(), new_href.end(), '\\', '/'); #endif ir->setAttribute("xlink:href", new_href.c_str()); } else { diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index 5f576d00f..ae6f238d4 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -1008,28 +1008,6 @@ void sp_repr_write_stream_element( Node * repr, Writer & out, add_whitespace = false; } - // THIS DOESN'T APPEAR TO DO ANYTHING. Can it be commented out or deleted? - { - GQuark const href_key = g_quark_from_static_string("xlink:href"); - //GQuark const absref_key = g_quark_from_static_string("sodipodi:absref"); - - gchar const *xxHref = 0; - //gchar const *xxAbsref = 0; - for ( List<AttributeRecord const> ai(attributes); ai; ++ai ) { - if ( ai->key == href_key ) { - xxHref = ai->value; - //} else if ( ai->key == absref_key ) { - //xxAbsref = ai->value; - } - } - - // Might add a special case for absref but no href. - if ( old_href_base && new_href_base && xxHref ) { - //g_message("href rebase test with [%s] and [%s]", xxHref, xxAbsref); - //std::string newOne = rebase_href_attrs( old_href_base, new_href_base, xxHref, xxAbsref ); - } - } - for ( List<AttributeRecord const> iter = rebase_href_attrs(old_href_base, new_href_base, attributes); iter ; ++iter ) |
